added dbus support
authorCreamy Goodness <lance.colton@gmail.com>
Wed, 20 Oct 2010 04:09:09 +0000 (22:09 -0600)
committerCreamy Goodness <lance.colton@gmail.com>
Sat, 18 Dec 2010 21:46:35 +0000 (14:46 -0700)
added new variables ${cell_radio_dbm} and  ${cell_radio_percent}

205 files changed:
.gitignore
INSTALL
build_notes.txt [new file with mode: 0644]
config.guess
config.sub
configure
configure.ac
configure.ac.in
debian/README.Debian [new file with mode: 0644]
debian/control
debian/dirs [new file with mode: 0644]
debian/docs [new file with mode: 0644]
debian/files [deleted file]
debian/rules
m4/libtool.m4 [deleted file]
m4/ltoptions.m4 [deleted file]
m4/ltsugar.m4 [deleted file]
m4/ltversion.m4 [deleted file]
m4/lt~obsolete.m4 [deleted file]
src/common.h
src/conky.c
src/conky.h
src/core.c
src/dbus/.deps/dbus-address.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-auth-script.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-auth-util.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-auth.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-bus.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-connection.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-credentials-util.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-credentials.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-dataslot.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-errors.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-hash.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-internals.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-keyring.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-list.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-mainloop.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-marshal-basic.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-marshal-byteswap-util.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-marshal-byteswap.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-marshal-header.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-marshal-recursive-util.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-marshal-recursive.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-marshal-validate-util.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-marshal-validate.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-memory.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-mempool.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-message-factory.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-message-util.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-message.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-misc.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-object-tree.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-pending-call.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-resources.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-server-debug-pipe.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-server-socket.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-server-unix.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-server.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-sha.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-shell.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-signature.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-spawn.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-string-util.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-string.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-sysdeps-pthread.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-sysdeps-unix.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-sysdeps-util-unix.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-sysdeps-util.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-sysdeps.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-test.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-threads.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-timeout.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-transport-socket.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-transport-unix.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-transport.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-userdb-util.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-userdb.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-uuidgen.Plo [new file with mode: 0644]
src/dbus/.deps/dbus-watch.Plo [new file with mode: 0644]
src/dbus/Makefile [new file with mode: 0644]
src/dbus/Makefile.am [new file with mode: 0644]
src/dbus/Makefile.in [new file with mode: 0644]
src/dbus/dbus-address.c [new file with mode: 0644]
src/dbus/dbus-address.h [new file with mode: 0644]
src/dbus/dbus-arch-deps.h [new file with mode: 0644]
src/dbus/dbus-arch-deps.h.in [new file with mode: 0644]
src/dbus/dbus-auth-script.c [new file with mode: 0644]
src/dbus/dbus-auth-script.h [new file with mode: 0644]
src/dbus/dbus-auth-util.c [new file with mode: 0644]
src/dbus/dbus-auth.c [new file with mode: 0644]
src/dbus/dbus-auth.h [new file with mode: 0644]
src/dbus/dbus-bus.c [new file with mode: 0644]
src/dbus/dbus-bus.h [new file with mode: 0644]
src/dbus/dbus-connection-internal.h [new file with mode: 0644]
src/dbus/dbus-connection.c [new file with mode: 0644]
src/dbus/dbus-connection.h [new file with mode: 0644]
src/dbus/dbus-credentials-util.c [new file with mode: 0644]
src/dbus/dbus-credentials.c [new file with mode: 0644]
src/dbus/dbus-credentials.h [new file with mode: 0644]
src/dbus/dbus-dataslot.c [new file with mode: 0644]
src/dbus/dbus-dataslot.h [new file with mode: 0644]
src/dbus/dbus-errors.c [new file with mode: 0644]
src/dbus/dbus-errors.h [new file with mode: 0644]
src/dbus/dbus-hash.c [new file with mode: 0644]
src/dbus/dbus-hash.h [new file with mode: 0644]
src/dbus/dbus-internals.c [new file with mode: 0644]
src/dbus/dbus-internals.h [new file with mode: 0644]
src/dbus/dbus-keyring.c [new file with mode: 0644]
src/dbus/dbus-keyring.h [new file with mode: 0644]
src/dbus/dbus-list.c [new file with mode: 0644]
src/dbus/dbus-list.h [new file with mode: 0644]
src/dbus/dbus-macros.h [new file with mode: 0644]
src/dbus/dbus-mainloop.c [new file with mode: 0644]
src/dbus/dbus-mainloop.h [new file with mode: 0644]
src/dbus/dbus-marshal-basic.c [new file with mode: 0644]
src/dbus/dbus-marshal-basic.h [new file with mode: 0644]
src/dbus/dbus-marshal-byteswap-util.c [new file with mode: 0644]
src/dbus/dbus-marshal-byteswap.c [new file with mode: 0644]
src/dbus/dbus-marshal-byteswap.h [new file with mode: 0644]
src/dbus/dbus-marshal-header.c [new file with mode: 0644]
src/dbus/dbus-marshal-header.h [new file with mode: 0644]
src/dbus/dbus-marshal-recursive-util.c [new file with mode: 0644]
src/dbus/dbus-marshal-recursive.c [new file with mode: 0644]
src/dbus/dbus-marshal-recursive.h [new file with mode: 0644]
src/dbus/dbus-marshal-validate-util.c [new file with mode: 0644]
src/dbus/dbus-marshal-validate.c [new file with mode: 0644]
src/dbus/dbus-marshal-validate.h [new file with mode: 0644]
src/dbus/dbus-memory.c [new file with mode: 0644]
src/dbus/dbus-memory.h [new file with mode: 0644]
src/dbus/dbus-mempool.c [new file with mode: 0644]
src/dbus/dbus-mempool.h [new file with mode: 0644]
src/dbus/dbus-message-factory.c [new file with mode: 0644]
src/dbus/dbus-message-factory.h [new file with mode: 0644]
src/dbus/dbus-message-internal.h [new file with mode: 0644]
src/dbus/dbus-message-private.h [new file with mode: 0644]
src/dbus/dbus-message-util.c [new file with mode: 0644]
src/dbus/dbus-message.c [new file with mode: 0644]
src/dbus/dbus-message.h [new file with mode: 0644]
src/dbus/dbus-misc.c [new file with mode: 0644]
src/dbus/dbus-misc.h [new file with mode: 0644]
src/dbus/dbus-object-tree.c [new file with mode: 0644]
src/dbus/dbus-object-tree.h [new file with mode: 0644]
src/dbus/dbus-pending-call-internal.h [new file with mode: 0644]
src/dbus/dbus-pending-call.c [new file with mode: 0644]
src/dbus/dbus-pending-call.h [new file with mode: 0644]
src/dbus/dbus-protocol.h [new file with mode: 0644]
src/dbus/dbus-resources.c [new file with mode: 0644]
src/dbus/dbus-resources.h [new file with mode: 0644]
src/dbus/dbus-server-debug-pipe.c [new file with mode: 0644]
src/dbus/dbus-server-debug-pipe.h [new file with mode: 0644]
src/dbus/dbus-server-protected.h [new file with mode: 0644]
src/dbus/dbus-server-socket.c [new file with mode: 0644]
src/dbus/dbus-server-socket.h [new file with mode: 0644]
src/dbus/dbus-server-unix.c [new file with mode: 0644]
src/dbus/dbus-server-unix.h [new file with mode: 0644]
src/dbus/dbus-server.c [new file with mode: 0644]
src/dbus/dbus-server.h [new file with mode: 0644]
src/dbus/dbus-sha.c [new file with mode: 0644]
src/dbus/dbus-sha.h [new file with mode: 0644]
src/dbus/dbus-shared.h [new file with mode: 0644]
src/dbus/dbus-shell.c [new file with mode: 0644]
src/dbus/dbus-shell.h [new file with mode: 0644]
src/dbus/dbus-signature.c [new file with mode: 0644]
src/dbus/dbus-signature.h [new file with mode: 0644]
src/dbus/dbus-spawn.c [new file with mode: 0644]
src/dbus/dbus-spawn.h [new file with mode: 0644]
src/dbus/dbus-string-private.h [new file with mode: 0644]
src/dbus/dbus-string-util.c [new file with mode: 0644]
src/dbus/dbus-string.c [new file with mode: 0644]
src/dbus/dbus-string.h [new file with mode: 0644]
src/dbus/dbus-sysdeps-pthread.c [new file with mode: 0644]
src/dbus/dbus-sysdeps-unix.c [new file with mode: 0644]
src/dbus/dbus-sysdeps-unix.h [new file with mode: 0644]
src/dbus/dbus-sysdeps-util-unix.c [new file with mode: 0644]
src/dbus/dbus-sysdeps-util.c [new file with mode: 0644]
src/dbus/dbus-sysdeps.c [new file with mode: 0644]
src/dbus/dbus-sysdeps.h [new file with mode: 0644]
src/dbus/dbus-test-main.c [new file with mode: 0644]
src/dbus/dbus-test.c [new file with mode: 0644]
src/dbus/dbus-test.h [new file with mode: 0644]
src/dbus/dbus-threads-internal.h [new file with mode: 0644]
src/dbus/dbus-threads.c [new file with mode: 0644]
src/dbus/dbus-threads.h [new file with mode: 0644]
src/dbus/dbus-timeout.c [new file with mode: 0644]
src/dbus/dbus-timeout.h [new file with mode: 0644]
src/dbus/dbus-transport-protected.h [new file with mode: 0644]
src/dbus/dbus-transport-socket.c [new file with mode: 0644]
src/dbus/dbus-transport-socket.h [new file with mode: 0644]
src/dbus/dbus-transport-unix.c [new file with mode: 0644]
src/dbus/dbus-transport-unix.h [new file with mode: 0644]
src/dbus/dbus-transport.c [new file with mode: 0644]
src/dbus/dbus-transport.h [new file with mode: 0644]
src/dbus/dbus-types.h [new file with mode: 0644]
src/dbus/dbus-userdb-util.c [new file with mode: 0644]
src/dbus/dbus-userdb.c [new file with mode: 0644]
src/dbus/dbus-userdb.h [new file with mode: 0644]
src/dbus/dbus-uuidgen.c [new file with mode: 0644]
src/dbus/dbus-uuidgen.h [new file with mode: 0644]
src/dbus/dbus-watch.c [new file with mode: 0644]
src/dbus/dbus-watch.h [new file with mode: 0644]
src/dbus/dbus.h [new file with mode: 0644]
src/defconfig.h [deleted file]
src/linux.c
src/text_object.h

index 080950f..ec23663 100644 (file)
@@ -34,6 +34,13 @@ src/conky
 src/stamp-h1
 src/.deps/
 src/build.h
+.cproject
+.project
+.settings*/
+*.Po
+*.o
+*.ex
+*.EX
 src/config.h
 src/defconfig.h
 src/*.o
diff --git a/INSTALL b/INSTALL
index f420f3e..54caf7c 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -1,29 +1,40 @@
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software
+Foundation, Inc.
+
+   This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
 Basic Installation
 ==================
 
    These are generic installation instructions.
 
-   NOTE: If you use Gentoo, see the README about using the included ebuild.
-
    The `configure' shell script attempts to guess correct values for
 various system-dependent variables used during compilation.  It uses
 those values to create a `Makefile' in each directory of the package.
 It may also create one or more `.h' files containing system-dependent
 definitions.  Finally, it creates a shell script `config.status' that
-you can run in the future to recreate the current configuration, a file
-`config.cache' that saves the results of its tests to speed up
-reconfiguring, and a file `config.log' containing compiler output
-(useful mainly for debugging `configure').
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+   It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring.  (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
 
    If you need to do unusual things to compile the package, please try
 to figure out how `configure' could check whether to do them, and mail
 diffs or instructions to the address given in the `README' so they can
-be considered for the next release.  If at some point `config.cache'
-contains results you don't want to keep, you may remove or edit it.
+be considered for the next release.  If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
 
-   The file `configure.in' is used to create `configure' by a program
-called `autoconf'.  You only need `configure.in' if you want to change
-it or regenerate `configure' using a newer version of `autoconf'.
+   The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'.  You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
 
 The simplest way to compile this package is:
 
@@ -57,14 +68,16 @@ Compilers and Options
 =====================
 
    Some systems require unusual options for compilation or linking that
-the `configure' script does not know about.  You can give `configure'
-initial values for variables by setting them in the environment.  Using
-a Bourne-compatible shell, you can do that on the command line like
-this:
-     CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+the `configure' script does not know about.  Run `./configure --help'
+for details on some of the pertinent environment variables.
 
-Or on systems that have the `env' program, you can do it like this:
-     env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+   You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment.  Here
+is an example:
+
+     ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+   *Note Defining Variables::, for more details.
 
 Compiling For Multiple Architectures
 ====================================
@@ -77,11 +90,11 @@ directory where you want the object files and executables to go and run
 the `configure' script.  `configure' automatically checks for the
 source code in the directory that `configure' is in and in `..'.
 
-   If you have to use a `make' that does not supports the `VPATH'
-variable, you have to compile the package for one architecture at a time
-in the source code directory.  After you have installed the package for
-one architecture, use `make distclean' before reconfiguring for another
-architecture.
+   If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory.  After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
 
 Installation Names
 ==================
@@ -124,22 +137,32 @@ you can use the `configure' options `--x-includes=DIR' and
 Specifying the System Type
 ==========================
 
-   There may be some features `configure' can not figure out
-automatically, but needs to determine by the type of host the package
-will run on.  Usually `configure' can figure that out, but if it prints
-a message saying it can not guess the host type, give it the
-`--host=TYPE' option.  TYPE can either be a short name for the system
-type, such as `sun4', or a canonical name with three fields:
+   There may be some features `configure' cannot figure out
+automatically, but needs to determine by the type of machine the package
+will run on.  Usually, assuming the package is built to be run on the
+_same_ architectures, `configure' can figure that out, but if it prints
+a message saying it cannot guess the machine type, give it the
+`--build=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
      CPU-COMPANY-SYSTEM
 
-See the file `config.sub' for the possible values of each field.  If
+where SYSTEM can have one of these forms:
+
+     OS KERNEL-OS
+
+   See the file `config.sub' for the possible values of each field.  If
 `config.sub' isn't included in this package, then this package doesn't
-need to know the host type.
+need to know the machine type.
 
-   If you are building compiler tools for cross-compiling, you can also
+   If you are _building_ compiler tools for cross-compiling, you should
 use the `--target=TYPE' option to select the type of system they will
-produce code for and the `--build=TYPE' option to select the type of
-system on which you are compiling the package.
+produce code for.
+
+   If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
 
 Sharing Defaults
 ================
@@ -152,20 +175,44 @@ default values for variables like `CC', `cache_file', and `prefix'.
 `CONFIG_SITE' environment variable to the location of the site script.
 A warning: not all `configure' scripts look for a site script.
 
-Operation Controls
+Defining Variables
 ==================
 
+   Variables not defined in a site shell script can be set in the
+environment passed to `configure'.  However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost.  In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'.  For example:
+
+     ./configure CC=/usr/local2/bin/gcc
+
+will cause the specified gcc to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+`configure' Invocation
+======================
+
    `configure' recognizes the following options to control how it
 operates.
 
-`--cache-file=FILE'
-     Use and save the results of the tests in FILE instead of
-     `./config.cache'.  Set FILE to `/dev/null' to disable caching, for
-     debugging `configure'.
-
 `--help'
+`-h'
      Print a summary of the options to `configure', and exit.
 
+`--version'
+`-V'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`--cache-file=FILE'
+     Enable the cache: use and save the results of the tests in FILE,
+     traditionally `config.cache'.  FILE defaults to `/dev/null' to
+     disable caching.
+
+`--config-cache'
+`-C'
+     Alias for `--cache-file=config.cache'.
+
 `--quiet'
 `--silent'
 `-q'
@@ -177,8 +224,6 @@ operates.
      Look for the package's source code in directory DIR.  Usually
      `configure' can determine that directory automatically.
 
-`--version'
-     Print the version of Autoconf used to generate the `configure'
-     script, and exit.
+`configure' also accepts some other, not widely useful, options.  Run
+`configure --help' for more details.
 
-`configure' also accepts some other, not widely useful, options.
diff --git a/build_notes.txt b/build_notes.txt
new file mode 100644 (file)
index 0000000..69ce6b8
--- /dev/null
@@ -0,0 +1,38 @@
+apt-get install maemo-optify\r
+also need to satisfy liblua5.1-0 by installing liblua5.1-0-dev\r
+x86:\r
+The following extra packages will be installed:\r
+  liblua5.1-0 libreadline5-dev\r
+Recommended packages:\r
+  libtool\r
+The following packages will be REMOVED:\r
+  libreadline4-dev maemo-core-debug maemo-core-dev maemo-sdk-debug\r
+  maemo-sdk-dev\r
+The following NEW packages will be installed:\r
+  liblua5.1-0 liblua5.1-0-dev libreadline5-dev\r
+armel:\r
+The following extra packages will be installed:\r
+  libreadline5 libreadline5-dev readline-common\r
+Recommended packages:\r
+  libtool\r
+The following packages will be REMOVED:\r
+  libreadline4-dev maemo-core-dev maemo-sdk-dev\r
+The following NEW packages will be installed:\r
+  liblua5.1-0-dev libreadline5 libreadline5-dev readline-common\r
+0 upgraded, 4 newly installed, 3 to remove and 8 not upgraded.\r
+\r
+autoreconf --force --install\r
+./configure (--enable-imlib2 ... ??)\r
+make install\r
+make dist \r
+tar xfz conky-1.8.0.tar.gz\r
+cd conky-1.8.0\r
+dh_make -e lance.colton@gmail.com -f ../conky-1.8.0.tar.gz -c GPL\r
+extract and copy to new debian folder from old one:\r
+control, rules, conky.conf, conky-all.postinst conkylogo48.png, conkylogo64.png, conky.desktop (might not show extension), conky.sh, optify\r
+\r
+remove nvidia-settings from dependancies in control, if x86\r
+\r
+copy to conky/conky/doc folder: conky.1\r
+\r
+dpkg-buildpackage -b -rfakeroot -klance.colton@gmail.com\r
index e3a2116..f32079a 100644 (file)
@@ -1,10 +1,10 @@
 #! /bin/sh
 # Attempt to guess a canonical system name.
 #   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
 #   Free Software Foundation, Inc.
 
-timestamp='2009-06-10'
+timestamp='2008-01-23'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -170,7 +170,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
            arm*|i386|m68k|ns32k|sh3*|sparc|vax)
                eval $set_cc_for_build
                if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
-                       | grep -q __ELF__
+                       | grep __ELF__ >/dev/null
                then
                    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
                    # Return netbsd for either.  FIX?
@@ -324,9 +324,6 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
        case `/usr/bin/uname -p` in
            sparc) echo sparc-icl-nx7; exit ;;
        esac ;;
-    s390x:SunOS:*:*)
-       echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
-       exit ;;
     sun4H:SunOS:5.*:*)
        echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
        exit ;;
@@ -334,20 +331,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
        echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
        exit ;;
     i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
-       eval $set_cc_for_build
-       SUN_ARCH="i386"
-       # If there is a compiler, see if it is configured for 64-bit objects.
-       # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
-       # This test works for both compilers.
-       if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
-           if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
-               (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
-               grep IS_64BIT_ARCH >/dev/null
-           then
-               SUN_ARCH="x86_64"
-           fi
-       fi
-       echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
        exit ;;
     sun4*:SunOS:6*:*)
        # According to config.sub, this is the proper way to canonicalize
@@ -656,7 +640,7 @@ EOF
            # => hppa64-hp-hpux11.23
 
            if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
-               grep -q __LP64__
+               grep __LP64__ >/dev/null
            then
                HP_ARCH="hppa2.0w"
            else
@@ -812,7 +796,7 @@ EOF
            x86)
                echo i586-pc-interix${UNAME_RELEASE}
                exit ;;
-           EM64T | authenticamd | genuineintel)
+           EM64T | authenticamd)
                echo x86_64-unknown-interix${UNAME_RELEASE}
                exit ;;
            IA64)
@@ -822,9 +806,6 @@ EOF
     [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
        echo i${UNAME_MACHINE}-pc-mks
        exit ;;
-    8664:Windows_NT:*)
-       echo x86_64-pc-mks
-       exit ;;
     i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
        # How do we know it's Interix rather than the generic POSIX subsystem?
        # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
@@ -885,17 +866,40 @@ EOF
     m68*:Linux:*:*)
        echo ${UNAME_MACHINE}-unknown-linux-gnu
        exit ;;
-    mips:Linux:*:* | mips64:Linux:*:*)
+    mips:Linux:*:*)
        eval $set_cc_for_build
        sed 's/^        //' << EOF >$dummy.c
        #undef CPU
-       #undef ${UNAME_MACHINE}
-       #undef ${UNAME_MACHINE}el
+       #undef mips
+       #undef mipsel
        #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
-       CPU=${UNAME_MACHINE}el
+       CPU=mipsel
        #else
        #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
-       CPU=${UNAME_MACHINE}
+       CPU=mips
+       #else
+       CPU=
+       #endif
+       #endif
+EOF
+       eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
+           /^CPU/{
+               s: ::g
+               p
+           }'`"
+       test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+       ;;
+    mips64:Linux:*:*)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #undef CPU
+       #undef mips64
+       #undef mips64el
+       #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+       CPU=mips64el
+       #else
+       #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+       CPU=mips64
        #else
        CPU=
        #endif
@@ -927,13 +931,10 @@ EOF
          EV67)  UNAME_MACHINE=alphaev67 ;;
          EV68*) UNAME_MACHINE=alphaev68 ;;
         esac
-       objdump --private-headers /bin/sh | grep -q ld.so.1
+       objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
        if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
        echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
        exit ;;
-    padre:Linux:*:*)
-       echo sparc-unknown-linux-gnu
-       exit ;;
     parisc:Linux:*:* | hppa:Linux:*:*)
        # Look for CPU level
        case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
@@ -981,6 +982,17 @@ EOF
          elf32-i386)
                TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
                ;;
+         a.out-i386-linux)
+               echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+               exit ;;
+         coff-i386)
+               echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+               exit ;;
+         "")
+               # Either a pre-BFD a.out linker (linux-gnuoldld) or
+               # one that does not give us useful --help.
+               echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+               exit ;;
        esac
        # Determine whether the default compiler is a.out or elf
        eval $set_cc_for_build
@@ -1046,7 +1058,7 @@ EOF
     i*86:syllable:*:*)
        echo ${UNAME_MACHINE}-pc-syllable
        exit ;;
-    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
        echo i386-unknown-lynxos${UNAME_RELEASE}
        exit ;;
     i*86:*DOS:*:*)
@@ -1090,11 +1102,8 @@ EOF
     pc:*:*:*)
        # Left here for compatibility:
         # uname -m prints for DJGPP always 'pc', but it prints nothing about
-        # the processor, so we play safe by assuming i586.
-       # Note: whatever this is, it MUST be the same as what config.sub
-       # prints for the "djgpp" host, or else GDB configury will decide that
-       # this is a cross-build.
-       echo i586-pc-msdosdjgpp
+        # the processor, so we play safe by assuming i386.
+       echo i386-pc-msdosdjgpp
         exit ;;
     Intel:Mach:3*:*)
        echo i386-pc-mach3
@@ -1132,16 +1141,6 @@ EOF
     3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
         /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
           && { echo i486-ncr-sysv4; exit; } ;;
-    NCR*:*:4.2:* | MPRAS*:*:4.2:*)
-       OS_REL='.3'
-       test -r /etc/.relid \
-           && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
-       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
-           && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
-       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
-           && { echo i586-ncr-sysv4.3${OS_REL}; exit; }
-       /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
-           && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
     m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
        echo m68k-unknown-lynxos${UNAME_RELEASE}
        exit ;;
@@ -1154,7 +1153,7 @@ EOF
     rs6000:LynxOS:2.*:*)
        echo rs6000-unknown-lynxos${UNAME_RELEASE}
        exit ;;
-    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
        echo powerpc-unknown-lynxos${UNAME_RELEASE}
        exit ;;
     SM[BE]S:UNIX_SV:*:*)
@@ -1217,9 +1216,6 @@ EOF
     BePC:BeOS:*:*)     # BeOS running on Intel PC compatible.
        echo i586-pc-beos
        exit ;;
-    BePC:Haiku:*:*)    # Haiku running on Intel PC compatible.
-       echo i586-pc-haiku
-       exit ;;
     SX-4:SUPER-UX:*:*)
        echo sx4-nec-superux${UNAME_RELEASE}
        exit ;;
@@ -1328,9 +1324,6 @@ EOF
     i*86:rdos:*:*)
        echo ${UNAME_MACHINE}-pc-rdos
        exit ;;
-    i*86:AROS:*:*)
-       echo ${UNAME_MACHINE}-pc-aros
-       exit ;;
 esac
 
 #echo '(No uname command or uname output not recognized.)' 1>&2
index eb0389a..6759825 100644 (file)
@@ -1,10 +1,10 @@
 #! /bin/sh
 # Configuration validation subroutine script.
 #   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
 #   Free Software Foundation, Inc.
 
-timestamp='2009-06-11'
+timestamp='2008-01-16'
 
 # This file is (in principle) common to ALL GNU software.
 # The presence of a machine in this file suggests that SOME GNU software
@@ -122,7 +122,6 @@ maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
 case $maybe_os in
   nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
   uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
-  kopensolaris*-gnu* | \
   storm-chaos* | os2-emx* | rtmk-nova*)
     os=-$maybe_os
     basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
@@ -153,9 +152,6 @@ case $os in
                os=
                basic_machine=$1
                ;;
-        -bluegene*)
-               os=-cnk
-               ;;
        -sim | -cisco | -oki | -wec | -winbond)
                os=
                basic_machine=$1
@@ -253,16 +249,13 @@ case $basic_machine in
        | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
        | i370 | i860 | i960 | ia64 \
        | ip2k | iq2000 \
-       | lm32 \
        | m32c | m32r | m32rle | m68000 | m68k | m88k \
-       | maxq | mb | microblaze | mcore | mep | metag \
+       | maxq | mb | microblaze | mcore | mep \
        | mips | mipsbe | mipseb | mipsel | mipsle \
        | mips16 \
        | mips64 | mips64el \
-       | mips64octeon | mips64octeonel \
-       | mips64orion | mips64orionel \
-       | mips64r5900 | mips64r5900el \
        | mips64vr | mips64vrel \
+       | mips64orion | mips64orionel \
        | mips64vr4100 | mips64vr4100el \
        | mips64vr4300 | mips64vr4300el \
        | mips64vr5000 | mips64vr5000el \
@@ -275,7 +268,6 @@ case $basic_machine in
        | mipsisa64sr71k | mipsisa64sr71kel \
        | mipstx39 | mipstx39el \
        | mn10200 | mn10300 \
-       | moxie \
        | mt \
        | msp430 \
        | nios | nios2 \
@@ -285,7 +277,7 @@ case $basic_machine in
        | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
        | pyramid \
        | score \
-       | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+       | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
        | sh64 | sh64le \
        | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
        | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
@@ -294,7 +286,7 @@ case $basic_machine in
        | v850 | v850e \
        | we32k \
        | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
-       | z8k | z80)
+       | z8k)
                basic_machine=$basic_machine-unknown
                ;;
        m6811 | m68hc11 | m6812 | m68hc12)
@@ -337,17 +329,14 @@ case $basic_machine in
        | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
        | i*86-* | i860-* | i960-* | ia64-* \
        | ip2k-* | iq2000-* \
-       | lm32-* \
        | m32c-* | m32r-* | m32rle-* \
        | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
-       | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
+       | m88110-* | m88k-* | maxq-* | mcore-* \
        | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
        | mips16-* \
        | mips64-* | mips64el-* \
-       | mips64octeon-* | mips64octeonel-* \
-       | mips64orion-* | mips64orionel-* \
-       | mips64r5900-* | mips64r5900el-* \
        | mips64vr-* | mips64vrel-* \
+       | mips64orion-* | mips64orionel-* \
        | mips64vr4100-* | mips64vr4100el-* \
        | mips64vr4300-* | mips64vr4300el-* \
        | mips64vr5000-* | mips64vr5000el-* \
@@ -369,20 +358,20 @@ case $basic_machine in
        | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
        | pyramid-* \
        | romp-* | rs6000-* \
-       | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+       | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
        | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
        | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
        | sparclite-* \
        | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
        | tahoe-* | thumb-* \
-       | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* | tile-* \
+       | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
        | tron-* \
        | v850-* | v850e-* | vax-* \
        | we32k-* \
        | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
        | xstormy16-* | xtensa*-* \
        | ymp-* \
-       | z8k-* | z80-*)
+       | z8k-*)
                ;;
        # Recognize the basic CPU types without company name, with glob match.
        xtensa*)
@@ -450,10 +439,6 @@ case $basic_machine in
                basic_machine=m68k-apollo
                os=-bsd
                ;;
-       aros)
-               basic_machine=i386-pc
-               os=-aros
-               ;;
        aux)
                basic_machine=m68k-apple
                os=-aux
@@ -470,18 +455,10 @@ case $basic_machine in
                basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
                os=-linux
                ;;
-       bluegene*)
-               basic_machine=powerpc-ibm
-               os=-cnk
-               ;;
        c90)
                basic_machine=c90-cray
                os=-unicos
                ;;
-        cegcc)
-               basic_machine=arm-unknown
-               os=-cegcc
-               ;;
        convex-c1)
                basic_machine=c1-convex
                os=-bsd
@@ -549,10 +526,6 @@ case $basic_machine in
                basic_machine=m88k-motorola
                os=-sysv3
                ;;
-       dicos)
-               basic_machine=i686-pc
-               os=-dicos
-               ;;
        djgpp)
                basic_machine=i586-pc
                os=-msdosdjgpp
@@ -1155,10 +1128,6 @@ case $basic_machine in
                basic_machine=z8k-unknown
                os=-sim
                ;;
-       z80-*-coff)
-               basic_machine=z80-unknown
-               os=-sim
-               ;;
        none)
                basic_machine=none-none
                os=-none
@@ -1197,7 +1166,7 @@ case $basic_machine in
        we32k)
                basic_machine=we32k-att
                ;;
-       sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
+       sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
                basic_machine=sh-unknown
                ;;
        sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
@@ -1267,11 +1236,10 @@ case $os in
        # Each alternative MUST END IN A *, to match a version number.
        # -sysv* is not here because it comes later, after sysvr4.
        -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
-             | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
+             | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
              | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
-             | -kopensolaris* \
              | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
-             | -aos* | -aros* \
+             | -aos* \
              | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
              | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
              | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
@@ -1280,7 +1248,7 @@ case $os in
              | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
              | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
              | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
-             | -chorusos* | -chorusrdb* | -cegcc* \
+             | -chorusos* | -chorusrdb* \
              | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
              | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
              | -uxpv* | -beos* | -mpeix* | -udk* \
@@ -1420,9 +1388,6 @@ case $os in
        -zvmoe)
                os=-zvmoe
                ;;
-       -dicos*)
-               os=-dicos
-               ;;
        -none)
                ;;
        *)
@@ -1620,7 +1585,7 @@ case $basic_machine in
                        -sunos*)
                                vendor=sun
                                ;;
-                       -cnk*|-aix*)
+                       -aix*)
                                vendor=ibm
                                ;;
                        -beos*)
index 1f4278a..23fc497 100644 (file)
--- a/configure
+++ b/configure
@@ -20302,7 +20302,7 @@ echo "${ECHO_T}no" >&6; }
 
 fi
 
-ac_config_files="$ac_config_files Makefile data/Makefile doc/Makefile src/Makefile src/build.h lua/Makefile"
+ac_config_files="$ac_config_files Makefile data/Makefile doc/Makefile src/Makefile src/build.h lua/Makefile src/dbus/Makefile"
 
 
 uname=`uname`
@@ -29684,6 +29684,7 @@ do
     "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;;
     "src/build.h") CONFIG_FILES="$CONFIG_FILES src/build.h" ;;
     "lua/Makefile") CONFIG_FILES="$CONFIG_FILES lua/Makefile" ;;
+    "src/dbus/Makefile") CONFIG_FILES="$CONFIG_FILES src/dbus/Makefile" ;;
 
   *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
 echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
index 945ed96..21ff0cd 100644 (file)
@@ -49,6 +49,7 @@ AC_CONFIG_FILES(
   src/Makefile
   src/build.h
   lua/Makefile
+  src/dbus/Makefile
   )
 
 uname=`uname`
index 289c5bb..8a51790 100644 (file)
@@ -49,6 +49,7 @@ AC_CONFIG_FILES(
   src/Makefile
   src/build.h
   lua/Makefile
+  src/dbus/Makefile
   )
 
 uname=`uname`
diff --git a/debian/README.Debian b/debian/README.Debian
new file mode 100644 (file)
index 0000000..82227d4
--- /dev/null
@@ -0,0 +1,6 @@
+conky for Debian
+----------------
+
+<possible notes regarding this package - if none, delete this file>
+
+ -- Lance Colton <lance.colton@gmail.com>  Mon, 27 Sep 2010 23:05:20 -0700
index ee71d5d..a0a4c4a 100644 (file)
@@ -11,7 +11,7 @@ Build-Depends: debhelper (>= 5.0.0), libtool, automake,
               libasound2-dev [!kfreebsd-i386 !kfreebsd-amd64 !hurd-i386],
               libkvm-dev [kfreebsd-i386 kfreebsd-amd64],
               libdevstat-dev [kfreebsd-i386 kfreebsd-amd64],
-              nvidia-settings [i386 amd64], libncurses5-dev
+              libncurses5-dev, liblua5.1-0-dev, lib-dbus-1-dev
 Homepage: http://conky.sourceforge.net/
 Standards-Version: 3.8.4
 
@@ -70,7 +70,7 @@ Description: Highly configurable system monitor (transitional package)
 
 Package: conky-all
 Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}
+Depends: ${shlibs:Depends}, ${misc:Depends}, liblua5.1-0
 Suggests: apcupsd, moc, mpd
 Conflicts: conky-std, conky-cli
 Replaces: conky, conky-std, conky-cli
diff --git a/debian/dirs b/debian/dirs
new file mode 100644 (file)
index 0000000..ca882bb
--- /dev/null
@@ -0,0 +1,2 @@
+usr/bin
+usr/sbin
diff --git a/debian/docs b/debian/docs
new file mode 100644 (file)
index 0000000..5502ed8
--- /dev/null
@@ -0,0 +1,3 @@
+NEWS
+README
+TODO
diff --git a/debian/files b/debian/files
deleted file mode 100644 (file)
index 1fbfa90..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-conky_1.8.0-1_all.deb user/desktop extra
-conky-all_1.8.0-1_armel.deb user/desktop optional
index cd528af..48ff3a4 100644 (file)
@@ -2,7 +2,7 @@
 # -*- makefile -*-
 
 # Uncomment this to turn on verbose mode.
-#export DH_VERBOSE=1
+export DH_VERBOSE=1
 
 # These are used for cross-compiling and for saving the configure script
 # from having to guess our platform (since we know it already)
@@ -12,7 +12,7 @@ DEB_HOST_ARCH_OS    ?= $(shell dpkg-architecture -qDEB_HOST_ARCH_OS)
 DEB_HOST_ARCH_CPU   ?= $(shell dpkg-architecture -qDEB_HOST_ARCH_CPU)
 
 CFLAGS = -Wall -g
-LDFLAGS = -Wl,--as-needed
+LDFLAGS = -Wl,--as-needed -ldbus-1
 
 ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
        CFLAGS += -O0
@@ -44,14 +44,14 @@ configure: config-stamp
 
 config-stamp:
        dh_testdir
-
-       chmod +x ./autogen.sh
+#DML#  chmod +x ./autogen.sh
        AUTOMAKE=automake ./autogen.sh
 
-       ln -sf /usr/share/misc/config.sub .
-       ln -sf /usr/share/misc/config.guess .
+# copy these instead
+#      ln -sf /usr/share/misc/config.sub .
+#      ln -sf /usr/share/misc/config.guess .
 
-       mkdir build-std build-cli build-all
+       mkdir build-all
 
 #DML#          cd build-std && CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \
 #DML#          ../configure $(COMMON_CONFIGURE_FLAGS) $(LINUX_CONF_ARGS_STD)
@@ -65,7 +65,7 @@ config-stamp:
 
        cd build-all && CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \
        ../configure $(COMMON_CONFIGURE_FLAGS) \
-       --enable-imlib2=no --enable-rss --enable-weather-xoap \
+        --enable-rss --enable-weather-xoap \
        --enable-eve --enable-lua=no --enable-lua-cairo=no --enable-lua-imlib2=no \
        --disable-static --enable-argb \
        $(LINUX_CONF_ARGS_ALL) $(ENABLE_NVIDIA)
diff --git a/m4/libtool.m4 b/m4/libtool.m4
deleted file mode 100644 (file)
index a3fee53..0000000
+++ /dev/null
@@ -1,7377 +0,0 @@
-# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*-
-#
-#   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
-#                 2006, 2007, 2008 Free Software Foundation, Inc.
-#   Written by Gordon Matzigkeit, 1996
-#
-# This file is free software; the Free Software Foundation gives
-# unlimited permission to copy and/or distribute it, with or without
-# modifications, as long as this notice is preserved.
-
-m4_define([_LT_COPYING], [dnl
-#   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
-#                 2006, 2007, 2008 Free Software Foundation, Inc.
-#   Written by Gordon Matzigkeit, 1996
-#
-#   This file is part of GNU Libtool.
-#
-# GNU Libtool 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.
-#
-# As a special exception to the GNU General Public License,
-# if you distribute this file as part of a program or library that
-# is built using GNU Libtool, you may include this file under the
-# same distribution terms that you use for the rest of that program.
-#
-# GNU Libtool 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 GNU Libtool; see the file COPYING.  If not, a copy
-# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
-# obtained by writing to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-])
-
-# serial 56 LT_INIT
-
-
-# LT_PREREQ(VERSION)
-# ------------------
-# Complain and exit if this libtool version is less that VERSION.
-m4_defun([LT_PREREQ],
-[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1,
-       [m4_default([$3],
-                  [m4_fatal([Libtool version $1 or higher is required],
-                            63)])],
-       [$2])])
-
-
-# _LT_CHECK_BUILDDIR
-# ------------------
-# Complain if the absolute build directory name contains unusual characters
-m4_defun([_LT_CHECK_BUILDDIR],
-[case `pwd` in
-  *\ * | *\    *)
-    AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;;
-esac
-])
-
-
-# LT_INIT([OPTIONS])
-# ------------------
-AC_DEFUN([LT_INIT],
-[AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT
-AC_BEFORE([$0], [LT_LANG])dnl
-AC_BEFORE([$0], [LT_OUTPUT])dnl
-AC_BEFORE([$0], [LTDL_INIT])dnl
-m4_require([_LT_CHECK_BUILDDIR])dnl
-
-dnl Autoconf doesn't catch unexpanded LT_ macros by default:
-m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl
-m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl
-dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4
-dnl unless we require an AC_DEFUNed macro:
-AC_REQUIRE([LTOPTIONS_VERSION])dnl
-AC_REQUIRE([LTSUGAR_VERSION])dnl
-AC_REQUIRE([LTVERSION_VERSION])dnl
-AC_REQUIRE([LTOBSOLETE_VERSION])dnl
-m4_require([_LT_PROG_LTMAIN])dnl
-
-dnl Parse OPTIONS
-_LT_SET_OPTIONS([$0], [$1])
-
-# This can be used to rebuild libtool when needed
-LIBTOOL_DEPS="$ltmain"
-
-# Always use our own libtool.
-LIBTOOL='$(SHELL) $(top_builddir)/libtool'
-AC_SUBST(LIBTOOL)dnl
-
-_LT_SETUP
-
-# Only expand once:
-m4_define([LT_INIT])
-])# LT_INIT
-
-# Old names:
-AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT])
-AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT])
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([AC_PROG_LIBTOOL], [])
-dnl AC_DEFUN([AM_PROG_LIBTOOL], [])
-
-
-# _LT_CC_BASENAME(CC)
-# -------------------
-# Calculate cc_basename.  Skip known compiler wrappers and cross-prefix.
-m4_defun([_LT_CC_BASENAME],
-[for cc_temp in $1""; do
-  case $cc_temp in
-    compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;;
-    distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;;
-    \-*) ;;
-    *) break;;
-  esac
-done
-cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"`
-])
-
-
-# _LT_FILEUTILS_DEFAULTS
-# ----------------------
-# It is okay to use these file commands and assume they have been set
-# sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'.
-m4_defun([_LT_FILEUTILS_DEFAULTS],
-[: ${CP="cp -f"}
-: ${MV="mv -f"}
-: ${RM="rm -f"}
-])# _LT_FILEUTILS_DEFAULTS
-
-
-# _LT_SETUP
-# ---------
-m4_defun([_LT_SETUP],
-[AC_REQUIRE([AC_CANONICAL_HOST])dnl
-AC_REQUIRE([AC_CANONICAL_BUILD])dnl
-_LT_DECL([], [host_alias], [0], [The host system])dnl
-_LT_DECL([], [host], [0])dnl
-_LT_DECL([], [host_os], [0])dnl
-dnl
-_LT_DECL([], [build_alias], [0], [The build system])dnl
-_LT_DECL([], [build], [0])dnl
-_LT_DECL([], [build_os], [0])dnl
-dnl
-AC_REQUIRE([AC_PROG_CC])dnl
-AC_REQUIRE([LT_PATH_LD])dnl
-AC_REQUIRE([LT_PATH_NM])dnl
-dnl
-AC_REQUIRE([AC_PROG_LN_S])dnl
-test -z "$LN_S" && LN_S="ln -s"
-_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl
-dnl
-AC_REQUIRE([LT_CMD_MAX_LEN])dnl
-_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl
-_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl
-dnl
-m4_require([_LT_FILEUTILS_DEFAULTS])dnl
-m4_require([_LT_CHECK_SHELL_FEATURES])dnl
-m4_require([_LT_CMD_RELOAD])dnl
-m4_require([_LT_CHECK_MAGIC_METHOD])dnl
-m4_require([_LT_CMD_OLD_ARCHIVE])dnl
-m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
-
-_LT_CONFIG_LIBTOOL_INIT([
-# See if we are running on zsh, and set the options which allow our
-# commands through without removal of \ escapes INIT.
-if test -n "\${ZSH_VERSION+set}" ; then
-   setopt NO_GLOB_SUBST
-fi
-])
-if test -n "${ZSH_VERSION+set}" ; then
-   setopt NO_GLOB_SUBST
-fi
-
-_LT_CHECK_OBJDIR
-
-m4_require([_LT_TAG_COMPILER])dnl
-_LT_PROG_ECHO_BACKSLASH
-
-case $host_os in
-aix3*)
-  # AIX sometimes has problems with the GCC collect2 program.  For some
-  # reason, if we set the COLLECT_NAMES environment variable, the problems
-  # vanish in a puff of smoke.
-  if test "X${COLLECT_NAMES+set}" != Xset; then
-    COLLECT_NAMES=
-    export COLLECT_NAMES
-  fi
-  ;;
-esac
-
-# Sed substitution that helps us do robust quoting.  It backslashifies
-# metacharacters that are still active within double-quoted strings.
-sed_quote_subst='s/\([["`$\\]]\)/\\\1/g'
-
-# Same as above, but do not quote variable references.
-double_quote_subst='s/\([["`\\]]\)/\\\1/g'
-
-# Sed substitution to delay expansion of an escaped shell variable in a
-# double_quote_subst'ed string.
-delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
-
-# Sed substitution to delay expansion of an escaped single quote.
-delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
-
-# Sed substitution to avoid accidental globbing in evaled expressions
-no_glob_subst='s/\*/\\\*/g'
-
-# Global variables:
-ofile=libtool
-can_build_shared=yes
-
-# All known linkers require a `.a' archive for static linking (except MSVC,
-# which needs '.lib').
-libext=a
-
-with_gnu_ld="$lt_cv_prog_gnu_ld"
-
-old_CC="$CC"
-old_CFLAGS="$CFLAGS"
-
-# Set sane defaults for various variables
-test -z "$CC" && CC=cc
-test -z "$LTCC" && LTCC=$CC
-test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
-test -z "$LD" && LD=ld
-test -z "$ac_objext" && ac_objext=o
-
-_LT_CC_BASENAME([$compiler])
-
-# Only perform the check for file, if the check method requires it
-test -z "$MAGIC_CMD" && MAGIC_CMD=file
-case $deplibs_check_method in
-file_magic*)
-  if test "$file_magic_cmd" = '$MAGIC_CMD'; then
-    _LT_PATH_MAGIC
-  fi
-  ;;
-esac
-
-# Use C for the default configuration in the libtool script
-LT_SUPPORTED_TAG([CC])
-_LT_LANG_C_CONFIG
-_LT_LANG_DEFAULT_CONFIG
-_LT_CONFIG_COMMANDS
-])# _LT_SETUP
-
-
-# _LT_PROG_LTMAIN
-# ---------------
-# Note that this code is called both from `configure', and `config.status'
-# now that we use AC_CONFIG_COMMANDS to generate libtool.  Notably,
-# `config.status' has no value for ac_aux_dir unless we are using Automake,
-# so we pass a copy along to make sure it has a sensible value anyway.
-m4_defun([_LT_PROG_LTMAIN],
-[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl
-_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir'])
-ltmain="$ac_aux_dir/ltmain.sh"
-])# _LT_PROG_LTMAIN
-
-
-## ------------------------------------- ##
-## Accumulate code for creating libtool. ##
-## ------------------------------------- ##
-
-# So that we can recreate a full libtool script including additional
-# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS
-# in macros and then make a single call at the end using the `libtool'
-# label.
-
-
-# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS])
-# ----------------------------------------
-# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later.
-m4_define([_LT_CONFIG_LIBTOOL_INIT],
-[m4_ifval([$1],
-          [m4_append([_LT_OUTPUT_LIBTOOL_INIT],
-                     [$1
-])])])
-
-# Initialize.
-m4_define([_LT_OUTPUT_LIBTOOL_INIT])
-
-
-# _LT_CONFIG_LIBTOOL([COMMANDS])
-# ------------------------------
-# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later.
-m4_define([_LT_CONFIG_LIBTOOL],
-[m4_ifval([$1],
-          [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS],
-                     [$1
-])])])
-
-# Initialize.
-m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS])
-
-
-# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS])
-# -----------------------------------------------------
-m4_defun([_LT_CONFIG_SAVE_COMMANDS],
-[_LT_CONFIG_LIBTOOL([$1])
-_LT_CONFIG_LIBTOOL_INIT([$2])
-])
-
-
-# _LT_FORMAT_COMMENT([COMMENT])
-# -----------------------------
-# Add leading comment marks to the start of each line, and a trailing
-# full-stop to the whole comment if one is not present already.
-m4_define([_LT_FORMAT_COMMENT],
-[m4_ifval([$1], [
-m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])],
-              [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.])
-)])
-
-
-
-## ------------------------ ##
-## FIXME: Eliminate VARNAME ##
-## ------------------------ ##
-
-
-# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?])
-# -------------------------------------------------------------------
-# CONFIGNAME is the name given to the value in the libtool script.
-# VARNAME is the (base) name used in the configure script.
-# VALUE may be 0, 1 or 2 for a computed quote escaped value based on
-# VARNAME.  Any other value will be used directly.
-m4_define([_LT_DECL],
-[lt_if_append_uniq([lt_decl_varnames], [$2], [, ],
-    [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name],
-       [m4_ifval([$1], [$1], [$2])])
-    lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3])
-    m4_ifval([$4],
-       [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])])
-    lt_dict_add_subkey([lt_decl_dict], [$2],
-       [tagged?], [m4_ifval([$5], [yes], [no])])])
-])
-
-
-# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION])
-# --------------------------------------------------------
-m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])])
-
-
-# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...])
-# ------------------------------------------------
-m4_define([lt_decl_tag_varnames],
-[_lt_decl_filter([tagged?], [yes], $@)])
-
-
-# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..])
-# ---------------------------------------------------------
-m4_define([_lt_decl_filter],
-[m4_case([$#],
-  [0], [m4_fatal([$0: too few arguments: $#])],
-  [1], [m4_fatal([$0: too few arguments: $#: $1])],
-  [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)],
-  [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)],
-  [lt_dict_filter([lt_decl_dict], $@)])[]dnl
-])
-
-
-# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...])
-# --------------------------------------------------
-m4_define([lt_decl_quote_varnames],
-[_lt_decl_filter([value], [1], $@)])
-
-
-# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...])
-# ---------------------------------------------------
-m4_define([lt_decl_dquote_varnames],
-[_lt_decl_filter([value], [2], $@)])
-
-
-# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...])
-# ---------------------------------------------------
-m4_define([lt_decl_varnames_tagged],
-[m4_assert([$# <= 2])dnl
-_$0(m4_quote(m4_default([$1], [[, ]])),
-    m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]),
-    m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))])
-m4_define([_lt_decl_varnames_tagged],
-[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])])
-
-
-# lt_decl_all_varnames([SEPARATOR], [VARNAME1...])
-# ------------------------------------------------
-m4_define([lt_decl_all_varnames],
-[_$0(m4_quote(m4_default([$1], [[, ]])),
-     m4_if([$2], [],
-          m4_quote(lt_decl_varnames),
-       m4_quote(m4_shift($@))))[]dnl
-])
-m4_define([_lt_decl_all_varnames],
-[lt_join($@, lt_decl_varnames_tagged([$1],
-                       lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl
-])
-
-
-# _LT_CONFIG_STATUS_DECLARE([VARNAME])
-# ------------------------------------
-# Quote a variable value, and forward it to `config.status' so that its
-# declaration there will have the same value as in `configure'.  VARNAME
-# must have a single quote delimited value for this to work.
-m4_define([_LT_CONFIG_STATUS_DECLARE],
-[$1='`$ECHO "X$][$1" | $Xsed -e "$delay_single_quote_subst"`'])
-
-
-# _LT_CONFIG_STATUS_DECLARATIONS
-# ------------------------------
-# We delimit libtool config variables with single quotes, so when
-# we write them to config.status, we have to be sure to quote all
-# embedded single quotes properly.  In configure, this macro expands
-# each variable declared with _LT_DECL (and _LT_TAGDECL) into:
-#
-#    <var>='`$ECHO "X$<var>" | $Xsed -e "$delay_single_quote_subst"`'
-m4_defun([_LT_CONFIG_STATUS_DECLARATIONS],
-[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames),
-    [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])])
-
-
-# _LT_LIBTOOL_TAGS
-# ----------------
-# Output comment and list of tags supported by the script
-m4_defun([_LT_LIBTOOL_TAGS],
-[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl
-available_tags="_LT_TAGS"dnl
-])
-
-
-# _LT_LIBTOOL_DECLARE(VARNAME, [TAG])
-# -----------------------------------
-# Extract the dictionary values for VARNAME (optionally with TAG) and
-# expand to a commented shell variable setting:
-#
-#    # Some comment about what VAR is for.
-#    visible_name=$lt_internal_name
-m4_define([_LT_LIBTOOL_DECLARE],
-[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1],
-                                          [description])))[]dnl
-m4_pushdef([_libtool_name],
-    m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl
-m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])),
-    [0], [_libtool_name=[$]$1],
-    [1], [_libtool_name=$lt_[]$1],
-    [2], [_libtool_name=$lt_[]$1],
-    [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl
-m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl
-])
-
-
-# _LT_LIBTOOL_CONFIG_VARS
-# -----------------------
-# Produce commented declarations of non-tagged libtool config variables
-# suitable for insertion in the LIBTOOL CONFIG section of the `libtool'
-# script.  Tagged libtool config variables (even for the LIBTOOL CONFIG
-# section) are produced by _LT_LIBTOOL_TAG_VARS.
-m4_defun([_LT_LIBTOOL_CONFIG_VARS],
-[m4_foreach([_lt_var],
-    m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)),
-    [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])])
-
-
-# _LT_LIBTOOL_TAG_VARS(TAG)
-# -------------------------
-m4_define([_LT_LIBTOOL_TAG_VARS],
-[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames),
-    [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])])
-
-
-# _LT_TAGVAR(VARNAME, [TAGNAME])
-# ------------------------------
-m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])])
-
-
-# _LT_CONFIG_COMMANDS
-# -------------------
-# Send accumulated output to $CONFIG_STATUS.  Thanks to the lists of
-# variables for single and double quote escaping we saved from calls
-# to _LT_DECL, we can put quote escaped variables declarations
-# into `config.status', and then the shell code to quote escape them in
-# for loops in `config.status'.  Finally, any additional code accumulated
-# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded.
-m4_defun([_LT_CONFIG_COMMANDS],
-[AC_PROVIDE_IFELSE([LT_OUTPUT],
-       dnl If the libtool generation code has been placed in $CONFIG_LT,
-       dnl instead of duplicating it all over again into config.status,
-       dnl then we will have config.status run $CONFIG_LT later, so it
-       dnl needs to know what name is stored there:
-        [AC_CONFIG_COMMANDS([libtool],
-            [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])],
-    dnl If the libtool generation code is destined for config.status,
-    dnl expand the accumulated commands and init code now:
-    [AC_CONFIG_COMMANDS([libtool],
-        [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])])
-])#_LT_CONFIG_COMMANDS
-
-
-# Initialize.
-m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT],
-[
-
-# The HP-UX ksh and POSIX shell print the target directory to stdout
-# if CDPATH is set.
-(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
-
-sed_quote_subst='$sed_quote_subst'
-double_quote_subst='$double_quote_subst'
-delay_variable_subst='$delay_variable_subst'
-_LT_CONFIG_STATUS_DECLARATIONS
-LTCC='$LTCC'
-LTCFLAGS='$LTCFLAGS'
-compiler='$compiler_DEFAULT'
-
-# Quote evaled strings.
-for var in lt_decl_all_varnames([[ \
-]], lt_decl_quote_varnames); do
-    case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
-    *[[\\\\\\\`\\"\\\$]]*)
-      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
-      ;;
-    *)
-      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
-      ;;
-    esac
-done
-
-# Double-quote double-evaled strings.
-for var in lt_decl_all_varnames([[ \
-]], lt_decl_dquote_varnames); do
-    case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
-    *[[\\\\\\\`\\"\\\$]]*)
-      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
-      ;;
-    *)
-      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
-      ;;
-    esac
-done
-
-# Fix-up fallback echo if it was mangled by the above quoting rules.
-case \$lt_ECHO in
-*'\\\[$]0 --fallback-echo"')dnl "
-  lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\[$]0 --fallback-echo"\[$]/\[$]0 --fallback-echo"/'\`
-  ;;
-esac
-
-_LT_OUTPUT_LIBTOOL_INIT
-])
-
-
-# LT_OUTPUT
-# ---------
-# This macro allows early generation of the libtool script (before
-# AC_OUTPUT is called), incase it is used in configure for compilation
-# tests.
-AC_DEFUN([LT_OUTPUT],
-[: ${CONFIG_LT=./config.lt}
-AC_MSG_NOTICE([creating $CONFIG_LT])
-cat >"$CONFIG_LT" <<_LTEOF
-#! $SHELL
-# Generated by $as_me.
-# Run this file to recreate a libtool stub with the current configuration.
-
-lt_cl_silent=false
-SHELL=\${CONFIG_SHELL-$SHELL}
-_LTEOF
-
-cat >>"$CONFIG_LT" <<\_LTEOF
-AS_SHELL_SANITIZE
-_AS_PREPARE
-
-exec AS_MESSAGE_FD>&1
-exec AS_MESSAGE_LOG_FD>>config.log
-{
-  echo
-  AS_BOX([Running $as_me.])
-} >&AS_MESSAGE_LOG_FD
-
-lt_cl_help="\
-\`$as_me' creates a local libtool stub from the current configuration,
-for use in further configure time tests before the real libtool is
-generated.
-
-Usage: $[0] [[OPTIONS]]
-
-  -h, --help      print this help, then exit
-  -V, --version   print version number, then exit
-  -q, --quiet     do not print progress messages
-  -d, --debug     don't remove temporary files
-
-Report bugs to <bug-libtool@gnu.org>."
-
-lt_cl_version="\
-m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl
-m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION])
-configured by $[0], generated by m4_PACKAGE_STRING.
-
-Copyright (C) 2008 Free Software Foundation, Inc.
-This config.lt script is free software; the Free Software Foundation
-gives unlimited permision to copy, distribute and modify it."
-
-while test $[#] != 0
-do
-  case $[1] in
-    --version | --v* | -V )
-      echo "$lt_cl_version"; exit 0 ;;
-    --help | --h* | -h )
-      echo "$lt_cl_help"; exit 0 ;;
-    --debug | --d* | -d )
-      debug=: ;;
-    --quiet | --q* | --silent | --s* | -q )
-      lt_cl_silent=: ;;
-
-    -*) AC_MSG_ERROR([unrecognized option: $[1]
-Try \`$[0] --help' for more information.]) ;;
-
-    *) AC_MSG_ERROR([unrecognized argument: $[1]
-Try \`$[0] --help' for more information.]) ;;
-  esac
-  shift
-done
-
-if $lt_cl_silent; then
-  exec AS_MESSAGE_FD>/dev/null
-fi
-_LTEOF
-
-cat >>"$CONFIG_LT" <<_LTEOF
-_LT_OUTPUT_LIBTOOL_COMMANDS_INIT
-_LTEOF
-
-cat >>"$CONFIG_LT" <<\_LTEOF
-AC_MSG_NOTICE([creating $ofile])
-_LT_OUTPUT_LIBTOOL_COMMANDS
-AS_EXIT(0)
-_LTEOF
-chmod +x "$CONFIG_LT"
-
-# configure is writing to config.log, but config.lt does its own redirection,
-# appending to config.log, which fails on DOS, as config.log is still kept
-# open by configure.  Here we exec the FD to /dev/null, effectively closing
-# config.log, so it can be properly (re)opened and appended to by config.lt.
-if test "$no_create" != yes; then
-  lt_cl_success=:
-  test "$silent" = yes &&
-    lt_config_lt_args="$lt_config_lt_args --quiet"
-  exec AS_MESSAGE_LOG_FD>/dev/null
-  $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false
-  exec AS_MESSAGE_LOG_FD>>config.log
-  $lt_cl_success || AS_EXIT(1)
-fi
-])# LT_OUTPUT
-
-
-# _LT_CONFIG(TAG)
-# ---------------
-# If TAG is the built-in tag, create an initial libtool script with a
-# default configuration from the untagged config vars.  Otherwise add code
-# to config.status for appending the configuration named by TAG from the
-# matching tagged config vars.
-m4_defun([_LT_CONFIG],
-[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
-_LT_CONFIG_SAVE_COMMANDS([
-  m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl
-  m4_if(_LT_TAG, [C], [
-    # See if we are running on zsh, and set the options which allow our
-    # commands through without removal of \ escapes.
-    if test -n "${ZSH_VERSION+set}" ; then
-      setopt NO_GLOB_SUBST
-    fi
-
-    cfgfile="${ofile}T"
-    trap "$RM \"$cfgfile\"; exit 1" 1 2 15
-    $RM "$cfgfile"
-
-    cat <<_LT_EOF >> "$cfgfile"
-#! $SHELL
-
-# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
-# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
-# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
-# NOTE: Changes made to this file will be lost: look at ltmain.sh.
-#
-_LT_COPYING
-_LT_LIBTOOL_TAGS
-
-# ### BEGIN LIBTOOL CONFIG
-_LT_LIBTOOL_CONFIG_VARS
-_LT_LIBTOOL_TAG_VARS
-# ### END LIBTOOL CONFIG
-
-_LT_EOF
-
-  case $host_os in
-  aix3*)
-    cat <<\_LT_EOF >> "$cfgfile"
-# AIX sometimes has problems with the GCC collect2 program.  For some
-# reason, if we set the COLLECT_NAMES environment variable, the problems
-# vanish in a puff of smoke.
-if test "X${COLLECT_NAMES+set}" != Xset; then
-  COLLECT_NAMES=
-  export COLLECT_NAMES
-fi
-_LT_EOF
-    ;;
-  esac
-
-  _LT_PROG_LTMAIN
-
-  # We use sed instead of cat because bash on DJGPP gets confused if
-  # if finds mixed CR/LF and LF-only lines.  Since sed operates in
-  # text mode, it properly converts lines to CR/LF.  This bash problem
-  # is reportedly fixed, but why not run on old versions too?
-  sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \
-    || (rm -f "$cfgfile"; exit 1)
-
-  _LT_PROG_XSI_SHELLFNS
-
-  sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \
-    || (rm -f "$cfgfile"; exit 1)
-
-  mv -f "$cfgfile" "$ofile" ||
-    (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
-  chmod +x "$ofile"
-],
-[cat <<_LT_EOF >> "$ofile"
-
-dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded
-dnl in a comment (ie after a #).
-# ### BEGIN LIBTOOL TAG CONFIG: $1
-_LT_LIBTOOL_TAG_VARS(_LT_TAG)
-# ### END LIBTOOL TAG CONFIG: $1
-_LT_EOF
-])dnl /m4_if
-],
-[m4_if([$1], [], [
-    PACKAGE='$PACKAGE'
-    VERSION='$VERSION'
-    TIMESTAMP='$TIMESTAMP'
-    RM='$RM'
-    ofile='$ofile'], [])
-])dnl /_LT_CONFIG_SAVE_COMMANDS
-])# _LT_CONFIG
-
-
-# LT_SUPPORTED_TAG(TAG)
-# ---------------------
-# Trace this macro to discover what tags are supported by the libtool
-# --tag option, using:
-#    autoconf --trace 'LT_SUPPORTED_TAG:$1'
-AC_DEFUN([LT_SUPPORTED_TAG], [])
-
-
-# C support is built-in for now
-m4_define([_LT_LANG_C_enabled], [])
-m4_define([_LT_TAGS], [])
-
-
-# LT_LANG(LANG)
-# -------------
-# Enable libtool support for the given language if not already enabled.
-AC_DEFUN([LT_LANG],
-[AC_BEFORE([$0], [LT_OUTPUT])dnl
-m4_case([$1],
-  [C],                 [_LT_LANG(C)],
-  [C++],               [_LT_LANG(CXX)],
-  [Java],              [_LT_LANG(GCJ)],
-  [Fortran 77],                [_LT_LANG(F77)],
-  [Fortran],           [_LT_LANG(FC)],
-  [Windows Resource],  [_LT_LANG(RC)],
-  [m4_ifdef([_LT_LANG_]$1[_CONFIG],
-    [_LT_LANG($1)],
-    [m4_fatal([$0: unsupported language: "$1"])])])dnl
-])# LT_LANG
-
-
-# _LT_LANG(LANGNAME)
-# ------------------
-m4_defun([_LT_LANG],
-[m4_ifdef([_LT_LANG_]$1[_enabled], [],
-  [LT_SUPPORTED_TAG([$1])dnl
-  m4_append([_LT_TAGS], [$1 ])dnl
-  m4_define([_LT_LANG_]$1[_enabled], [])dnl
-  _LT_LANG_$1_CONFIG($1)])dnl
-])# _LT_LANG
-
-
-# _LT_LANG_DEFAULT_CONFIG
-# -----------------------
-m4_defun([_LT_LANG_DEFAULT_CONFIG],
-[AC_PROVIDE_IFELSE([AC_PROG_CXX],
-  [LT_LANG(CXX)],
-  [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])])
-
-AC_PROVIDE_IFELSE([AC_PROG_F77],
-  [LT_LANG(F77)],
-  [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])])
-
-AC_PROVIDE_IFELSE([AC_PROG_FC],
-  [LT_LANG(FC)],
-  [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])])
-
-dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal
-dnl pulling things in needlessly.
-AC_PROVIDE_IFELSE([AC_PROG_GCJ],
-  [LT_LANG(GCJ)],
-  [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],
-    [LT_LANG(GCJ)],
-    [AC_PROVIDE_IFELSE([LT_PROG_GCJ],
-      [LT_LANG(GCJ)],
-      [m4_ifdef([AC_PROG_GCJ],
-       [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])])
-       m4_ifdef([A][M_PROG_GCJ],
-       [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])])
-       m4_ifdef([LT_PROG_GCJ],
-       [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])])
-
-AC_PROVIDE_IFELSE([LT_PROG_RC],
-  [LT_LANG(RC)],
-  [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])])
-])# _LT_LANG_DEFAULT_CONFIG
-
-# Obsolete macros:
-AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)])
-AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)])
-AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)])
-AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)])
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([AC_LIBTOOL_CXX], [])
-dnl AC_DEFUN([AC_LIBTOOL_F77], [])
-dnl AC_DEFUN([AC_LIBTOOL_FC], [])
-dnl AC_DEFUN([AC_LIBTOOL_GCJ], [])
-
-
-# _LT_TAG_COMPILER
-# ----------------
-m4_defun([_LT_TAG_COMPILER],
-[AC_REQUIRE([AC_PROG_CC])dnl
-
-_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl
-_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl
-_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl
-_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl
-
-# If no C compiler was specified, use CC.
-LTCC=${LTCC-"$CC"}
-
-# If no C compiler flags were specified, use CFLAGS.
-LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
-
-# Allow CC to be a program name with arguments.
-compiler=$CC
-])# _LT_TAG_COMPILER
-
-
-# _LT_COMPILER_BOILERPLATE
-# ------------------------
-# Check for compiler boilerplate output or warnings with
-# the simple compiler test code.
-m4_defun([_LT_COMPILER_BOILERPLATE],
-[m4_require([_LT_DECL_SED])dnl
-ac_outfile=conftest.$ac_objext
-echo "$lt_simple_compile_test_code" >conftest.$ac_ext
-eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
-_lt_compiler_boilerplate=`cat conftest.err`
-$RM conftest*
-])# _LT_COMPILER_BOILERPLATE
-
-
-# _LT_LINKER_BOILERPLATE
-# ----------------------
-# Check for linker boilerplate output or warnings with
-# the simple link test code.
-m4_defun([_LT_LINKER_BOILERPLATE],
-[m4_require([_LT_DECL_SED])dnl
-ac_outfile=conftest.$ac_objext
-echo "$lt_simple_link_test_code" >conftest.$ac_ext
-eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
-_lt_linker_boilerplate=`cat conftest.err`
-$RM -r conftest*
-])# _LT_LINKER_BOILERPLATE
-
-# _LT_REQUIRED_DARWIN_CHECKS
-# -------------------------
-m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[
-  case $host_os in
-    rhapsody* | darwin*)
-    AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:])
-    AC_CHECK_TOOL([NMEDIT], [nmedit], [:])
-    AC_CHECK_TOOL([LIPO], [lipo], [:])
-    AC_CHECK_TOOL([OTOOL], [otool], [:])
-    AC_CHECK_TOOL([OTOOL64], [otool64], [:])
-    _LT_DECL([], [DSYMUTIL], [1],
-      [Tool to manipulate archived DWARF debug symbol files on Mac OS X])
-    _LT_DECL([], [NMEDIT], [1],
-      [Tool to change global to local symbols on Mac OS X])
-    _LT_DECL([], [LIPO], [1],
-      [Tool to manipulate fat objects and archives on Mac OS X])
-    _LT_DECL([], [OTOOL], [1],
-      [ldd/readelf like tool for Mach-O binaries on Mac OS X])
-    _LT_DECL([], [OTOOL64], [1],
-      [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4])
-
-    AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod],
-      [lt_cv_apple_cc_single_mod=no
-      if test -z "${LT_MULTI_MODULE}"; then
-       # By default we will add the -single_module flag. You can override
-       # by either setting the environment variable LT_MULTI_MODULE
-       # non-empty at configure time, or by adding -multi_module to the
-       # link flags.
-       rm -rf libconftest.dylib*
-       echo "int foo(void){return 1;}" > conftest.c
-       echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
--dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD
-       $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
-         -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
-        _lt_result=$?
-       if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then
-         lt_cv_apple_cc_single_mod=yes
-       else
-         cat conftest.err >&AS_MESSAGE_LOG_FD
-       fi
-       rm -rf libconftest.dylib*
-       rm -f conftest.*
-      fi])
-    AC_CACHE_CHECK([for -exported_symbols_list linker flag],
-      [lt_cv_ld_exported_symbols_list],
-      [lt_cv_ld_exported_symbols_list=no
-      save_LDFLAGS=$LDFLAGS
-      echo "_main" > conftest.sym
-      LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
-      AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
-       [lt_cv_ld_exported_symbols_list=yes],
-       [lt_cv_ld_exported_symbols_list=no])
-       LDFLAGS="$save_LDFLAGS"
-    ])
-    case $host_os in
-    rhapsody* | darwin1.[[012]])
-      _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
-    darwin1.*)
-      _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
-    darwin*) # darwin 5.x on
-      # if running on 10.5 or later, the deployment target defaults
-      # to the OS version, if on x86, and 10.4, the deployment
-      # target defaults to 10.4. Don't you love it?
-      case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
-       10.0,*86*-darwin8*|10.0,*-darwin[[91]]*)
-         _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
-       10.[[012]]*)
-         _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
-       10.*)
-         _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
-      esac
-    ;;
-  esac
-    if test "$lt_cv_apple_cc_single_mod" = "yes"; then
-      _lt_dar_single_mod='$single_module'
-    fi
-    if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
-      _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
-    else
-      _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
-    fi
-    if test "$DSYMUTIL" != ":"; then
-      _lt_dsymutil='~$DSYMUTIL $lib || :'
-    else
-      _lt_dsymutil=
-    fi
-    ;;
-  esac
-])
-
-
-# _LT_DARWIN_LINKER_FEATURES
-# --------------------------
-# Checks for linker and compiler features on darwin
-m4_defun([_LT_DARWIN_LINKER_FEATURES],
-[
-  m4_require([_LT_REQUIRED_DARWIN_CHECKS])
-  _LT_TAGVAR(archive_cmds_need_lc, $1)=no
-  _LT_TAGVAR(hardcode_direct, $1)=no
-  _LT_TAGVAR(hardcode_automatic, $1)=yes
-  _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
-  _LT_TAGVAR(whole_archive_flag_spec, $1)=''
-  _LT_TAGVAR(link_all_deplibs, $1)=yes
-  _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined"
-  case $cc_basename in
-     ifort*) _lt_dar_can_shared=yes ;;
-     *) _lt_dar_can_shared=$GCC ;;
-  esac
-  if test "$_lt_dar_can_shared" = "yes"; then
-    output_verbose_link_cmd=echo
-    _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
-    _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
-    _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
-    _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
-    m4_if([$1], [CXX],
-[   if test "$lt_cv_apple_cc_single_mod" != "yes"; then
-      _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}"
-      _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}"
-    fi
-],[])
-  else
-  _LT_TAGVAR(ld_shlibs, $1)=no
-  fi
-])
-
-# _LT_SYS_MODULE_PATH_AIX
-# -----------------------
-# Links a minimal program and checks the executable
-# for the system default hardcoded library path. In most cases,
-# this is /usr/lib:/lib, but when the MPI compilers are used
-# the location of the communication and MPI libs are included too.
-# If we don't find anything, use the default library path according
-# to the aix ld manual.
-m4_defun([_LT_SYS_MODULE_PATH_AIX],
-[m4_require([_LT_DECL_SED])dnl
-AC_LINK_IFELSE(AC_LANG_PROGRAM,[
-lt_aix_libpath_sed='
-    /Import File Strings/,/^$/ {
-       /^0/ {
-           s/^0  *\(.*\)$/\1/
-           p
-       }
-    }'
-aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
-# Check for a 64-bit object if we didn't find anything.
-if test -z "$aix_libpath"; then
-  aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
-fi],[])
-if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
-])# _LT_SYS_MODULE_PATH_AIX
-
-
-# _LT_SHELL_INIT(ARG)
-# -------------------
-m4_define([_LT_SHELL_INIT],
-[ifdef([AC_DIVERSION_NOTICE],
-            [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)],
-        [AC_DIVERT_PUSH(NOTICE)])
-$1
-AC_DIVERT_POP
-])# _LT_SHELL_INIT
-
-
-# _LT_PROG_ECHO_BACKSLASH
-# -----------------------
-# Add some code to the start of the generated configure script which
-# will find an echo command which doesn't interpret backslashes.
-m4_defun([_LT_PROG_ECHO_BACKSLASH],
-[_LT_SHELL_INIT([
-# Check that we are running under the correct shell.
-SHELL=${CONFIG_SHELL-/bin/sh}
-
-case X$lt_ECHO in
-X*--fallback-echo)
-  # Remove one level of quotation (which was required for Make).
-  ECHO=`echo "$lt_ECHO" | sed 's,\\\\\[$]\\[$]0,'[$]0','`
-  ;;
-esac
-
-ECHO=${lt_ECHO-echo}
-if test "X[$]1" = X--no-reexec; then
-  # Discard the --no-reexec flag, and continue.
-  shift
-elif test "X[$]1" = X--fallback-echo; then
-  # Avoid inline document here, it may be left over
-  :
-elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then
-  # Yippee, $ECHO works!
-  :
-else
-  # Restart under the correct shell.
-  exec $SHELL "[$]0" --no-reexec ${1+"[$]@"}
-fi
-
-if test "X[$]1" = X--fallback-echo; then
-  # used as fallback echo
-  shift
-  cat <<_LT_EOF
-[$]*
-_LT_EOF
-  exit 0
-fi
-
-# The HP-UX ksh and POSIX shell print the target directory to stdout
-# if CDPATH is set.
-(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
-
-if test -z "$lt_ECHO"; then
-  if test "X${echo_test_string+set}" != Xset; then
-    # find a string as large as possible, as long as the shell can cope with it
-    for cmd in 'sed 50q "[$]0"' 'sed 20q "[$]0"' 'sed 10q "[$]0"' 'sed 2q "[$]0"' 'echo test'; do
-      # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ...
-      if { echo_test_string=`eval $cmd`; } 2>/dev/null &&
-        { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null
-      then
-        break
-      fi
-    done
-  fi
-
-  if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
-     echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
-     test "X$echo_testing_string" = "X$echo_test_string"; then
-    :
-  else
-    # The Solaris, AIX, and Digital Unix default echo programs unquote
-    # backslashes.  This makes it impossible to quote backslashes using
-    #   echo "$something" | sed 's/\\/\\\\/g'
-    #
-    # So, first we look for a working echo in the user's PATH.
-
-    lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
-    for dir in $PATH /usr/ucb; do
-      IFS="$lt_save_ifs"
-      if (test -f $dir/echo || test -f $dir/echo$ac_exeext) &&
-         test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' &&
-         echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` &&
-         test "X$echo_testing_string" = "X$echo_test_string"; then
-        ECHO="$dir/echo"
-        break
-      fi
-    done
-    IFS="$lt_save_ifs"
-
-    if test "X$ECHO" = Xecho; then
-      # We didn't find a better echo, so look for alternatives.
-      if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' &&
-         echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` &&
-         test "X$echo_testing_string" = "X$echo_test_string"; then
-        # This shell has a builtin print -r that does the trick.
-        ECHO='print -r'
-      elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } &&
-          test "X$CONFIG_SHELL" != X/bin/ksh; then
-        # If we have ksh, try running configure again with it.
-        ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
-        export ORIGINAL_CONFIG_SHELL
-        CONFIG_SHELL=/bin/ksh
-        export CONFIG_SHELL
-        exec $CONFIG_SHELL "[$]0" --no-reexec ${1+"[$]@"}
-      else
-        # Try using printf.
-        ECHO='printf %s\n'
-        if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
-          echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
-          test "X$echo_testing_string" = "X$echo_test_string"; then
-         # Cool, printf works
-         :
-        elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` &&
-            test "X$echo_testing_string" = 'X\t' &&
-            echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
-            test "X$echo_testing_string" = "X$echo_test_string"; then
-         CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL
-         export CONFIG_SHELL
-         SHELL="$CONFIG_SHELL"
-         export SHELL
-         ECHO="$CONFIG_SHELL [$]0 --fallback-echo"
-        elif echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` &&
-            test "X$echo_testing_string" = 'X\t' &&
-            echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
-            test "X$echo_testing_string" = "X$echo_test_string"; then
-         ECHO="$CONFIG_SHELL [$]0 --fallback-echo"
-        else
-         # maybe with a smaller string...
-         prev=:
-
-         for cmd in 'echo test' 'sed 2q "[$]0"' 'sed 10q "[$]0"' 'sed 20q "[$]0"' 'sed 50q "[$]0"'; do
-           if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null
-           then
-             break
-           fi
-           prev="$cmd"
-         done
-
-         if test "$prev" != 'sed 50q "[$]0"'; then
-           echo_test_string=`eval $prev`
-           export echo_test_string
-           exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "[$]0" ${1+"[$]@"}
-         else
-           # Oops.  We lost completely, so just stick with echo.
-           ECHO=echo
-         fi
-        fi
-      fi
-    fi
-  fi
-fi
-
-# Copy echo and quote the copy suitably for passing to libtool from
-# the Makefile, instead of quoting the original, which is used later.
-lt_ECHO=$ECHO
-if test "X$lt_ECHO" = "X$CONFIG_SHELL [$]0 --fallback-echo"; then
-   lt_ECHO="$CONFIG_SHELL \\\$\[$]0 --fallback-echo"
-fi
-
-AC_SUBST(lt_ECHO)
-])
-_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts])
-_LT_DECL([], [ECHO], [1],
-    [An echo program that does not interpret backslashes])
-])# _LT_PROG_ECHO_BACKSLASH
-
-
-# _LT_ENABLE_LOCK
-# ---------------
-m4_defun([_LT_ENABLE_LOCK],
-[AC_ARG_ENABLE([libtool-lock],
-  [AS_HELP_STRING([--disable-libtool-lock],
-    [avoid locking (might break parallel builds)])])
-test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
-
-# Some flags need to be propagated to the compiler or linker for good
-# libtool support.
-case $host in
-ia64-*-hpux*)
-  # Find out which ABI we are using.
-  echo 'int i;' > conftest.$ac_ext
-  if AC_TRY_EVAL(ac_compile); then
-    case `/usr/bin/file conftest.$ac_objext` in
-      *ELF-32*)
-       HPUX_IA64_MODE="32"
-       ;;
-      *ELF-64*)
-       HPUX_IA64_MODE="64"
-       ;;
-    esac
-  fi
-  rm -rf conftest*
-  ;;
-*-*-irix6*)
-  # Find out which ABI we are using.
-  echo '[#]line __oline__ "configure"' > conftest.$ac_ext
-  if AC_TRY_EVAL(ac_compile); then
-    if test "$lt_cv_prog_gnu_ld" = yes; then
-      case `/usr/bin/file conftest.$ac_objext` in
-       *32-bit*)
-         LD="${LD-ld} -melf32bsmip"
-         ;;
-       *N32*)
-         LD="${LD-ld} -melf32bmipn32"
-         ;;
-       *64-bit*)
-         LD="${LD-ld} -melf64bmip"
-       ;;
-      esac
-    else
-      case `/usr/bin/file conftest.$ac_objext` in
-       *32-bit*)
-         LD="${LD-ld} -32"
-         ;;
-       *N32*)
-         LD="${LD-ld} -n32"
-         ;;
-       *64-bit*)
-         LD="${LD-ld} -64"
-         ;;
-      esac
-    fi
-  fi
-  rm -rf conftest*
-  ;;
-
-x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
-s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
-  # Find out which ABI we are using.
-  echo 'int i;' > conftest.$ac_ext
-  if AC_TRY_EVAL(ac_compile); then
-    case `/usr/bin/file conftest.o` in
-      *32-bit*)
-       case $host in
-         x86_64-*kfreebsd*-gnu)
-           LD="${LD-ld} -m elf_i386_fbsd"
-           ;;
-         x86_64-*linux*)
-           LD="${LD-ld} -m elf_i386"
-           ;;
-         ppc64-*linux*|powerpc64-*linux*)
-           LD="${LD-ld} -m elf32ppclinux"
-           ;;
-         s390x-*linux*)
-           LD="${LD-ld} -m elf_s390"
-           ;;
-         sparc64-*linux*)
-           LD="${LD-ld} -m elf32_sparc"
-           ;;
-       esac
-       ;;
-      *64-bit*)
-       case $host in
-         x86_64-*kfreebsd*-gnu)
-           LD="${LD-ld} -m elf_x86_64_fbsd"
-           ;;
-         x86_64-*linux*)
-           LD="${LD-ld} -m elf_x86_64"
-           ;;
-         ppc*-*linux*|powerpc*-*linux*)
-           LD="${LD-ld} -m elf64ppc"
-           ;;
-         s390*-*linux*|s390*-*tpf*)
-           LD="${LD-ld} -m elf64_s390"
-           ;;
-         sparc*-*linux*)
-           LD="${LD-ld} -m elf64_sparc"
-           ;;
-       esac
-       ;;
-    esac
-  fi
-  rm -rf conftest*
-  ;;
-
-*-*-sco3.2v5*)
-  # On SCO OpenServer 5, we need -belf to get full-featured binaries.
-  SAVE_CFLAGS="$CFLAGS"
-  CFLAGS="$CFLAGS -belf"
-  AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf,
-    [AC_LANG_PUSH(C)
-     AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no])
-     AC_LANG_POP])
-  if test x"$lt_cv_cc_needs_belf" != x"yes"; then
-    # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
-    CFLAGS="$SAVE_CFLAGS"
-  fi
-  ;;
-sparc*-*solaris*)
-  # Find out which ABI we are using.
-  echo 'int i;' > conftest.$ac_ext
-  if AC_TRY_EVAL(ac_compile); then
-    case `/usr/bin/file conftest.o` in
-    *64-bit*)
-      case $lt_cv_prog_gnu_ld in
-      yes*) LD="${LD-ld} -m elf64_sparc" ;;
-      *)
-       if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
-         LD="${LD-ld} -64"
-       fi
-       ;;
-      esac
-      ;;
-    esac
-  fi
-  rm -rf conftest*
-  ;;
-esac
-
-need_locks="$enable_libtool_lock"
-])# _LT_ENABLE_LOCK
-
-
-# _LT_CMD_OLD_ARCHIVE
-# -------------------
-m4_defun([_LT_CMD_OLD_ARCHIVE],
-[AC_CHECK_TOOL(AR, ar, false)
-test -z "$AR" && AR=ar
-test -z "$AR_FLAGS" && AR_FLAGS=cru
-_LT_DECL([], [AR], [1], [The archiver])
-_LT_DECL([], [AR_FLAGS], [1])
-
-AC_CHECK_TOOL(STRIP, strip, :)
-test -z "$STRIP" && STRIP=:
-_LT_DECL([], [STRIP], [1], [A symbol stripping program])
-
-AC_CHECK_TOOL(RANLIB, ranlib, :)
-test -z "$RANLIB" && RANLIB=:
-_LT_DECL([], [RANLIB], [1],
-    [Commands used to install an old-style archive])
-
-# Determine commands to create old-style static archives.
-old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
-old_postinstall_cmds='chmod 644 $oldlib'
-old_postuninstall_cmds=
-
-if test -n "$RANLIB"; then
-  case $host_os in
-  openbsd*)
-    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib"
-    ;;
-  *)
-    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib"
-    ;;
-  esac
-  old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
-fi
-_LT_DECL([], [old_postinstall_cmds], [2])
-_LT_DECL([], [old_postuninstall_cmds], [2])
-_LT_TAGDECL([], [old_archive_cmds], [2],
-    [Commands used to build an old-style archive])
-])# _LT_CMD_OLD_ARCHIVE
-
-
-# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
-#              [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE])
-# ----------------------------------------------------------------
-# Check whether the given compiler option works
-AC_DEFUN([_LT_COMPILER_OPTION],
-[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
-m4_require([_LT_DECL_SED])dnl
-AC_CACHE_CHECK([$1], [$2],
-  [$2=no
-   m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4])
-   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
-   lt_compiler_flag="$3"
-   # Insert the option either (1) after the last *FLAGS variable, or
-   # (2) before a word containing "conftest.", or (3) at the end.
-   # Note that $ac_compile itself does not contain backslashes and begins
-   # with a dollar sign (not a hyphen), so the echo should work correctly.
-   # The option is referenced via a variable to avoid confusing sed.
-   lt_compile=`echo "$ac_compile" | $SED \
-   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-   -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
-   -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
-   (eval "$lt_compile" 2>conftest.err)
-   ac_status=$?
-   cat conftest.err >&AS_MESSAGE_LOG_FD
-   echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
-   if (exit $ac_status) && test -s "$ac_outfile"; then
-     # The compiler can only warn and ignore the option if not recognized
-     # So say no if there are warnings other than the usual output.
-     $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
-     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
-     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
-       $2=yes
-     fi
-   fi
-   $RM conftest*
-])
-
-if test x"[$]$2" = xyes; then
-    m4_if([$5], , :, [$5])
-else
-    m4_if([$6], , :, [$6])
-fi
-])# _LT_COMPILER_OPTION
-
-# Old name:
-AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION])
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [])
-
-
-# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
-#                  [ACTION-SUCCESS], [ACTION-FAILURE])
-# ----------------------------------------------------
-# Check whether the given linker option works
-AC_DEFUN([_LT_LINKER_OPTION],
-[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
-m4_require([_LT_DECL_SED])dnl
-AC_CACHE_CHECK([$1], [$2],
-  [$2=no
-   save_LDFLAGS="$LDFLAGS"
-   LDFLAGS="$LDFLAGS $3"
-   echo "$lt_simple_link_test_code" > conftest.$ac_ext
-   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
-     # The linker can only warn and ignore the option if not recognized
-     # So say no if there are warnings
-     if test -s conftest.err; then
-       # Append any errors to the config.log.
-       cat conftest.err 1>&AS_MESSAGE_LOG_FD
-       $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp
-       $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
-       if diff conftest.exp conftest.er2 >/dev/null; then
-         $2=yes
-       fi
-     else
-       $2=yes
-     fi
-   fi
-   $RM -r conftest*
-   LDFLAGS="$save_LDFLAGS"
-])
-
-if test x"[$]$2" = xyes; then
-    m4_if([$4], , :, [$4])
-else
-    m4_if([$5], , :, [$5])
-fi
-])# _LT_LINKER_OPTION
-
-# Old name:
-AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION])
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], [])
-
-
-# LT_CMD_MAX_LEN
-#---------------
-AC_DEFUN([LT_CMD_MAX_LEN],
-[AC_REQUIRE([AC_CANONICAL_HOST])dnl
-# find the maximum length of command line arguments
-AC_MSG_CHECKING([the maximum length of command line arguments])
-AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
-  i=0
-  teststring="ABCD"
-
-  case $build_os in
-  msdosdjgpp*)
-    # On DJGPP, this test can blow up pretty badly due to problems in libc
-    # (any single argument exceeding 2000 bytes causes a buffer overrun
-    # during glob expansion).  Even if it were fixed, the result of this
-    # check would be larger than it should be.
-    lt_cv_sys_max_cmd_len=12288;    # 12K is about right
-    ;;
-
-  gnu*)
-    # Under GNU Hurd, this test is not required because there is
-    # no limit to the length of command line arguments.
-    # Libtool will interpret -1 as no limit whatsoever
-    lt_cv_sys_max_cmd_len=-1;
-    ;;
-
-  cygwin* | mingw* | cegcc*)
-    # On Win9x/ME, this test blows up -- it succeeds, but takes
-    # about 5 minutes as the teststring grows exponentially.
-    # Worse, since 9x/ME are not pre-emptively multitasking,
-    # you end up with a "frozen" computer, even though with patience
-    # the test eventually succeeds (with a max line length of 256k).
-    # Instead, let's just punt: use the minimum linelength reported by
-    # all of the supported platforms: 8192 (on NT/2K/XP).
-    lt_cv_sys_max_cmd_len=8192;
-    ;;
-
-  amigaos*)
-    # On AmigaOS with pdksh, this test takes hours, literally.
-    # So we just punt and use a minimum line length of 8192.
-    lt_cv_sys_max_cmd_len=8192;
-    ;;
-
-  netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
-    # This has been around since 386BSD, at least.  Likely further.
-    if test -x /sbin/sysctl; then
-      lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
-    elif test -x /usr/sbin/sysctl; then
-      lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
-    else
-      lt_cv_sys_max_cmd_len=65536      # usable default for all BSDs
-    fi
-    # And add a safety zone
-    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
-    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
-    ;;
-
-  interix*)
-    # We know the value 262144 and hardcode it with a safety zone (like BSD)
-    lt_cv_sys_max_cmd_len=196608
-    ;;
-
-  osf*)
-    # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
-    # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
-    # nice to cause kernel panics so lets avoid the loop below.
-    # First set a reasonable default.
-    lt_cv_sys_max_cmd_len=16384
-    #
-    if test -x /sbin/sysconfig; then
-      case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
-        *1*) lt_cv_sys_max_cmd_len=-1 ;;
-      esac
-    fi
-    ;;
-  sco3.2v5*)
-    lt_cv_sys_max_cmd_len=102400
-    ;;
-  sysv5* | sco5v6* | sysv4.2uw2*)
-    kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
-    if test -n "$kargmax"; then
-      lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[        ]]//'`
-    else
-      lt_cv_sys_max_cmd_len=32768
-    fi
-    ;;
-  *)
-    lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
-    if test -n "$lt_cv_sys_max_cmd_len"; then
-      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
-      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
-    else
-      # Make teststring a little bigger before we do anything with it.
-      # a 1K string should be a reasonable start.
-      for i in 1 2 3 4 5 6 7 8 ; do
-        teststring=$teststring$teststring
-      done
-      SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
-      # If test is not a shell built-in, we'll probably end up computing a
-      # maximum length that is only half of the actual maximum length, but
-      # we can't tell.
-      while { test "X"`$SHELL [$]0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \
-                = "XX$teststring$teststring"; } >/dev/null 2>&1 &&
-             test $i != 17 # 1/2 MB should be enough
-      do
-        i=`expr $i + 1`
-        teststring=$teststring$teststring
-      done
-      # Only check the string length outside the loop.
-      lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
-      teststring=
-      # Add a significant safety factor because C++ compilers can tack on
-      # massive amounts of additional arguments before passing them to the
-      # linker.  It appears as though 1/2 is a usable value.
-      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
-    fi
-    ;;
-  esac
-])
-if test -n $lt_cv_sys_max_cmd_len ; then
-  AC_MSG_RESULT($lt_cv_sys_max_cmd_len)
-else
-  AC_MSG_RESULT(none)
-fi
-max_cmd_len=$lt_cv_sys_max_cmd_len
-_LT_DECL([], [max_cmd_len], [0],
-    [What is the maximum length of a command?])
-])# LT_CMD_MAX_LEN
-
-# Old name:
-AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN])
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [])
-
-
-# _LT_HEADER_DLFCN
-# ----------------
-m4_defun([_LT_HEADER_DLFCN],
-[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl
-])# _LT_HEADER_DLFCN
-
-
-# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE,
-#                      ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING)
-# ----------------------------------------------------------------
-m4_defun([_LT_TRY_DLOPEN_SELF],
-[m4_require([_LT_HEADER_DLFCN])dnl
-if test "$cross_compiling" = yes; then :
-  [$4]
-else
-  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
-  lt_status=$lt_dlunknown
-  cat > conftest.$ac_ext <<_LT_EOF
-[#line __oline__ "configure"
-#include "confdefs.h"
-
-#if HAVE_DLFCN_H
-#include <dlfcn.h>
-#endif
-
-#include <stdio.h>
-
-#ifdef RTLD_GLOBAL
-#  define LT_DLGLOBAL          RTLD_GLOBAL
-#else
-#  ifdef DL_GLOBAL
-#    define LT_DLGLOBAL                DL_GLOBAL
-#  else
-#    define LT_DLGLOBAL                0
-#  endif
-#endif
-
-/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
-   find out it does not work in some platform. */
-#ifndef LT_DLLAZY_OR_NOW
-#  ifdef RTLD_LAZY
-#    define LT_DLLAZY_OR_NOW           RTLD_LAZY
-#  else
-#    ifdef DL_LAZY
-#      define LT_DLLAZY_OR_NOW         DL_LAZY
-#    else
-#      ifdef RTLD_NOW
-#        define LT_DLLAZY_OR_NOW       RTLD_NOW
-#      else
-#        ifdef DL_NOW
-#          define LT_DLLAZY_OR_NOW     DL_NOW
-#        else
-#          define LT_DLLAZY_OR_NOW     0
-#        endif
-#      endif
-#    endif
-#  endif
-#endif
-
-void fnord() { int i=42;}
-int main ()
-{
-  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
-  int status = $lt_dlunknown;
-
-  if (self)
-    {
-      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
-      else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
-      /* dlclose (self); */
-    }
-  else
-    puts (dlerror ());
-
-  return status;
-}]
-_LT_EOF
-  if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then
-    (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null
-    lt_status=$?
-    case x$lt_status in
-      x$lt_dlno_uscore) $1 ;;
-      x$lt_dlneed_uscore) $2 ;;
-      x$lt_dlunknown|x*) $3 ;;
-    esac
-  else :
-    # compilation failed
-    $3
-  fi
-fi
-rm -fr conftest*
-])# _LT_TRY_DLOPEN_SELF
-
-
-# LT_SYS_DLOPEN_SELF
-# ------------------
-AC_DEFUN([LT_SYS_DLOPEN_SELF],
-[m4_require([_LT_HEADER_DLFCN])dnl
-if test "x$enable_dlopen" != xyes; then
-  enable_dlopen=unknown
-  enable_dlopen_self=unknown
-  enable_dlopen_self_static=unknown
-else
-  lt_cv_dlopen=no
-  lt_cv_dlopen_libs=
-
-  case $host_os in
-  beos*)
-    lt_cv_dlopen="load_add_on"
-    lt_cv_dlopen_libs=
-    lt_cv_dlopen_self=yes
-    ;;
-
-  mingw* | pw32* | cegcc*)
-    lt_cv_dlopen="LoadLibrary"
-    lt_cv_dlopen_libs=
-    ;;
-
-  cygwin*)
-    lt_cv_dlopen="dlopen"
-    lt_cv_dlopen_libs=
-    ;;
-
-  darwin*)
-  # if libdl is installed we need to link against it
-    AC_CHECK_LIB([dl], [dlopen],
-               [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[
-    lt_cv_dlopen="dyld"
-    lt_cv_dlopen_libs=
-    lt_cv_dlopen_self=yes
-    ])
-    ;;
-
-  *)
-    AC_CHECK_FUNC([shl_load],
-         [lt_cv_dlopen="shl_load"],
-      [AC_CHECK_LIB([dld], [shl_load],
-           [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"],
-       [AC_CHECK_FUNC([dlopen],
-             [lt_cv_dlopen="dlopen"],
-         [AC_CHECK_LIB([dl], [dlopen],
-               [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],
-           [AC_CHECK_LIB([svld], [dlopen],
-                 [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"],
-             [AC_CHECK_LIB([dld], [dld_link],
-                   [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"])
-             ])
-           ])
-         ])
-       ])
-      ])
-    ;;
-  esac
-
-  if test "x$lt_cv_dlopen" != xno; then
-    enable_dlopen=yes
-  else
-    enable_dlopen=no
-  fi
-
-  case $lt_cv_dlopen in
-  dlopen)
-    save_CPPFLAGS="$CPPFLAGS"
-    test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
-
-    save_LDFLAGS="$LDFLAGS"
-    wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
-
-    save_LIBS="$LIBS"
-    LIBS="$lt_cv_dlopen_libs $LIBS"
-
-    AC_CACHE_CHECK([whether a program can dlopen itself],
-         lt_cv_dlopen_self, [dnl
-         _LT_TRY_DLOPEN_SELF(
-           lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes,
-           lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross)
-    ])
-
-    if test "x$lt_cv_dlopen_self" = xyes; then
-      wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
-      AC_CACHE_CHECK([whether a statically linked program can dlopen itself],
-         lt_cv_dlopen_self_static, [dnl
-         _LT_TRY_DLOPEN_SELF(
-           lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes,
-           lt_cv_dlopen_self_static=no,  lt_cv_dlopen_self_static=cross)
-      ])
-    fi
-
-    CPPFLAGS="$save_CPPFLAGS"
-    LDFLAGS="$save_LDFLAGS"
-    LIBS="$save_LIBS"
-    ;;
-  esac
-
-  case $lt_cv_dlopen_self in
-  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
-  *) enable_dlopen_self=unknown ;;
-  esac
-
-  case $lt_cv_dlopen_self_static in
-  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
-  *) enable_dlopen_self_static=unknown ;;
-  esac
-fi
-_LT_DECL([dlopen_support], [enable_dlopen], [0],
-        [Whether dlopen is supported])
-_LT_DECL([dlopen_self], [enable_dlopen_self], [0],
-        [Whether dlopen of programs is supported])
-_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0],
-        [Whether dlopen of statically linked programs is supported])
-])# LT_SYS_DLOPEN_SELF
-
-# Old name:
-AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF])
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [])
-
-
-# _LT_COMPILER_C_O([TAGNAME])
-# ---------------------------
-# Check to see if options -c and -o are simultaneously supported by compiler.
-# This macro does not hard code the compiler like AC_PROG_CC_C_O.
-m4_defun([_LT_COMPILER_C_O],
-[m4_require([_LT_DECL_SED])dnl
-m4_require([_LT_FILEUTILS_DEFAULTS])dnl
-m4_require([_LT_TAG_COMPILER])dnl
-AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext],
-  [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)],
-  [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no
-   $RM -r conftest 2>/dev/null
-   mkdir conftest
-   cd conftest
-   mkdir out
-   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
-
-   lt_compiler_flag="-o out/conftest2.$ac_objext"
-   # Insert the option either (1) after the last *FLAGS variable, or
-   # (2) before a word containing "conftest.", or (3) at the end.
-   # Note that $ac_compile itself does not contain backslashes and begins
-   # with a dollar sign (not a hyphen), so the echo should work correctly.
-   lt_compile=`echo "$ac_compile" | $SED \
-   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-   -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
-   -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
-   (eval "$lt_compile" 2>out/conftest.err)
-   ac_status=$?
-   cat out/conftest.err >&AS_MESSAGE_LOG_FD
-   echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
-   if (exit $ac_status) && test -s out/conftest2.$ac_objext
-   then
-     # The compiler can only warn and ignore the option if not recognized
-     # So say no if there are warnings
-     $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
-     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
-     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
-       _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
-     fi
-   fi
-   chmod u+w . 2>&AS_MESSAGE_LOG_FD
-   $RM conftest*
-   # SGI C++ compiler will create directory out/ii_files/ for
-   # template instantiation
-   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
-   $RM out/* && rmdir out
-   cd ..
-   $RM -r conftest
-   $RM conftest*
-])
-_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1],
-       [Does compiler simultaneously support -c and -o options?])
-])# _LT_COMPILER_C_O
-
-
-# _LT_COMPILER_FILE_LOCKS([TAGNAME])
-# ----------------------------------
-# Check to see if we can do hard links to lock some files if needed
-m4_defun([_LT_COMPILER_FILE_LOCKS],
-[m4_require([_LT_ENABLE_LOCK])dnl
-m4_require([_LT_FILEUTILS_DEFAULTS])dnl
-_LT_COMPILER_C_O([$1])
-
-hard_links="nottested"
-if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then
-  # do not overwrite the value of need_locks provided by the user
-  AC_MSG_CHECKING([if we can lock with hard links])
-  hard_links=yes
-  $RM conftest*
-  ln conftest.a conftest.b 2>/dev/null && hard_links=no
-  touch conftest.a
-  ln conftest.a conftest.b 2>&5 || hard_links=no
-  ln conftest.a conftest.b 2>/dev/null && hard_links=no
-  AC_MSG_RESULT([$hard_links])
-  if test "$hard_links" = no; then
-    AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe])
-    need_locks=warn
-  fi
-else
-  need_locks=no
-fi
-_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?])
-])# _LT_COMPILER_FILE_LOCKS
-
-
-# _LT_CHECK_OBJDIR
-# ----------------
-m4_defun([_LT_CHECK_OBJDIR],
-[AC_CACHE_CHECK([for objdir], [lt_cv_objdir],
-[rm -f .libs 2>/dev/null
-mkdir .libs 2>/dev/null
-if test -d .libs; then
-  lt_cv_objdir=.libs
-else
-  # MS-DOS does not allow filenames that begin with a dot.
-  lt_cv_objdir=_libs
-fi
-rmdir .libs 2>/dev/null])
-objdir=$lt_cv_objdir
-_LT_DECL([], [objdir], [0],
-         [The name of the directory that contains temporary libtool files])dnl
-m4_pattern_allow([LT_OBJDIR])dnl
-AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/",
-  [Define to the sub-directory in which libtool stores uninstalled libraries.])
-])# _LT_CHECK_OBJDIR
-
-
-# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME])
-# --------------------------------------
-# Check hardcoding attributes.
-m4_defun([_LT_LINKER_HARDCODE_LIBPATH],
-[AC_MSG_CHECKING([how to hardcode library paths into programs])
-_LT_TAGVAR(hardcode_action, $1)=
-if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" ||
-   test -n "$_LT_TAGVAR(runpath_var, $1)" ||
-   test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then
-
-  # We can hardcode non-existent directories.
-  if test "$_LT_TAGVAR(hardcode_direct, $1)" != no &&
-     # If the only mechanism to avoid hardcoding is shlibpath_var, we
-     # have to relink, otherwise we might link with an installed library
-     # when we should be linking with a yet-to-be-installed one
-     ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no &&
-     test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then
-    # Linking always hardcodes the temporary library directory.
-    _LT_TAGVAR(hardcode_action, $1)=relink
-  else
-    # We can link without hardcoding, and we can hardcode nonexisting dirs.
-    _LT_TAGVAR(hardcode_action, $1)=immediate
-  fi
-else
-  # We cannot hardcode anything, or else we can only hardcode existing
-  # directories.
-  _LT_TAGVAR(hardcode_action, $1)=unsupported
-fi
-AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)])
-
-if test "$_LT_TAGVAR(hardcode_action, $1)" = relink ||
-   test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then
-  # Fast installation is not supported
-  enable_fast_install=no
-elif test "$shlibpath_overrides_runpath" = yes ||
-     test "$enable_shared" = no; then
-  # Fast installation is not necessary
-  enable_fast_install=needless
-fi
-_LT_TAGDECL([], [hardcode_action], [0],
-    [How to hardcode a shared library path into an executable])
-])# _LT_LINKER_HARDCODE_LIBPATH
-
-
-# _LT_CMD_STRIPLIB
-# ----------------
-m4_defun([_LT_CMD_STRIPLIB],
-[m4_require([_LT_DECL_EGREP])
-striplib=
-old_striplib=
-AC_MSG_CHECKING([whether stripping libraries is possible])
-if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
-  test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
-  test -z "$striplib" && striplib="$STRIP --strip-unneeded"
-  AC_MSG_RESULT([yes])
-else
-# FIXME - insert some real tests, host_os isn't really good enough
-  case $host_os in
-  darwin*)
-    if test -n "$STRIP" ; then
-      striplib="$STRIP -x"
-      old_striplib="$STRIP -S"
-      AC_MSG_RESULT([yes])
-    else
-      AC_MSG_RESULT([no])
-    fi
-    ;;
-  *)
-    AC_MSG_RESULT([no])
-    ;;
-  esac
-fi
-_LT_DECL([], [old_striplib], [1], [Commands to strip libraries])
-_LT_DECL([], [striplib], [1])
-])# _LT_CMD_STRIPLIB
-
-
-# _LT_SYS_DYNAMIC_LINKER([TAG])
-# -----------------------------
-# PORTME Fill in your ld.so characteristics
-m4_defun([_LT_SYS_DYNAMIC_LINKER],
-[AC_REQUIRE([AC_CANONICAL_HOST])dnl
-m4_require([_LT_DECL_EGREP])dnl
-m4_require([_LT_FILEUTILS_DEFAULTS])dnl
-m4_require([_LT_DECL_OBJDUMP])dnl
-m4_require([_LT_DECL_SED])dnl
-AC_MSG_CHECKING([dynamic linker characteristics])
-m4_if([$1],
-       [], [
-if test "$GCC" = yes; then
-  case $host_os in
-    darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
-    *) lt_awk_arg="/^libraries:/" ;;
-  esac
-  lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"`
-  if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then
-    # if the path contains ";" then we assume it to be the separator
-    # otherwise default to the standard path separator (i.e. ":") - it is
-    # assumed that no part of a normal pathname contains ";" but that should
-    # okay in the real world where ";" in dirpaths is itself problematic.
-    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'`
-  else
-    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED  -e "s/$PATH_SEPARATOR/ /g"`
-  fi
-  # Ok, now we have the path, separated by spaces, we can step through it
-  # and add multilib dir if necessary.
-  lt_tmp_lt_search_path_spec=
-  lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
-  for lt_sys_path in $lt_search_path_spec; do
-    if test -d "$lt_sys_path/$lt_multi_os_dir"; then
-      lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
-    else
-      test -d "$lt_sys_path" && \
-       lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
-    fi
-  done
-  lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk '
-BEGIN {RS=" "; FS="/|\n";} {
-  lt_foo="";
-  lt_count=0;
-  for (lt_i = NF; lt_i > 0; lt_i--) {
-    if ($lt_i != "" && $lt_i != ".") {
-      if ($lt_i == "..") {
-        lt_count++;
-      } else {
-        if (lt_count == 0) {
-          lt_foo="/" $lt_i lt_foo;
-        } else {
-          lt_count--;
-        }
-      }
-    }
-  }
-  if (lt_foo != "") { lt_freq[[lt_foo]]++; }
-  if (lt_freq[[lt_foo]] == 1) { print lt_foo; }
-}'`
-  sys_lib_search_path_spec=`$ECHO $lt_search_path_spec`
-else
-  sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
-fi])
-library_names_spec=
-libname_spec='lib$name'
-soname_spec=
-shrext_cmds=".so"
-postinstall_cmds=
-postuninstall_cmds=
-finish_cmds=
-finish_eval=
-shlibpath_var=
-shlibpath_overrides_runpath=unknown
-version_type=none
-dynamic_linker="$host_os ld.so"
-sys_lib_dlsearch_path_spec="/lib /usr/lib"
-need_lib_prefix=unknown
-hardcode_into_libs=no
-
-# when you set need_version to no, make sure it does not cause -set_version
-# flags to be left without arguments
-need_version=unknown
-
-case $host_os in
-aix3*)
-  version_type=linux
-  library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
-  shlibpath_var=LIBPATH
-
-  # AIX 3 has no versioning support, so we append a major version to the name.
-  soname_spec='${libname}${release}${shared_ext}$major'
-  ;;
-
-aix[[4-9]]*)
-  version_type=linux
-  need_lib_prefix=no
-  need_version=no
-  hardcode_into_libs=yes
-  if test "$host_cpu" = ia64; then
-    # AIX 5 supports IA64
-    library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
-    shlibpath_var=LD_LIBRARY_PATH
-  else
-    # With GCC up to 2.95.x, collect2 would create an import file
-    # for dependence libraries.  The import file would start with
-    # the line `#! .'.  This would cause the generated library to
-    # depend on `.', always an invalid library.  This was fixed in
-    # development snapshots of GCC prior to 3.0.
-    case $host_os in
-      aix4 | aix4.[[01]] | aix4.[[01]].*)
-      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
-          echo ' yes '
-          echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
-       :
-      else
-       can_build_shared=no
-      fi
-      ;;
-    esac
-    # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
-    # soname into executable. Probably we can add versioning support to
-    # collect2, so additional links can be useful in future.
-    if test "$aix_use_runtimelinking" = yes; then
-      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
-      # instead of lib<name>.a to let people know that these are not
-      # typical AIX shared libraries.
-      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-    else
-      # We preserve .a as extension for shared libraries through AIX4.2
-      # and later when we are not doing run time linking.
-      library_names_spec='${libname}${release}.a $libname.a'
-      soname_spec='${libname}${release}${shared_ext}$major'
-    fi
-    shlibpath_var=LIBPATH
-  fi
-  ;;
-
-amigaos*)
-  case $host_cpu in
-  powerpc)
-    # Since July 2007 AmigaOS4 officially supports .so libraries.
-    # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
-    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-    ;;
-  m68k)
-    library_names_spec='$libname.ixlibrary $libname.a'
-    # Create ${libname}_ixlibrary.a entries in /sys/libs.
-    finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
-    ;;
-  esac
-  ;;
-
-beos*)
-  library_names_spec='${libname}${shared_ext}'
-  dynamic_linker="$host_os ld.so"
-  shlibpath_var=LIBRARY_PATH
-  ;;
-
-bsdi[[45]]*)
-  version_type=linux
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
-  shlibpath_var=LD_LIBRARY_PATH
-  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
-  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
-  # the default ld.so.conf also contains /usr/contrib/lib and
-  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
-  # libtool to hard-code these into programs
-  ;;
-
-cygwin* | mingw* | pw32* | cegcc*)
-  version_type=windows
-  shrext_cmds=".dll"
-  need_version=no
-  need_lib_prefix=no
-
-  case $GCC,$host_os in
-  yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*)
-    library_names_spec='$libname.dll.a'
-    # DLL is installed to $(libdir)/../bin by postinstall_cmds
-    postinstall_cmds='base_file=`basename \${file}`~
-      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
-      dldir=$destdir/`dirname \$dlpath`~
-      test -d \$dldir || mkdir -p \$dldir~
-      $install_prog $dir/$dlname \$dldir/$dlname~
-      chmod a+x \$dldir/$dlname~
-      if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
-        eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
-      fi'
-    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
-      dlpath=$dir/\$dldll~
-       $RM \$dlpath'
-    shlibpath_overrides_runpath=yes
-
-    case $host_os in
-    cygwin*)
-      # Cygwin DLLs use 'cyg' prefix rather than 'lib'
-      soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
-      sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib"
-      ;;
-    mingw* | cegcc*)
-      # MinGW DLLs use traditional 'lib' prefix
-      soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
-      sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
-      if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then
-        # It is most probably a Windows format PATH printed by
-        # mingw gcc, but we are running on Cygwin. Gcc prints its search
-        # path with ; separators, and with drive letters. We can handle the
-        # drive letters (cygwin fileutils understands them), so leave them,
-        # especially as we might pass files found there to a mingw objdump,
-        # which wouldn't understand a cygwinified path. Ahh.
-        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
-      else
-        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED  -e "s/$PATH_SEPARATOR/ /g"`
-      fi
-      ;;
-    pw32*)
-      # pw32 DLLs use 'pw' prefix rather than 'lib'
-      library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
-      ;;
-    esac
-    ;;
-
-  *)
-    library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib'
-    ;;
-  esac
-  dynamic_linker='Win32 ld.exe'
-  # FIXME: first we should search . and the directory the executable is in
-  shlibpath_var=PATH
-  ;;
-
-darwin* | rhapsody*)
-  dynamic_linker="$host_os dyld"
-  version_type=darwin
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
-  soname_spec='${libname}${release}${major}$shared_ext'
-  shlibpath_overrides_runpath=yes
-  shlibpath_var=DYLD_LIBRARY_PATH
-  shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
-m4_if([$1], [],[
-  sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"])
-  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
-  ;;
-
-dgux*)
-  version_type=linux
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  shlibpath_var=LD_LIBRARY_PATH
-  ;;
-
-freebsd1*)
-  dynamic_linker=no
-  ;;
-
-freebsd* | dragonfly*)
-  # DragonFly does not have aout.  When/if they implement a new
-  # versioning mechanism, adjust this.
-  if test -x /usr/bin/objformat; then
-    objformat=`/usr/bin/objformat`
-  else
-    case $host_os in
-    freebsd[[123]]*) objformat=aout ;;
-    *) objformat=elf ;;
-    esac
-  fi
-  version_type=freebsd-$objformat
-  case $version_type in
-    freebsd-elf*)
-      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
-      need_version=no
-      need_lib_prefix=no
-      ;;
-    freebsd-*)
-      library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
-      need_version=yes
-      ;;
-  esac
-  shlibpath_var=LD_LIBRARY_PATH
-  case $host_os in
-  freebsd2*)
-    shlibpath_overrides_runpath=yes
-    ;;
-  freebsd3.[[01]]* | freebsdelf3.[[01]]*)
-    shlibpath_overrides_runpath=yes
-    hardcode_into_libs=yes
-    ;;
-  freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \
-  freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1)
-    shlibpath_overrides_runpath=no
-    hardcode_into_libs=yes
-    ;;
-  *) # from 4.6 on, and DragonFly
-    shlibpath_overrides_runpath=yes
-    hardcode_into_libs=yes
-    ;;
-  esac
-  ;;
-
-gnu*)
-  version_type=linux
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  shlibpath_var=LD_LIBRARY_PATH
-  hardcode_into_libs=yes
-  ;;
-
-hpux9* | hpux10* | hpux11*)
-  # Give a soname corresponding to the major version so that dld.sl refuses to
-  # link against other versions.
-  version_type=sunos
-  need_lib_prefix=no
-  need_version=no
-  case $host_cpu in
-  ia64*)
-    shrext_cmds='.so'
-    hardcode_into_libs=yes
-    dynamic_linker="$host_os dld.so"
-    shlibpath_var=LD_LIBRARY_PATH
-    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
-    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-    soname_spec='${libname}${release}${shared_ext}$major'
-    if test "X$HPUX_IA64_MODE" = X32; then
-      sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
-    else
-      sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
-    fi
-    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
-    ;;
-  hppa*64*)
-    shrext_cmds='.sl'
-    hardcode_into_libs=yes
-    dynamic_linker="$host_os dld.sl"
-    shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
-    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
-    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-    soname_spec='${libname}${release}${shared_ext}$major'
-    sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
-    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
-    ;;
-  *)
-    shrext_cmds='.sl'
-    dynamic_linker="$host_os dld.sl"
-    shlibpath_var=SHLIB_PATH
-    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
-    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-    soname_spec='${libname}${release}${shared_ext}$major'
-    ;;
-  esac
-  # HP-UX runs *really* slowly unless shared libraries are mode 555.
-  postinstall_cmds='chmod 555 $lib'
-  ;;
-
-interix[[3-9]]*)
-  version_type=linux
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=no
-  hardcode_into_libs=yes
-  ;;
-
-irix5* | irix6* | nonstopux*)
-  case $host_os in
-    nonstopux*) version_type=nonstopux ;;
-    *)
-       if test "$lt_cv_prog_gnu_ld" = yes; then
-               version_type=linux
-       else
-               version_type=irix
-       fi ;;
-  esac
-  need_lib_prefix=no
-  need_version=no
-  soname_spec='${libname}${release}${shared_ext}$major'
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
-  case $host_os in
-  irix5* | nonstopux*)
-    libsuff= shlibsuff=
-    ;;
-  *)
-    case $LD in # libtool.m4 will add one of these switches to LD
-    *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
-      libsuff= shlibsuff= libmagic=32-bit;;
-    *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
-      libsuff=32 shlibsuff=N32 libmagic=N32;;
-    *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
-      libsuff=64 shlibsuff=64 libmagic=64-bit;;
-    *) libsuff= shlibsuff= libmagic=never-match;;
-    esac
-    ;;
-  esac
-  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
-  shlibpath_overrides_runpath=no
-  sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
-  sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
-  hardcode_into_libs=yes
-  ;;
-
-# No shared lib support for Linux oldld, aout, or coff.
-linux*oldld* | linux*aout* | linux*coff*)
-  dynamic_linker=no
-  ;;
-
-# This must be Linux ELF.
-linux* | k*bsd*-gnu | kopensolaris*-gnu)
-  version_type=linux
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=no
-  # Some binutils ld are patched to set DT_RUNPATH
-  save_LDFLAGS=$LDFLAGS
-  save_libdir=$libdir
-  eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \
-       LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\""
-  AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
-    [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null],
-       [shlibpath_overrides_runpath=yes])])
-  LDFLAGS=$save_LDFLAGS
-  libdir=$save_libdir
-
-  # This implies no fast_install, which is unacceptable.
-  # Some rework will be needed to allow for fast_install
-  # before this can be enabled.
-  hardcode_into_libs=yes
-
-  # Append ld.so.conf contents to the search path
-  if test -f /etc/ld.so.conf; then
-    lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[  ]*hwcap[        ]/d;s/[:,      ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '`
-    sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
-  fi
-
-  # We used to test for /lib/ld.so.1 and disable shared libraries on
-  # powerpc, because MkLinux only supported shared libraries with the
-  # GNU dynamic linker.  Since this was broken with cross compilers,
-  # most powerpc-linux boxes support dynamic linking these days and
-  # people can always --disable-shared, the test was removed, and we
-  # assume the GNU/Linux dynamic linker is in use.
-  dynamic_linker='GNU/Linux ld.so'
-  ;;
-
-netbsdelf*-gnu)
-  version_type=linux
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=no
-  hardcode_into_libs=yes
-  dynamic_linker='NetBSD ld.elf_so'
-  ;;
-
-netbsd*)
-  version_type=sunos
-  need_lib_prefix=no
-  need_version=no
-  if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
-    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
-    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
-    dynamic_linker='NetBSD (a.out) ld.so'
-  else
-    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
-    soname_spec='${libname}${release}${shared_ext}$major'
-    dynamic_linker='NetBSD ld.elf_so'
-  fi
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=yes
-  hardcode_into_libs=yes
-  ;;
-
-newsos6)
-  version_type=linux
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=yes
-  ;;
-
-*nto* | *qnx*)
-  version_type=qnx
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=no
-  hardcode_into_libs=yes
-  dynamic_linker='ldqnx.so'
-  ;;
-
-openbsd*)
-  version_type=sunos
-  sys_lib_dlsearch_path_spec="/usr/lib"
-  need_lib_prefix=no
-  # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
-  case $host_os in
-    openbsd3.3 | openbsd3.3.*) need_version=yes ;;
-    *)                         need_version=no  ;;
-  esac
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
-  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
-  shlibpath_var=LD_LIBRARY_PATH
-  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
-    case $host_os in
-      openbsd2.[[89]] | openbsd2.[[89]].*)
-       shlibpath_overrides_runpath=no
-       ;;
-      *)
-       shlibpath_overrides_runpath=yes
-       ;;
-      esac
-  else
-    shlibpath_overrides_runpath=yes
-  fi
-  ;;
-
-os2*)
-  libname_spec='$name'
-  shrext_cmds=".dll"
-  need_lib_prefix=no
-  library_names_spec='$libname${shared_ext} $libname.a'
-  dynamic_linker='OS/2 ld.exe'
-  shlibpath_var=LIBPATH
-  ;;
-
-osf3* | osf4* | osf5*)
-  version_type=osf
-  need_lib_prefix=no
-  need_version=no
-  soname_spec='${libname}${release}${shared_ext}$major'
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-  shlibpath_var=LD_LIBRARY_PATH
-  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
-  sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
-  ;;
-
-rdos*)
-  dynamic_linker=no
-  ;;
-
-solaris*)
-  version_type=linux
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=yes
-  hardcode_into_libs=yes
-  # ldd complains unless libraries are executable
-  postinstall_cmds='chmod +x $lib'
-  ;;
-
-sunos4*)
-  version_type=sunos
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
-  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=yes
-  if test "$with_gnu_ld" = yes; then
-    need_lib_prefix=no
-  fi
-  need_version=yes
-  ;;
-
-sysv4 | sysv4.3*)
-  version_type=linux
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  shlibpath_var=LD_LIBRARY_PATH
-  case $host_vendor in
-    sni)
-      shlibpath_overrides_runpath=no
-      need_lib_prefix=no
-      runpath_var=LD_RUN_PATH
-      ;;
-    siemens)
-      need_lib_prefix=no
-      ;;
-    motorola)
-      need_lib_prefix=no
-      need_version=no
-      shlibpath_overrides_runpath=no
-      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
-      ;;
-  esac
-  ;;
-
-sysv4*MP*)
-  if test -d /usr/nec ;then
-    version_type=linux
-    library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
-    soname_spec='$libname${shared_ext}.$major'
-    shlibpath_var=LD_LIBRARY_PATH
-  fi
-  ;;
-
-sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
-  version_type=freebsd-elf
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=yes
-  hardcode_into_libs=yes
-  if test "$with_gnu_ld" = yes; then
-    sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
-  else
-    sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
-    case $host_os in
-      sco3.2v5*)
-        sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
-       ;;
-    esac
-  fi
-  sys_lib_dlsearch_path_spec='/usr/lib'
-  ;;
-
-tpf*)
-  # TPF is a cross-target only.  Preferred cross-host = GNU/Linux.
-  version_type=linux
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=no
-  hardcode_into_libs=yes
-  ;;
-
-uts4*)
-  version_type=linux
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  shlibpath_var=LD_LIBRARY_PATH
-  ;;
-
-*)
-  dynamic_linker=no
-  ;;
-esac
-AC_MSG_RESULT([$dynamic_linker])
-test "$dynamic_linker" = no && can_build_shared=no
-
-variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
-if test "$GCC" = yes; then
-  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
-fi
-
-if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
-  sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
-fi
-if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
-  sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
-fi
-
-_LT_DECL([], [variables_saved_for_relink], [1],
-    [Variables whose values should be saved in libtool wrapper scripts and
-    restored at link time])
-_LT_DECL([], [need_lib_prefix], [0],
-    [Do we need the "lib" prefix for modules?])
-_LT_DECL([], [need_version], [0], [Do we need a version for libraries?])
-_LT_DECL([], [version_type], [0], [Library versioning type])
-_LT_DECL([], [runpath_var], [0],  [Shared library runtime path variable])
-_LT_DECL([], [shlibpath_var], [0],[Shared library path variable])
-_LT_DECL([], [shlibpath_overrides_runpath], [0],
-    [Is shlibpath searched before the hard-coded library search path?])
-_LT_DECL([], [libname_spec], [1], [Format of library name prefix])
-_LT_DECL([], [library_names_spec], [1],
-    [[List of archive names.  First name is the real one, the rest are links.
-    The last name is the one that the linker finds with -lNAME]])
-_LT_DECL([], [soname_spec], [1],
-    [[The coded name of the library, if different from the real name]])
-_LT_DECL([], [postinstall_cmds], [2],
-    [Command to use after installation of a shared archive])
-_LT_DECL([], [postuninstall_cmds], [2],
-    [Command to use after uninstallation of a shared archive])
-_LT_DECL([], [finish_cmds], [2],
-    [Commands used to finish a libtool library installation in a directory])
-_LT_DECL([], [finish_eval], [1],
-    [[As "finish_cmds", except a single script fragment to be evaled but
-    not shown]])
-_LT_DECL([], [hardcode_into_libs], [0],
-    [Whether we should hardcode library paths into libraries])
-_LT_DECL([], [sys_lib_search_path_spec], [2],
-    [Compile-time system search path for libraries])
-_LT_DECL([], [sys_lib_dlsearch_path_spec], [2],
-    [Run-time system search path for libraries])
-])# _LT_SYS_DYNAMIC_LINKER
-
-
-# _LT_PATH_TOOL_PREFIX(TOOL)
-# --------------------------
-# find a file program which can recognize shared library
-AC_DEFUN([_LT_PATH_TOOL_PREFIX],
-[m4_require([_LT_DECL_EGREP])dnl
-AC_MSG_CHECKING([for $1])
-AC_CACHE_VAL(lt_cv_path_MAGIC_CMD,
-[case $MAGIC_CMD in
-[[\\/*] |  ?:[\\/]*])
-  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
-  ;;
-*)
-  lt_save_MAGIC_CMD="$MAGIC_CMD"
-  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
-dnl $ac_dummy forces splitting on constant user-supplied paths.
-dnl POSIX.2 word splitting is done only on the output of word expansions,
-dnl not every word.  This closes a longstanding sh security hole.
-  ac_dummy="m4_if([$2], , $PATH, [$2])"
-  for ac_dir in $ac_dummy; do
-    IFS="$lt_save_ifs"
-    test -z "$ac_dir" && ac_dir=.
-    if test -f $ac_dir/$1; then
-      lt_cv_path_MAGIC_CMD="$ac_dir/$1"
-      if test -n "$file_magic_test_file"; then
-       case $deplibs_check_method in
-       "file_magic "*)
-         file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
-         MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
-         if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
-           $EGREP "$file_magic_regex" > /dev/null; then
-           :
-         else
-           cat <<_LT_EOF 1>&2
-
-*** Warning: the command libtool uses to detect shared libraries,
-*** $file_magic_cmd, produces output that libtool cannot recognize.
-*** The result is that libtool may fail to recognize shared libraries
-*** as such.  This will affect the creation of libtool libraries that
-*** depend on shared libraries, but programs linked with such libtool
-*** libraries will work regardless of this problem.  Nevertheless, you
-*** may want to report the problem to your system manager and/or to
-*** bug-libtool@gnu.org
-
-_LT_EOF
-         fi ;;
-       esac
-      fi
-      break
-    fi
-  done
-  IFS="$lt_save_ifs"
-  MAGIC_CMD="$lt_save_MAGIC_CMD"
-  ;;
-esac])
-MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
-if test -n "$MAGIC_CMD"; then
-  AC_MSG_RESULT($MAGIC_CMD)
-else
-  AC_MSG_RESULT(no)
-fi
-_LT_DECL([], [MAGIC_CMD], [0],
-        [Used to examine libraries when file_magic_cmd begins with "file"])dnl
-])# _LT_PATH_TOOL_PREFIX
-
-# Old name:
-AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX])
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], [])
-
-
-# _LT_PATH_MAGIC
-# --------------
-# find a file program which can recognize a shared library
-m4_defun([_LT_PATH_MAGIC],
-[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH)
-if test -z "$lt_cv_path_MAGIC_CMD"; then
-  if test -n "$ac_tool_prefix"; then
-    _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH)
-  else
-    MAGIC_CMD=:
-  fi
-fi
-])# _LT_PATH_MAGIC
-
-
-# LT_PATH_LD
-# ----------
-# find the pathname to the GNU or non-GNU linker
-AC_DEFUN([LT_PATH_LD],
-[AC_REQUIRE([AC_PROG_CC])dnl
-AC_REQUIRE([AC_CANONICAL_HOST])dnl
-AC_REQUIRE([AC_CANONICAL_BUILD])dnl
-m4_require([_LT_DECL_SED])dnl
-m4_require([_LT_DECL_EGREP])dnl
-
-AC_ARG_WITH([gnu-ld],
-    [AS_HELP_STRING([--with-gnu-ld],
-       [assume the C compiler uses GNU ld @<:@default=no@:>@])],
-    [test "$withval" = no || with_gnu_ld=yes],
-    [with_gnu_ld=no])dnl
-
-ac_prog=ld
-if test "$GCC" = yes; then
-  # Check if gcc -print-prog-name=ld gives a path.
-  AC_MSG_CHECKING([for ld used by $CC])
-  case $host in
-  *-*-mingw*)
-    # gcc leaves a trailing carriage return which upsets mingw
-    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
-  *)
-    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
-  esac
-  case $ac_prog in
-    # Accept absolute paths.
-    [[\\/]]* | ?:[[\\/]]*)
-      re_direlt='/[[^/]][[^/]]*/\.\./'
-      # Canonicalize the pathname of ld
-      ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
-      while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
-       ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
-      done
-      test -z "$LD" && LD="$ac_prog"
-      ;;
-  "")
-    # If it fails, then pretend we aren't using GCC.
-    ac_prog=ld
-    ;;
-  *)
-    # If it is relative, then search for the first ld in PATH.
-    with_gnu_ld=unknown
-    ;;
-  esac
-elif test "$with_gnu_ld" = yes; then
-  AC_MSG_CHECKING([for GNU ld])
-else
-  AC_MSG_CHECKING([for non-GNU ld])
-fi
-AC_CACHE_VAL(lt_cv_path_LD,
-[if test -z "$LD"; then
-  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
-  for ac_dir in $PATH; do
-    IFS="$lt_save_ifs"
-    test -z "$ac_dir" && ac_dir=.
-    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
-      lt_cv_path_LD="$ac_dir/$ac_prog"
-      # Check to see if the program is GNU ld.  I'd rather use --version,
-      # but apparently some variants of GNU ld only accept -v.
-      # Break only if it was the GNU/non-GNU ld that we prefer.
-      case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
-      *GNU* | *'with BFD'*)
-       test "$with_gnu_ld" != no && break
-       ;;
-      *)
-       test "$with_gnu_ld" != yes && break
-       ;;
-      esac
-    fi
-  done
-  IFS="$lt_save_ifs"
-else
-  lt_cv_path_LD="$LD" # Let the user override the test with a path.
-fi])
-LD="$lt_cv_path_LD"
-if test -n "$LD"; then
-  AC_MSG_RESULT($LD)
-else
-  AC_MSG_RESULT(no)
-fi
-test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
-_LT_PATH_LD_GNU
-AC_SUBST([LD])
-
-_LT_TAGDECL([], [LD], [1], [The linker used to build libraries])
-])# LT_PATH_LD
-
-# Old names:
-AU_ALIAS([AM_PROG_LD], [LT_PATH_LD])
-AU_ALIAS([AC_PROG_LD], [LT_PATH_LD])
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([AM_PROG_LD], [])
-dnl AC_DEFUN([AC_PROG_LD], [])
-
-
-# _LT_PATH_LD_GNU
-#- --------------
-m4_defun([_LT_PATH_LD_GNU],
-[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], lt_cv_prog_gnu_ld,
-[# I'd rather use --version here, but apparently some GNU lds only accept -v.
-case `$LD -v 2>&1 </dev/null` in
-*GNU* | *'with BFD'*)
-  lt_cv_prog_gnu_ld=yes
-  ;;
-*)
-  lt_cv_prog_gnu_ld=no
-  ;;
-esac])
-with_gnu_ld=$lt_cv_prog_gnu_ld
-])# _LT_PATH_LD_GNU
-
-
-# _LT_CMD_RELOAD
-# --------------
-# find reload flag for linker
-#   -- PORTME Some linkers may need a different reload flag.
-m4_defun([_LT_CMD_RELOAD],
-[AC_CACHE_CHECK([for $LD option to reload object files],
-  lt_cv_ld_reload_flag,
-  [lt_cv_ld_reload_flag='-r'])
-reload_flag=$lt_cv_ld_reload_flag
-case $reload_flag in
-"" | " "*) ;;
-*) reload_flag=" $reload_flag" ;;
-esac
-reload_cmds='$LD$reload_flag -o $output$reload_objs'
-case $host_os in
-  darwin*)
-    if test "$GCC" = yes; then
-      reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
-    else
-      reload_cmds='$LD$reload_flag -o $output$reload_objs'
-    fi
-    ;;
-esac
-_LT_DECL([], [reload_flag], [1], [How to create reloadable object files])dnl
-_LT_DECL([], [reload_cmds], [2])dnl
-])# _LT_CMD_RELOAD
-
-
-# _LT_CHECK_MAGIC_METHOD
-# ----------------------
-# how to check for library dependencies
-#  -- PORTME fill in with the dynamic library characteristics
-m4_defun([_LT_CHECK_MAGIC_METHOD],
-[m4_require([_LT_DECL_EGREP])
-m4_require([_LT_DECL_OBJDUMP])
-AC_CACHE_CHECK([how to recognize dependent libraries],
-lt_cv_deplibs_check_method,
-[lt_cv_file_magic_cmd='$MAGIC_CMD'
-lt_cv_file_magic_test_file=
-lt_cv_deplibs_check_method='unknown'
-# Need to set the preceding variable on all platforms that support
-# interlibrary dependencies.
-# 'none' -- dependencies not supported.
-# `unknown' -- same as none, but documents that we really don't know.
-# 'pass_all' -- all dependencies passed with no checks.
-# 'test_compile' -- check by making test program.
-# 'file_magic [[regex]]' -- check by looking for files in library path
-# which responds to the $file_magic_cmd with a given extended regex.
-# If you have `file' or equivalent on your system and you're not sure
-# whether `pass_all' will *always* work, you probably want this one.
-
-case $host_os in
-aix[[4-9]]*)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-beos*)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-bsdi[[45]]*)
-  lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)'
-  lt_cv_file_magic_cmd='/usr/bin/file -L'
-  lt_cv_file_magic_test_file=/shlib/libc.so
-  ;;
-
-cygwin*)
-  # func_win32_libid is a shell function defined in ltmain.sh
-  lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
-  lt_cv_file_magic_cmd='func_win32_libid'
-  ;;
-
-mingw* | pw32*)
-  # Base MSYS/MinGW do not provide the 'file' command needed by
-  # func_win32_libid shell function, so use a weaker test based on 'objdump',
-  # unless we find 'file', for example because we are cross-compiling.
-  if ( file / ) >/dev/null 2>&1; then
-    lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
-    lt_cv_file_magic_cmd='func_win32_libid'
-  else
-    lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?'
-    lt_cv_file_magic_cmd='$OBJDUMP -f'
-  fi
-  ;;
-
-cegcc)
-  # use the weaker test based on 'objdump'. See mingw*.
-  lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
-  lt_cv_file_magic_cmd='$OBJDUMP -f'
-  ;;
-
-darwin* | rhapsody*)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-freebsd* | dragonfly*)
-  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
-    case $host_cpu in
-    i*86 )
-      # Not sure whether the presence of OpenBSD here was a mistake.
-      # Let's accept both of them until this is cleared up.
-      lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library'
-      lt_cv_file_magic_cmd=/usr/bin/file
-      lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
-      ;;
-    esac
-  else
-    lt_cv_deplibs_check_method=pass_all
-  fi
-  ;;
-
-gnu*)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-hpux10.20* | hpux11*)
-  lt_cv_file_magic_cmd=/usr/bin/file
-  case $host_cpu in
-  ia64*)
-    lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64'
-    lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
-    ;;
-  hppa*64*)
-    [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]']
-    lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
-    ;;
-  *)
-    lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]].[[0-9]]) shared library'
-    lt_cv_file_magic_test_file=/usr/lib/libc.sl
-    ;;
-  esac
-  ;;
-
-interix[[3-9]]*)
-  # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
-  lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$'
-  ;;
-
-irix5* | irix6* | nonstopux*)
-  case $LD in
-  *-32|*"-32 ") libmagic=32-bit;;
-  *-n32|*"-n32 ") libmagic=N32;;
-  *-64|*"-64 ") libmagic=64-bit;;
-  *) libmagic=never-match;;
-  esac
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-# This must be Linux ELF.
-linux* | k*bsd*-gnu | kopensolaris*-gnu)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-netbsd* | netbsdelf*-gnu)
-  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
-    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
-  else
-    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$'
-  fi
-  ;;
-
-newos6*)
-  lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)'
-  lt_cv_file_magic_cmd=/usr/bin/file
-  lt_cv_file_magic_test_file=/usr/lib/libnls.so
-  ;;
-
-*nto* | *qnx*)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-openbsd*)
-  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
-    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$'
-  else
-    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
-  fi
-  ;;
-
-osf3* | osf4* | osf5*)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-rdos*)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-solaris*)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-sysv4 | sysv4.3*)
-  case $host_vendor in
-  motorola)
-    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]'
-    lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
-    ;;
-  ncr)
-    lt_cv_deplibs_check_method=pass_all
-    ;;
-  sequent)
-    lt_cv_file_magic_cmd='/bin/file'
-    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )'
-    ;;
-  sni)
-    lt_cv_file_magic_cmd='/bin/file'
-    lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib"
-    lt_cv_file_magic_test_file=/lib/libc.so
-    ;;
-  siemens)
-    lt_cv_deplibs_check_method=pass_all
-    ;;
-  pc)
-    lt_cv_deplibs_check_method=pass_all
-    ;;
-  esac
-  ;;
-
-tpf*)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-esac
-])
-file_magic_cmd=$lt_cv_file_magic_cmd
-deplibs_check_method=$lt_cv_deplibs_check_method
-test -z "$deplibs_check_method" && deplibs_check_method=unknown
-
-_LT_DECL([], [deplibs_check_method], [1],
-    [Method to check whether dependent libraries are shared objects])
-_LT_DECL([], [file_magic_cmd], [1],
-    [Command to use when deplibs_check_method == "file_magic"])
-])# _LT_CHECK_MAGIC_METHOD
-
-
-# LT_PATH_NM
-# ----------
-# find the pathname to a BSD- or MS-compatible name lister
-AC_DEFUN([LT_PATH_NM],
-[AC_REQUIRE([AC_PROG_CC])dnl
-AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM,
-[if test -n "$NM"; then
-  # Let the user override the test.
-  lt_cv_path_NM="$NM"
-else
-  lt_nm_to_check="${ac_tool_prefix}nm"
-  if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
-    lt_nm_to_check="$lt_nm_to_check nm"
-  fi
-  for lt_tmp_nm in $lt_nm_to_check; do
-    lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
-    for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
-      IFS="$lt_save_ifs"
-      test -z "$ac_dir" && ac_dir=.
-      tmp_nm="$ac_dir/$lt_tmp_nm"
-      if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
-       # Check to see if the nm accepts a BSD-compat flag.
-       # Adding the `sed 1q' prevents false positives on HP-UX, which says:
-       #   nm: unknown option "B" ignored
-       # Tru64's nm complains that /dev/null is an invalid object file
-       case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
-       */dev/null* | *'Invalid file or object type'*)
-         lt_cv_path_NM="$tmp_nm -B"
-         break
-         ;;
-       *)
-         case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
-         */dev/null*)
-           lt_cv_path_NM="$tmp_nm -p"
-           break
-           ;;
-         *)
-           lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
-           continue # so that we can try to find one that supports BSD flags
-           ;;
-         esac
-         ;;
-       esac
-      fi
-    done
-    IFS="$lt_save_ifs"
-  done
-  : ${lt_cv_path_NM=no}
-fi])
-if test "$lt_cv_path_NM" != "no"; then
-  NM="$lt_cv_path_NM"
-else
-  # Didn't find any BSD compatible name lister, look for dumpbin.
-  AC_CHECK_TOOLS(DUMPBIN, ["dumpbin -symbols" "link -dump -symbols"], :)
-  AC_SUBST([DUMPBIN])
-  if test "$DUMPBIN" != ":"; then
-    NM="$DUMPBIN"
-  fi
-fi
-test -z "$NM" && NM=nm
-AC_SUBST([NM])
-_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl
-
-AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface],
-  [lt_cv_nm_interface="BSD nm"
-  echo "int some_variable = 0;" > conftest.$ac_ext
-  (eval echo "\"\$as_me:__oline__: $ac_compile\"" >&AS_MESSAGE_LOG_FD)
-  (eval "$ac_compile" 2>conftest.err)
-  cat conftest.err >&AS_MESSAGE_LOG_FD
-  (eval echo "\"\$as_me:__oline__: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD)
-  (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
-  cat conftest.err >&AS_MESSAGE_LOG_FD
-  (eval echo "\"\$as_me:__oline__: output\"" >&AS_MESSAGE_LOG_FD)
-  cat conftest.out >&AS_MESSAGE_LOG_FD
-  if $GREP 'External.*some_variable' conftest.out > /dev/null; then
-    lt_cv_nm_interface="MS dumpbin"
-  fi
-  rm -f conftest*])
-])# LT_PATH_NM
-
-# Old names:
-AU_ALIAS([AM_PROG_NM], [LT_PATH_NM])
-AU_ALIAS([AC_PROG_NM], [LT_PATH_NM])
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([AM_PROG_NM], [])
-dnl AC_DEFUN([AC_PROG_NM], [])
-
-
-# LT_LIB_M
-# --------
-# check for math library
-AC_DEFUN([LT_LIB_M],
-[AC_REQUIRE([AC_CANONICAL_HOST])dnl
-LIBM=
-case $host in
-*-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*)
-  # These system don't have libm, or don't need it
-  ;;
-*-ncr-sysv4.3*)
-  AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw")
-  AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm")
-  ;;
-*)
-  AC_CHECK_LIB(m, cos, LIBM="-lm")
-  ;;
-esac
-AC_SUBST([LIBM])
-])# LT_LIB_M
-
-# Old name:
-AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M])
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([AC_CHECK_LIBM], [])
-
-
-# _LT_COMPILER_NO_RTTI([TAGNAME])
-# -------------------------------
-m4_defun([_LT_COMPILER_NO_RTTI],
-[m4_require([_LT_TAG_COMPILER])dnl
-
-_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
-
-if test "$GCC" = yes; then
-  _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
-
-  _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions],
-    lt_cv_prog_compiler_rtti_exceptions,
-    [-fno-rtti -fno-exceptions], [],
-    [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"])
-fi
-_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1],
-       [Compiler flag to turn off builtin functions])
-])# _LT_COMPILER_NO_RTTI
-
-
-# _LT_CMD_GLOBAL_SYMBOLS
-# ----------------------
-m4_defun([_LT_CMD_GLOBAL_SYMBOLS],
-[AC_REQUIRE([AC_CANONICAL_HOST])dnl
-AC_REQUIRE([AC_PROG_CC])dnl
-AC_REQUIRE([LT_PATH_NM])dnl
-AC_REQUIRE([LT_PATH_LD])dnl
-m4_require([_LT_DECL_SED])dnl
-m4_require([_LT_DECL_EGREP])dnl
-m4_require([_LT_TAG_COMPILER])dnl
-
-# Check for command to grab the raw symbol name followed by C symbol from nm.
-AC_MSG_CHECKING([command to parse $NM output from $compiler object])
-AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe],
-[
-# These are sane defaults that work on at least a few old systems.
-# [They come from Ultrix.  What could be older than Ultrix?!! ;)]
-
-# Character class describing NM global symbol codes.
-symcode='[[BCDEGRST]]'
-
-# Regexp to match symbols that can be accessed directly from C.
-sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)'
-
-# Define system-specific variables.
-case $host_os in
-aix*)
-  symcode='[[BCDT]]'
-  ;;
-cygwin* | mingw* | pw32* | cegcc*)
-  symcode='[[ABCDGISTW]]'
-  ;;
-hpux*)
-  if test "$host_cpu" = ia64; then
-    symcode='[[ABCDEGRST]]'
-  fi
-  ;;
-irix* | nonstopux*)
-  symcode='[[BCDEGRST]]'
-  ;;
-osf*)
-  symcode='[[BCDEGQRST]]'
-  ;;
-solaris*)
-  symcode='[[BDRT]]'
-  ;;
-sco3.2v5*)
-  symcode='[[DT]]'
-  ;;
-sysv4.2uw2*)
-  symcode='[[DT]]'
-  ;;
-sysv5* | sco5v6* | unixware* | OpenUNIX*)
-  symcode='[[ABDT]]'
-  ;;
-sysv4)
-  symcode='[[DFNSTU]]'
-  ;;
-esac
-
-# If we're using GNU nm, then use its standard symbol codes.
-case `$NM -V 2>&1` in
-*GNU* | *'with BFD'*)
-  symcode='[[ABCDGIRSTW]]' ;;
-esac
-
-# Transform an extracted symbol line into a proper C declaration.
-# Some systems (esp. on ia64) link data and code symbols differently,
-# so use this general approach.
-lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
-
-# Transform an extracted symbol line into symbol name and symbol address
-lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/  {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/  {\"\2\", (void *) \&\2},/p'"
-lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\) $/  {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/  {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/  {\"lib\2\", (void *) \&\2},/p'"
-
-# Handle CRLF in mingw tool chain
-opt_cr=
-case $build_os in
-mingw*)
-  opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
-  ;;
-esac
-
-# Try without a prefix underscore, then with it.
-for ac_symprfx in "" "_"; do
-
-  # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
-  symxfrm="\\1 $ac_symprfx\\2 \\2"
-
-  # Write the raw and C identifiers.
-  if test "$lt_cv_nm_interface" = "MS dumpbin"; then
-    # Fake it for dumpbin and say T for any non-static function
-    # and D for any global variable.
-    # Also find C++ and __fastcall symbols from MSVC++,
-    # which start with @ or ?.
-    lt_cv_sys_global_symbol_pipe="$AWK ['"\
-"     {last_section=section; section=\$ 3};"\
-"     /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
-"     \$ 0!~/External *\|/{next};"\
-"     / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
-"     {if(hide[section]) next};"\
-"     {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
-"     {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
-"     s[1]~/^[@?]/{print s[1], s[1]; next};"\
-"     s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
-"     ' prfx=^$ac_symprfx]"
-  else
-    lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[    ]]\($symcode$symcode*\)[[       ]][[    ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
-  fi
-
-  # Check to see that the pipe works correctly.
-  pipe_works=no
-
-  rm -f conftest*
-  cat > conftest.$ac_ext <<_LT_EOF
-#ifdef __cplusplus
-extern "C" {
-#endif
-char nm_test_var;
-void nm_test_func(void);
-void nm_test_func(void){}
-#ifdef __cplusplus
-}
-#endif
-int main(){nm_test_var='a';nm_test_func();return(0);}
-_LT_EOF
-
-  if AC_TRY_EVAL(ac_compile); then
-    # Now try to grab the symbols.
-    nlist=conftest.nm
-    if AC_TRY_EVAL(NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) && test -s "$nlist"; then
-      # Try sorting and uniquifying the output.
-      if sort "$nlist" | uniq > "$nlist"T; then
-       mv -f "$nlist"T "$nlist"
-      else
-       rm -f "$nlist"T
-      fi
-
-      # Make sure that we snagged all the symbols we need.
-      if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
-       if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
-         cat <<_LT_EOF > conftest.$ac_ext
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-_LT_EOF
-         # Now generate the symbol file.
-         eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
-
-         cat <<_LT_EOF >> conftest.$ac_ext
-
-/* The mapping between symbol names and symbols.  */
-const struct {
-  const char *name;
-  void       *address;
-}
-lt__PROGRAM__LTX_preloaded_symbols[[]] =
-{
-  { "@PROGRAM@", (void *) 0 },
-_LT_EOF
-         $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/  {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
-         cat <<\_LT_EOF >> conftest.$ac_ext
-  {0, (void *) 0}
-};
-
-/* This works around a problem in FreeBSD linker */
-#ifdef FREEBSD_WORKAROUND
-static const void *lt_preloaded_setup() {
-  return lt__PROGRAM__LTX_preloaded_symbols;
-}
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-_LT_EOF
-         # Now try linking the two files.
-         mv conftest.$ac_objext conftstm.$ac_objext
-         lt_save_LIBS="$LIBS"
-         lt_save_CFLAGS="$CFLAGS"
-         LIBS="conftstm.$ac_objext"
-         CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)"
-         if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then
-           pipe_works=yes
-         fi
-         LIBS="$lt_save_LIBS"
-         CFLAGS="$lt_save_CFLAGS"
-       else
-         echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD
-       fi
-      else
-       echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD
-      fi
-    else
-      echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD
-    fi
-  else
-    echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD
-    cat conftest.$ac_ext >&5
-  fi
-  rm -rf conftest* conftst*
-
-  # Do not use the global_symbol_pipe unless it works.
-  if test "$pipe_works" = yes; then
-    break
-  else
-    lt_cv_sys_global_symbol_pipe=
-  fi
-done
-])
-if test -z "$lt_cv_sys_global_symbol_pipe"; then
-  lt_cv_sys_global_symbol_to_cdecl=
-fi
-if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
-  AC_MSG_RESULT(failed)
-else
-  AC_MSG_RESULT(ok)
-fi
-
-_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1],
-    [Take the output of nm and produce a listing of raw symbols and C names])
-_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1],
-    [Transform the output of nm in a proper C declaration])
-_LT_DECL([global_symbol_to_c_name_address],
-    [lt_cv_sys_global_symbol_to_c_name_address], [1],
-    [Transform the output of nm in a C name address pair])
-_LT_DECL([global_symbol_to_c_name_address_lib_prefix],
-    [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1],
-    [Transform the output of nm in a C name address pair when lib prefix is needed])
-]) # _LT_CMD_GLOBAL_SYMBOLS
-
-
-# _LT_COMPILER_PIC([TAGNAME])
-# ---------------------------
-m4_defun([_LT_COMPILER_PIC],
-[m4_require([_LT_TAG_COMPILER])dnl
-_LT_TAGVAR(lt_prog_compiler_wl, $1)=
-_LT_TAGVAR(lt_prog_compiler_pic, $1)=
-_LT_TAGVAR(lt_prog_compiler_static, $1)=
-
-AC_MSG_CHECKING([for $compiler option to produce PIC])
-m4_if([$1], [CXX], [
-  # C++ specific cases for pic, static, wl, etc.
-  if test "$GXX" = yes; then
-    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-    _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
-
-    case $host_os in
-    aix*)
-      # All AIX code is PIC.
-      if test "$host_cpu" = ia64; then
-       # AIX 5 now supports IA64 processor
-       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-      fi
-      ;;
-
-    amigaos*)
-      case $host_cpu in
-      powerpc)
-            # see comment about AmigaOS4 .so support
-            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
-        ;;
-      m68k)
-            # FIXME: we need at least 68020 code to build shared libraries, but
-            # adding the `-m68020' flag to GCC prevents building anything better,
-            # like `-m68040'.
-            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
-        ;;
-      esac
-      ;;
-
-    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
-      # PIC is the default for these OSes.
-      ;;
-    mingw* | cygwin* | os2* | pw32* | cegcc*)
-      # This hack is so that the source file can tell whether it is being
-      # built for inclusion in a dll (and should export symbols for example).
-      # Although the cygwin gcc ignores -fPIC, still need this for old-style
-      # (--disable-auto-import) libraries
-      m4_if([$1], [GCJ], [],
-       [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
-      ;;
-    darwin* | rhapsody*)
-      # PIC is the default on this platform
-      # Common symbols not allowed in MH_DYLIB files
-      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
-      ;;
-    *djgpp*)
-      # DJGPP does not support shared libraries at all
-      _LT_TAGVAR(lt_prog_compiler_pic, $1)=
-      ;;
-    interix[[3-9]]*)
-      # Interix 3.x gcc -fpic/-fPIC options generate broken code.
-      # Instead, we relocate shared libraries at runtime.
-      ;;
-    sysv4*MP*)
-      if test -d /usr/nec; then
-       _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
-      fi
-      ;;
-    hpux*)
-      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
-      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag
-      # sets the default TLS model and affects inlining.
-      case $host_cpu in
-      hppa*64*)
-       ;;
-      *)
-       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
-       ;;
-      esac
-      ;;
-    *qnx* | *nto*)
-      # QNX uses GNU C++, but need to define -shared option too, otherwise
-      # it will coredump.
-      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
-      ;;
-    *)
-      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
-      ;;
-    esac
-  else
-    case $host_os in
-      aix[[4-9]]*)
-       # All AIX code is PIC.
-       if test "$host_cpu" = ia64; then
-         # AIX 5 now supports IA64 processor
-         _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-       else
-         _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
-       fi
-       ;;
-      chorus*)
-       case $cc_basename in
-       cxch68*)
-         # Green Hills C++ Compiler
-         # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a"
-         ;;
-       esac
-       ;;
-      dgux*)
-       case $cc_basename in
-         ec++*)
-           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-           ;;
-         ghcx*)
-           # Green Hills C++ Compiler
-           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
-           ;;
-         *)
-           ;;
-       esac
-       ;;
-      freebsd* | dragonfly*)
-       # FreeBSD uses GNU C++
-       ;;
-      hpux9* | hpux10* | hpux11*)
-       case $cc_basename in
-         CC*)
-           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-           _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
-           if test "$host_cpu" != ia64; then
-             _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
-           fi
-           ;;
-         aCC*)
-           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-           _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
-           case $host_cpu in
-           hppa*64*|ia64*)
-             # +Z the default
-             ;;
-           *)
-             _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
-             ;;
-           esac
-           ;;
-         *)
-           ;;
-       esac
-       ;;
-      interix*)
-       # This is c89, which is MS Visual C++ (no shared libs)
-       # Anyone wants to do a port?
-       ;;
-      irix5* | irix6* | nonstopux*)
-       case $cc_basename in
-         CC*)
-           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-           _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
-           # CC pic flag -KPIC is the default.
-           ;;
-         *)
-           ;;
-       esac
-       ;;
-      linux* | k*bsd*-gnu | kopensolaris*-gnu)
-       case $cc_basename in
-         KCC*)
-           # KAI C++ Compiler
-           _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
-           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
-           ;;
-         ecpc* )
-           # old Intel C++ for x86_64 which still supported -KPIC.
-           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-           _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
-           ;;
-         icpc* )
-           # Intel C++, used to be incompatible with GCC.
-           # ICC 10 doesn't accept -KPIC any more.
-           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
-           _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
-           ;;
-         pgCC* | pgcpp*)
-           # Portland Group C++ compiler
-           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
-           _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-           ;;
-         cxx*)
-           # Compaq C++
-           # Make sure the PIC flag is empty.  It appears that all Alpha
-           # Linux and Compaq Tru64 Unix objects are PIC.
-           _LT_TAGVAR(lt_prog_compiler_pic, $1)=
-           _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
-           ;;
-         xlc* | xlC*)
-           # IBM XL 8.0 on PPC
-           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
-           _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
-           ;;
-         *)
-           case `$CC -V 2>&1 | sed 5q` in
-           *Sun\ C*)
-             # Sun C++ 5.9
-             _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-             _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-             _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
-             ;;
-           esac
-           ;;
-       esac
-       ;;
-      lynxos*)
-       ;;
-      m88k*)
-       ;;
-      mvs*)
-       case $cc_basename in
-         cxx*)
-           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall'
-           ;;
-         *)
-           ;;
-       esac
-       ;;
-      netbsd* | netbsdelf*-gnu)
-       ;;
-      *qnx* | *nto*)
-        # QNX uses GNU C++, but need to define -shared option too, otherwise
-        # it will coredump.
-        _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
-        ;;
-      osf3* | osf4* | osf5*)
-       case $cc_basename in
-         KCC*)
-           _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
-           ;;
-         RCC*)
-           # Rational C++ 2.4.1
-           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
-           ;;
-         cxx*)
-           # Digital/Compaq C++
-           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-           # Make sure the PIC flag is empty.  It appears that all Alpha
-           # Linux and Compaq Tru64 Unix objects are PIC.
-           _LT_TAGVAR(lt_prog_compiler_pic, $1)=
-           _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
-           ;;
-         *)
-           ;;
-       esac
-       ;;
-      psos*)
-       ;;
-      solaris*)
-       case $cc_basename in
-         CC*)
-           # Sun C++ 4.2, 5.x and Centerline C++
-           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-           _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
-           ;;
-         gcx*)
-           # Green Hills C++ Compiler
-           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
-           ;;
-         *)
-           ;;
-       esac
-       ;;
-      sunos4*)
-       case $cc_basename in
-         CC*)
-           # Sun C++ 4.x
-           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
-           _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-           ;;
-         lcc*)
-           # Lucid
-           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
-           ;;
-         *)
-           ;;
-       esac
-       ;;
-      sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
-       case $cc_basename in
-         CC*)
-           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-           _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-           ;;
-       esac
-       ;;
-      tandem*)
-       case $cc_basename in
-         NCC*)
-           # NonStop-UX NCC 3.20
-           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-           ;;
-         *)
-           ;;
-       esac
-       ;;
-      vxworks*)
-       ;;
-      *)
-       _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
-       ;;
-    esac
-  fi
-],
-[
-  if test "$GCC" = yes; then
-    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-    _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
-
-    case $host_os in
-      aix*)
-      # All AIX code is PIC.
-      if test "$host_cpu" = ia64; then
-       # AIX 5 now supports IA64 processor
-       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-      fi
-      ;;
-
-    amigaos*)
-      case $host_cpu in
-      powerpc)
-            # see comment about AmigaOS4 .so support
-            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
-        ;;
-      m68k)
-            # FIXME: we need at least 68020 code to build shared libraries, but
-            # adding the `-m68020' flag to GCC prevents building anything better,
-            # like `-m68040'.
-            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
-        ;;
-      esac
-      ;;
-
-    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
-      # PIC is the default for these OSes.
-      ;;
-
-    mingw* | cygwin* | pw32* | os2* | cegcc*)
-      # This hack is so that the source file can tell whether it is being
-      # built for inclusion in a dll (and should export symbols for example).
-      # Although the cygwin gcc ignores -fPIC, still need this for old-style
-      # (--disable-auto-import) libraries
-      m4_if([$1], [GCJ], [],
-       [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
-      ;;
-
-    darwin* | rhapsody*)
-      # PIC is the default on this platform
-      # Common symbols not allowed in MH_DYLIB files
-      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
-      ;;
-
-    hpux*)
-      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
-      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag
-      # sets the default TLS model and affects inlining.
-      case $host_cpu in
-      hppa*64*)
-       # +Z the default
-       ;;
-      *)
-       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
-       ;;
-      esac
-      ;;
-
-    interix[[3-9]]*)
-      # Interix 3.x gcc -fpic/-fPIC options generate broken code.
-      # Instead, we relocate shared libraries at runtime.
-      ;;
-
-    msdosdjgpp*)
-      # Just because we use GCC doesn't mean we suddenly get shared libraries
-      # on systems that don't support them.
-      _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
-      enable_shared=no
-      ;;
-
-    *nto* | *qnx*)
-      # QNX uses GNU C++, but need to define -shared option too, otherwise
-      # it will coredump.
-      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
-      ;;
-
-    sysv4*MP*)
-      if test -d /usr/nec; then
-       _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
-      fi
-      ;;
-
-    *)
-      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
-      ;;
-    esac
-  else
-    # PORTME Check for flag to pass linker flags through the system compiler.
-    case $host_os in
-    aix*)
-      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-      if test "$host_cpu" = ia64; then
-       # AIX 5 now supports IA64 processor
-       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-      else
-       _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
-      fi
-      ;;
-
-    mingw* | cygwin* | pw32* | os2* | cegcc*)
-      # This hack is so that the source file can tell whether it is being
-      # built for inclusion in a dll (and should export symbols for example).
-      m4_if([$1], [GCJ], [],
-       [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
-      ;;
-
-    hpux9* | hpux10* | hpux11*)
-      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
-      # not for PA HP-UX.
-      case $host_cpu in
-      hppa*64*|ia64*)
-       # +Z the default
-       ;;
-      *)
-       _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
-       ;;
-      esac
-      # Is there a better lt_prog_compiler_static that works with the bundled CC?
-      _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
-      ;;
-
-    irix5* | irix6* | nonstopux*)
-      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-      # PIC (with -KPIC) is the default.
-      _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
-      ;;
-
-    linux* | k*bsd*-gnu | kopensolaris*-gnu)
-      case $cc_basename in
-      # old Intel for x86_64 which still supported -KPIC.
-      ecc*)
-       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-       _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
-        ;;
-      # icc used to be incompatible with GCC.
-      # ICC 10 doesn't accept -KPIC any more.
-      icc* | ifort*)
-       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
-       _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
-        ;;
-      # Lahey Fortran 8.1.
-      lf95*)
-       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-       _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared'
-       _LT_TAGVAR(lt_prog_compiler_static, $1)='--static'
-       ;;
-      pgcc* | pgf77* | pgf90* | pgf95*)
-        # Portland Group compilers (*not* the Pentium gcc compiler,
-       # which looks to be a dead project)
-       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
-       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-        ;;
-      ccc*)
-        _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-        # All Alpha code is PIC.
-        _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
-        ;;
-      xl*)
-       # IBM XL C 8.0/Fortran 10.1 on PPC
-       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
-       _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
-       ;;
-      *)
-       case `$CC -V 2>&1 | sed 5q` in
-       *Sun\ C*)
-         # Sun C 5.9
-         _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-         _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-         _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-         ;;
-       *Sun\ F*)
-         # Sun Fortran 8.3 passes all unrecognized flags to the linker
-         _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-         _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-         _LT_TAGVAR(lt_prog_compiler_wl, $1)=''
-         ;;
-       esac
-       ;;
-      esac
-      ;;
-
-    newsos6)
-      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-      ;;
-
-    *nto* | *qnx*)
-      # QNX uses GNU C++, but need to define -shared option too, otherwise
-      # it will coredump.
-      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
-      ;;
-
-    osf3* | osf4* | osf5*)
-      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-      # All OSF/1 code is PIC.
-      _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
-      ;;
-
-    rdos*)
-      _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
-      ;;
-
-    solaris*)
-      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-      case $cc_basename in
-      f77* | f90* | f95*)
-       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';;
-      *)
-       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';;
-      esac
-      ;;
-
-    sunos4*)
-      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
-      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
-      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-      ;;
-
-    sysv4 | sysv4.2uw2* | sysv4.3*)
-      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-      ;;
-
-    sysv4*MP*)
-      if test -d /usr/nec ;then
-       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic'
-       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-      fi
-      ;;
-
-    sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
-      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-      ;;
-
-    unicos*)
-      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-      _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
-      ;;
-
-    uts4*)
-      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
-      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-      ;;
-
-    *)
-      _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
-      ;;
-    esac
-  fi
-])
-case $host_os in
-  # For platforms which do not support PIC, -DPIC is meaningless:
-  *djgpp*)
-    _LT_TAGVAR(lt_prog_compiler_pic, $1)=
-    ;;
-  *)
-    _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])"
-    ;;
-esac
-AC_MSG_RESULT([$_LT_TAGVAR(lt_prog_compiler_pic, $1)])
-_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1],
-       [How to pass a linker flag through the compiler])
-
-#
-# Check to make sure the PIC flag actually works.
-#
-if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
-  _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works],
-    [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)],
-    [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [],
-    [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in
-     "" | " "*) ;;
-     *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;;
-     esac],
-    [_LT_TAGVAR(lt_prog_compiler_pic, $1)=
-     _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no])
-fi
-_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1],
-       [Additional compiler flags for building library objects])
-
-#
-# Check to make sure the static flag actually works.
-#
-wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\"
-_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works],
-  _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1),
-  $lt_tmp_static_flag,
-  [],
-  [_LT_TAGVAR(lt_prog_compiler_static, $1)=])
-_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1],
-       [Compiler flag to prevent dynamic linking])
-])# _LT_COMPILER_PIC
-
-
-# _LT_LINKER_SHLIBS([TAGNAME])
-# ----------------------------
-# See if the linker supports building shared libraries.
-m4_defun([_LT_LINKER_SHLIBS],
-[AC_REQUIRE([LT_PATH_LD])dnl
-AC_REQUIRE([LT_PATH_NM])dnl
-m4_require([_LT_FILEUTILS_DEFAULTS])dnl
-m4_require([_LT_DECL_EGREP])dnl
-m4_require([_LT_DECL_SED])dnl
-m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
-m4_require([_LT_TAG_COMPILER])dnl
-AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
-m4_if([$1], [CXX], [
-  _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
-  case $host_os in
-  aix[[4-9]]*)
-    # If we're using GNU nm, then we don't want the "-C" option.
-    # -C means demangle to AIX nm, but means don't demangle with GNU nm
-    if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
-      _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
-    else
-      _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
-    fi
-    ;;
-  pw32*)
-    _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds"
-  ;;
-  cygwin* | mingw* | cegcc*)
-    _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;/^.*[[ ]]__nm__/s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
-  ;;
-  linux* | k*bsd*-gnu)
-    _LT_TAGVAR(link_all_deplibs, $1)=no
-  ;;
-  *)
-    _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
-  ;;
-  esac
-  _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
-], [
-  runpath_var=
-  _LT_TAGVAR(allow_undefined_flag, $1)=
-  _LT_TAGVAR(always_export_symbols, $1)=no
-  _LT_TAGVAR(archive_cmds, $1)=
-  _LT_TAGVAR(archive_expsym_cmds, $1)=
-  _LT_TAGVAR(compiler_needs_object, $1)=no
-  _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
-  _LT_TAGVAR(export_dynamic_flag_spec, $1)=
-  _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
-  _LT_TAGVAR(hardcode_automatic, $1)=no
-  _LT_TAGVAR(hardcode_direct, $1)=no
-  _LT_TAGVAR(hardcode_direct_absolute, $1)=no
-  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
-  _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
-  _LT_TAGVAR(hardcode_libdir_separator, $1)=
-  _LT_TAGVAR(hardcode_minus_L, $1)=no
-  _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
-  _LT_TAGVAR(inherit_rpath, $1)=no
-  _LT_TAGVAR(link_all_deplibs, $1)=unknown
-  _LT_TAGVAR(module_cmds, $1)=
-  _LT_TAGVAR(module_expsym_cmds, $1)=
-  _LT_TAGVAR(old_archive_from_new_cmds, $1)=
-  _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)=
-  _LT_TAGVAR(thread_safe_flag_spec, $1)=
-  _LT_TAGVAR(whole_archive_flag_spec, $1)=
-  # include_expsyms should be a list of space-separated symbols to be *always*
-  # included in the symbol list
-  _LT_TAGVAR(include_expsyms, $1)=
-  # exclude_expsyms can be an extended regexp of symbols to exclude
-  # it will be wrapped by ` (' and `)$', so one must not match beginning or
-  # end of line.  Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
-  # as well as any symbol that contains `d'.
-  _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
-  # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
-  # platforms (ab)use it in PIC code, but their linkers get confused if
-  # the symbol is explicitly referenced.  Since portable code cannot
-  # rely on this symbol name, it's probably fine to never include it in
-  # preloaded symbol tables.
-  # Exclude shared library initialization/finalization symbols.
-dnl Note also adjust exclude_expsyms for C++ above.
-  extract_expsyms_cmds=
-
-  case $host_os in
-  cygwin* | mingw* | pw32* | cegcc*)
-    # FIXME: the MSVC++ port hasn't been tested in a loooong time
-    # When not using gcc, we currently assume that we are using
-    # Microsoft Visual C++.
-    if test "$GCC" != yes; then
-      with_gnu_ld=no
-    fi
-    ;;
-  interix*)
-    # we just hope/assume this is gcc and not c89 (= MSVC++)
-    with_gnu_ld=yes
-    ;;
-  openbsd*)
-    with_gnu_ld=no
-    ;;
-  linux* | k*bsd*-gnu)
-    _LT_TAGVAR(link_all_deplibs, $1)=no
-    ;;
-  esac
-
-  _LT_TAGVAR(ld_shlibs, $1)=yes
-  if test "$with_gnu_ld" = yes; then
-    # If archive_cmds runs LD, not CC, wlarc should be empty
-    wlarc='${wl}'
-
-    # Set some defaults for GNU ld with shared library support. These
-    # are reset later if shared libraries are not supported. Putting them
-    # here allows them to be overridden if necessary.
-    runpath_var=LD_RUN_PATH
-    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
-    _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
-    # ancient GNU ld didn't support --whole-archive et. al.
-    if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
-      _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
-    else
-      _LT_TAGVAR(whole_archive_flag_spec, $1)=
-    fi
-    supports_anon_versioning=no
-    case `$LD -v 2>&1` in
-      *GNU\ gold*) supports_anon_versioning=yes ;;
-      *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11
-      *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
-      *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
-      *\ 2.11.*) ;; # other 2.11 versions
-      *) supports_anon_versioning=yes ;;
-    esac
-
-    # See if GNU ld supports shared libraries.
-    case $host_os in
-    aix[[3-9]]*)
-      # On AIX/PPC, the GNU linker is very broken
-      if test "$host_cpu" != ia64; then
-       _LT_TAGVAR(ld_shlibs, $1)=no
-       cat <<_LT_EOF 1>&2
-
-*** Warning: the GNU linker, at least up to release 2.9.1, is reported
-*** to be unable to reliably create shared libraries on AIX.
-*** Therefore, libtool is disabling shared libraries support.  If you
-*** really care for shared libraries, you may want to modify your PATH
-*** so that a non-GNU linker is found, and then restart.
-
-_LT_EOF
-      fi
-      ;;
-
-    amigaos*)
-      case $host_cpu in
-      powerpc)
-            # see comment about AmigaOS4 .so support
-            _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
-            _LT_TAGVAR(archive_expsym_cmds, $1)=''
-        ;;
-      m68k)
-            _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
-            _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-            _LT_TAGVAR(hardcode_minus_L, $1)=yes
-        ;;
-      esac
-      ;;
-
-    beos*)
-      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
-       _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
-       # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
-       # support --undefined.  This deserves some investigation.  FIXME
-       _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
-      else
-       _LT_TAGVAR(ld_shlibs, $1)=no
-      fi
-      ;;
-
-    cygwin* | mingw* | pw32* | cegcc*)
-      # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
-      # as there is no search path for DLLs.
-      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
-      _LT_TAGVAR(always_export_symbols, $1)=no
-      _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
-      _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols'
-
-      if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
-        _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
-       # If the export-symbols file already is a .def file (1st line
-       # is EXPORTS), use it as is; otherwise, prepend...
-       _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
-         cp $export_symbols $output_objdir/$soname.def;
-       else
-         echo EXPORTS > $output_objdir/$soname.def;
-         cat $export_symbols >> $output_objdir/$soname.def;
-       fi~
-       $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
-      else
-       _LT_TAGVAR(ld_shlibs, $1)=no
-      fi
-      ;;
-
-    interix[[3-9]]*)
-      _LT_TAGVAR(hardcode_direct, $1)=no
-      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
-      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
-      # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
-      # Instead, shared libraries are loaded at an image base (0x10000000 by
-      # default) and relocated if they conflict, which is a slow very memory
-      # consuming and fragmenting process.  To avoid this, we pick a random,
-      # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
-      # time.  Moving up from 0x10000000 also allows more sbrk(2) space.
-      _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
-      _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
-      ;;
-
-    gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
-      tmp_diet=no
-      if test "$host_os" = linux-dietlibc; then
-       case $cc_basename in
-         diet\ *) tmp_diet=yes;;       # linux-dietlibc with static linking (!diet-dyn)
-       esac
-      fi
-      if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
-        && test "$tmp_diet" = no
-      then
-       tmp_addflag=
-       tmp_sharedflag='-shared'
-       case $cc_basename,$host_cpu in
-        pgcc*)                         # Portland Group C compiler
-         _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
-         tmp_addflag=' $pic_flag'
-         ;;
-       pgf77* | pgf90* | pgf95*)       # Portland Group f77 and f90 compilers
-         _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
-         tmp_addflag=' $pic_flag -Mnomain' ;;
-       ecc*,ia64* | icc*,ia64*)        # Intel C compiler on ia64
-         tmp_addflag=' -i_dynamic' ;;
-       efc*,ia64* | ifort*,ia64*)      # Intel Fortran compiler on ia64
-         tmp_addflag=' -i_dynamic -nofor_main' ;;
-       ifc* | ifort*)                  # Intel Fortran compiler
-         tmp_addflag=' -nofor_main' ;;
-       lf95*)                          # Lahey Fortran 8.1
-         _LT_TAGVAR(whole_archive_flag_spec, $1)=
-         tmp_sharedflag='--shared' ;;
-       xl[[cC]]*)                      # IBM XL C 8.0 on PPC (deal with xlf below)
-         tmp_sharedflag='-qmkshrobj'
-         tmp_addflag= ;;
-       esac
-       case `$CC -V 2>&1 | sed 5q` in
-       *Sun\ C*)                       # Sun C 5.9
-         _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
-         _LT_TAGVAR(compiler_needs_object, $1)=yes
-         tmp_sharedflag='-G' ;;
-       *Sun\ F*)                       # Sun Fortran 8.3
-         tmp_sharedflag='-G' ;;
-       esac
-       _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
-
-        if test "x$supports_anon_versioning" = xyes; then
-          _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
-           cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
-           echo "local: *; };" >> $output_objdir/$libname.ver~
-           $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
-        fi
-
-       case $cc_basename in
-       xlf*)
-         # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
-         _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive'
-         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
-         _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='-rpath $libdir'
-         _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib'
-         if test "x$supports_anon_versioning" = xyes; then
-           _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
-             cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
-             echo "local: *; };" >> $output_objdir/$libname.ver~
-             $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
-         fi
-         ;;
-       esac
-      else
-        _LT_TAGVAR(ld_shlibs, $1)=no
-      fi
-      ;;
-
-    netbsd* | netbsdelf*-gnu)
-      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
-       _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
-       wlarc=
-      else
-       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
-       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
-      fi
-      ;;
-
-    solaris*)
-      if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
-       _LT_TAGVAR(ld_shlibs, $1)=no
-       cat <<_LT_EOF 1>&2
-
-*** Warning: The releases 2.8.* of the GNU linker cannot reliably
-*** create shared libraries on Solaris systems.  Therefore, libtool
-*** is disabling shared libraries support.  We urge you to upgrade GNU
-*** binutils to release 2.9.1 or newer.  Another option is to modify
-*** your PATH or compiler configuration so that the native linker is
-*** used, and then restart.
-
-_LT_EOF
-      elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
-       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
-       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
-      else
-       _LT_TAGVAR(ld_shlibs, $1)=no
-      fi
-      ;;
-
-    sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
-      case `$LD -v 2>&1` in
-        *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*)
-       _LT_TAGVAR(ld_shlibs, $1)=no
-       cat <<_LT_EOF 1>&2
-
-*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
-*** reliably create shared libraries on SCO systems.  Therefore, libtool
-*** is disabling shared libraries support.  We urge you to upgrade GNU
-*** binutils to release 2.16.91.0.3 or newer.  Another option is to modify
-*** your PATH or compiler configuration so that the native linker is
-*** used, and then restart.
-
-_LT_EOF
-       ;;
-       *)
-         # For security reasons, it is highly recommended that you always
-         # use absolute paths for naming shared libraries, and exclude the
-         # DT_RUNPATH tag from executables and libraries.  But doing so
-         # requires that you compile everything twice, which is a pain.
-         if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
-           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
-           _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
-           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
-         else
-           _LT_TAGVAR(ld_shlibs, $1)=no
-         fi
-       ;;
-      esac
-      ;;
-
-    sunos4*)
-      _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
-      wlarc=
-      _LT_TAGVAR(hardcode_direct, $1)=yes
-      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    *)
-      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
-       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
-       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
-      else
-       _LT_TAGVAR(ld_shlibs, $1)=no
-      fi
-      ;;
-    esac
-
-    if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then
-      runpath_var=
-      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
-      _LT_TAGVAR(export_dynamic_flag_spec, $1)=
-      _LT_TAGVAR(whole_archive_flag_spec, $1)=
-    fi
-  else
-    # PORTME fill in a description of your system's linker (not GNU ld)
-    case $host_os in
-    aix3*)
-      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
-      _LT_TAGVAR(always_export_symbols, $1)=yes
-      _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
-      # Note: this linker hardcodes the directories in LIBPATH if there
-      # are no directories specified by -L.
-      _LT_TAGVAR(hardcode_minus_L, $1)=yes
-      if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
-       # Neither direct hardcoding nor static linking is supported with a
-       # broken collect2.
-       _LT_TAGVAR(hardcode_direct, $1)=unsupported
-      fi
-      ;;
-
-    aix[[4-9]]*)
-      if test "$host_cpu" = ia64; then
-       # On IA64, the linker does run time linking by default, so we don't
-       # have to do anything special.
-       aix_use_runtimelinking=no
-       exp_sym_flag='-Bexport'
-       no_entry_flag=""
-      else
-       # If we're using GNU nm, then we don't want the "-C" option.
-       # -C means demangle to AIX nm, but means don't demangle with GNU nm
-       if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
-         _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
-       else
-         _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
-       fi
-       aix_use_runtimelinking=no
-
-       # Test if we are trying to use run time linking or normal
-       # AIX style linking. If -brtl is somewhere in LDFLAGS, we
-       # need to do runtime linking.
-       case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
-         for ld_flag in $LDFLAGS; do
-         if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
-           aix_use_runtimelinking=yes
-           break
-         fi
-         done
-         ;;
-       esac
-
-       exp_sym_flag='-bexport'
-       no_entry_flag='-bnoentry'
-      fi
-
-      # When large executables or shared objects are built, AIX ld can
-      # have problems creating the table of contents.  If linking a library
-      # or program results in "error TOC overflow" add -mminimal-toc to
-      # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
-      # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
-
-      _LT_TAGVAR(archive_cmds, $1)=''
-      _LT_TAGVAR(hardcode_direct, $1)=yes
-      _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
-      _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
-      _LT_TAGVAR(link_all_deplibs, $1)=yes
-      _LT_TAGVAR(file_list_spec, $1)='${wl}-f,'
-
-      if test "$GCC" = yes; then
-       case $host_os in aix4.[[012]]|aix4.[[012]].*)
-       # We only want to do this on AIX 4.2 and lower, the check
-       # below for broken collect2 doesn't work under 4.3+
-         collect2name=`${CC} -print-prog-name=collect2`
-         if test -f "$collect2name" &&
-          strings "$collect2name" | $GREP resolve_lib_name >/dev/null
-         then
-         # We have reworked collect2
-         :
-         else
-         # We have old collect2
-         _LT_TAGVAR(hardcode_direct, $1)=unsupported
-         # It fails to find uninstalled libraries when the uninstalled
-         # path is not listed in the libpath.  Setting hardcode_minus_L
-         # to unsupported forces relinking
-         _LT_TAGVAR(hardcode_minus_L, $1)=yes
-         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-         _LT_TAGVAR(hardcode_libdir_separator, $1)=
-         fi
-         ;;
-       esac
-       shared_flag='-shared'
-       if test "$aix_use_runtimelinking" = yes; then
-         shared_flag="$shared_flag "'${wl}-G'
-       fi
-       _LT_TAGVAR(link_all_deplibs, $1)=no
-      else
-       # not using gcc
-       if test "$host_cpu" = ia64; then
-       # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
-       # chokes on -Wl,-G. The following line is correct:
-         shared_flag='-G'
-       else
-         if test "$aix_use_runtimelinking" = yes; then
-           shared_flag='${wl}-G'
-         else
-           shared_flag='${wl}-bM:SRE'
-         fi
-       fi
-      fi
-
-      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall'
-      # It seems that -bexpall does not export symbols beginning with
-      # underscore (_), so it is better to generate a list of symbols to export.
-      _LT_TAGVAR(always_export_symbols, $1)=yes
-      if test "$aix_use_runtimelinking" = yes; then
-       # Warning - without using the other runtime loading flags (-brtl),
-       # -berok will link without error, but may produce a broken library.
-       _LT_TAGVAR(allow_undefined_flag, $1)='-berok'
-        # Determine the default libpath from the value encoded in an
-        # empty executable.
-        _LT_SYS_MODULE_PATH_AIX
-        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
-        _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
-      else
-       if test "$host_cpu" = ia64; then
-         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
-         _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
-         _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
-       else
-        # Determine the default libpath from the value encoded in an
-        # empty executable.
-        _LT_SYS_MODULE_PATH_AIX
-        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
-         # Warning - without using the other run time loading flags,
-         # -berok will link without error, but may produce a broken library.
-         _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
-         _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
-         # Exported symbols can be pulled into shared objects from archives
-         _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
-         _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
-         # This is similar to how AIX traditionally builds its shared libraries.
-         _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
-       fi
-      fi
-      ;;
-
-    amigaos*)
-      case $host_cpu in
-      powerpc)
-            # see comment about AmigaOS4 .so support
-            _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
-            _LT_TAGVAR(archive_expsym_cmds, $1)=''
-        ;;
-      m68k)
-            _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
-            _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-            _LT_TAGVAR(hardcode_minus_L, $1)=yes
-        ;;
-      esac
-      ;;
-
-    bsdi[[45]]*)
-      _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic
-      ;;
-
-    cygwin* | mingw* | pw32* | cegcc*)
-      # When not using gcc, we currently assume that we are using
-      # Microsoft Visual C++.
-      # hardcode_libdir_flag_spec is actually meaningless, as there is
-      # no search path for DLLs.
-      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
-      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
-      # Tell ltmain to make .lib files, not .a files.
-      libext=lib
-      # Tell ltmain to make .dll files, not .so files.
-      shrext_cmds=".dll"
-      # FIXME: Setting linknames here is a bad hack.
-      _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames='
-      # The linker will automatically build a .lib file if we build a DLL.
-      _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
-      # FIXME: Should let the user specify the lib program.
-      _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs'
-      _LT_TAGVAR(fix_srcfile_path, $1)='`cygpath -w "$srcfile"`'
-      _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
-      ;;
-
-    darwin* | rhapsody*)
-      _LT_DARWIN_LINKER_FEATURES($1)
-      ;;
-
-    dgux*)
-      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
-      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    freebsd1*)
-      _LT_TAGVAR(ld_shlibs, $1)=no
-      ;;
-
-    # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
-    # support.  Future versions do this automatically, but an explicit c++rt0.o
-    # does not break anything, and helps significantly (at the cost of a little
-    # extra space).
-    freebsd2.2*)
-      _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
-      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
-      _LT_TAGVAR(hardcode_direct, $1)=yes
-      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    # Unfortunately, older versions of FreeBSD 2 do not have this feature.
-    freebsd2*)
-      _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
-      _LT_TAGVAR(hardcode_direct, $1)=yes
-      _LT_TAGVAR(hardcode_minus_L, $1)=yes
-      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
-    freebsd* | dragonfly*)
-      _LT_TAGVAR(archive_cmds, $1)='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
-      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
-      _LT_TAGVAR(hardcode_direct, $1)=yes
-      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    hpux9*)
-      if test "$GCC" = yes; then
-       _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
-      else
-       _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
-      fi
-      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
-      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
-      _LT_TAGVAR(hardcode_direct, $1)=yes
-
-      # hardcode_minus_L: Not really in the search PATH,
-      # but as the default location of the library.
-      _LT_TAGVAR(hardcode_minus_L, $1)=yes
-      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
-      ;;
-
-    hpux10*)
-      if test "$GCC" = yes -a "$with_gnu_ld" = no; then
-       _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
-      else
-       _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
-      fi
-      if test "$with_gnu_ld" = no; then
-       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
-       _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir'
-       _LT_TAGVAR(hardcode_libdir_separator, $1)=:
-       _LT_TAGVAR(hardcode_direct, $1)=yes
-       _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
-       _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
-       # hardcode_minus_L: Not really in the search PATH,
-       # but as the default location of the library.
-       _LT_TAGVAR(hardcode_minus_L, $1)=yes
-      fi
-      ;;
-
-    hpux11*)
-      if test "$GCC" = yes -a "$with_gnu_ld" = no; then
-       case $host_cpu in
-       hppa*64*)
-         _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
-         ;;
-       ia64*)
-         _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
-         ;;
-       *)
-         _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
-         ;;
-       esac
-      else
-       case $host_cpu in
-       hppa*64*)
-         _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
-         ;;
-       ia64*)
-         _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
-         ;;
-       *)
-         _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
-         ;;
-       esac
-      fi
-      if test "$with_gnu_ld" = no; then
-       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
-       _LT_TAGVAR(hardcode_libdir_separator, $1)=:
-
-       case $host_cpu in
-       hppa*64*|ia64*)
-         _LT_TAGVAR(hardcode_direct, $1)=no
-         _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-         ;;
-       *)
-         _LT_TAGVAR(hardcode_direct, $1)=yes
-         _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
-         _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
-
-         # hardcode_minus_L: Not really in the search PATH,
-         # but as the default location of the library.
-         _LT_TAGVAR(hardcode_minus_L, $1)=yes
-         ;;
-       esac
-      fi
-      ;;
-
-    irix5* | irix6* | nonstopux*)
-      if test "$GCC" = yes; then
-       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
-       # Try to use the -exported_symbol ld option, if it does not
-       # work, assume that -exports_file does not work either and
-       # implicitly export all symbols.
-        save_LDFLAGS="$LDFLAGS"
-        LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
-        AC_LINK_IFELSE(int foo(void) {},
-          _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
-        )
-        LDFLAGS="$save_LDFLAGS"
-      else
-       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
-       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
-      fi
-      _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
-      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
-      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
-      _LT_TAGVAR(inherit_rpath, $1)=yes
-      _LT_TAGVAR(link_all_deplibs, $1)=yes
-      ;;
-
-    netbsd* | netbsdelf*-gnu)
-      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
-       _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
-      else
-       _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags'      # ELF
-      fi
-      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
-      _LT_TAGVAR(hardcode_direct, $1)=yes
-      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    newsos6)
-      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
-      _LT_TAGVAR(hardcode_direct, $1)=yes
-      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
-      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
-      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    *nto* | *qnx*)
-      ;;
-
-    openbsd*)
-      if test -f /usr/libexec/ld.so; then
-       _LT_TAGVAR(hardcode_direct, $1)=yes
-       _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-       _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
-       if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
-         _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
-         _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
-         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
-         _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
-       else
-         case $host_os in
-          openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*)
-            _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
-            _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
-            ;;
-          *)
-            _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
-            _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
-            ;;
-         esac
-       fi
-      else
-       _LT_TAGVAR(ld_shlibs, $1)=no
-      fi
-      ;;
-
-    os2*)
-      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-      _LT_TAGVAR(hardcode_minus_L, $1)=yes
-      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
-      _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
-      _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
-      ;;
-
-    osf3*)
-      if test "$GCC" = yes; then
-       _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
-       _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
-      else
-       _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
-       _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
-      fi
-      _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
-      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
-      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
-      ;;
-
-    osf4* | osf5*)     # as osf3* with the addition of -msym flag
-      if test "$GCC" = yes; then
-       _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
-       _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
-       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
-      else
-       _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
-       _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
-       _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
-       $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
-
-       # Both c and cxx compiler support -rpath directly
-       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
-      fi
-      _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
-      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
-      ;;
-
-    solaris*)
-      _LT_TAGVAR(no_undefined_flag, $1)=' -z defs'
-      if test "$GCC" = yes; then
-       wlarc='${wl}'
-       _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
-       _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
-         $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
-      else
-       case `$CC -V 2>&1` in
-       *"Compilers 5.0"*)
-         wlarc=''
-         _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
-         _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
-         $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
-         ;;
-       *)
-         wlarc='${wl}'
-         _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
-         _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
-         $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
-         ;;
-       esac
-      fi
-      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
-      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-      case $host_os in
-      solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
-      *)
-       # The compiler driver will combine and reorder linker options,
-       # but understands `-z linker_flag'.  GCC discards it without `$wl',
-       # but is careful enough not to reorder.
-       # Supported since Solaris 2.6 (maybe 2.5.1?)
-       if test "$GCC" = yes; then
-         _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
-       else
-         _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
-       fi
-       ;;
-      esac
-      _LT_TAGVAR(link_all_deplibs, $1)=yes
-      ;;
-
-    sunos4*)
-      if test "x$host_vendor" = xsequent; then
-       # Use $CC to link under sequent, because it throws in some extra .o
-       # files that make .init and .fini sections work.
-       _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
-      else
-       _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
-      fi
-      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-      _LT_TAGVAR(hardcode_direct, $1)=yes
-      _LT_TAGVAR(hardcode_minus_L, $1)=yes
-      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    sysv4)
-      case $host_vendor in
-       sni)
-         _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
-         _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true???
-       ;;
-       siemens)
-         ## LD is ld it makes a PLAMLIB
-         ## CC just makes a GrossModule.
-         _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags'
-         _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs'
-         _LT_TAGVAR(hardcode_direct, $1)=no
-        ;;
-       motorola)
-         _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
-         _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie
-       ;;
-      esac
-      runpath_var='LD_RUN_PATH'
-      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    sysv4.3*)
-      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
-      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-      _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport'
-      ;;
-
-    sysv4*MP*)
-      if test -d /usr/nec; then
-       _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
-       _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-       runpath_var=LD_RUN_PATH
-       hardcode_runpath_var=yes
-       _LT_TAGVAR(ld_shlibs, $1)=yes
-      fi
-      ;;
-
-    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
-      _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
-      _LT_TAGVAR(archive_cmds_need_lc, $1)=no
-      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-      runpath_var='LD_RUN_PATH'
-
-      if test "$GCC" = yes; then
-       _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
-       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
-      else
-       _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
-       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
-      fi
-      ;;
-
-    sysv5* | sco3.2v5* | sco5v6*)
-      # Note: We can NOT use -z defs as we might desire, because we do not
-      # link with -lc, and that would cause any symbols used from libc to
-      # always be unresolved, which means just about no library would
-      # ever link correctly.  If we're not using GNU ld we use -z text
-      # though, which does catch some bad symbols but isn't as heavy-handed
-      # as -z defs.
-      _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
-      _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs'
-      _LT_TAGVAR(archive_cmds_need_lc, $1)=no
-      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir'
-      _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
-      _LT_TAGVAR(link_all_deplibs, $1)=yes
-      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
-      runpath_var='LD_RUN_PATH'
-
-      if test "$GCC" = yes; then
-       _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
-       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
-      else
-       _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
-       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
-      fi
-      ;;
-
-    uts4*)
-      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
-      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    *)
-      _LT_TAGVAR(ld_shlibs, $1)=no
-      ;;
-    esac
-
-    if test x$host_vendor = xsni; then
-      case $host in
-      sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
-       _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym'
-       ;;
-      esac
-    fi
-  fi
-])
-AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
-test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
-
-_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld
-
-_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl
-_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl
-_LT_DECL([], [extract_expsyms_cmds], [2],
-    [The commands to extract the exported symbol list from a shared archive])
-
-#
-# Do we need to explicitly link libc?
-#
-case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in
-x|xyes)
-  # Assume -lc should be added
-  _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
-
-  if test "$enable_shared" = yes && test "$GCC" = yes; then
-    case $_LT_TAGVAR(archive_cmds, $1) in
-    *'~'*)
-      # FIXME: we may have to deal with multi-command sequences.
-      ;;
-    '$CC '*)
-      # Test whether the compiler implicitly links with -lc since on some
-      # systems, -lgcc has to come before -lc. If gcc already passes -lc
-      # to ld, don't add -lc before -lgcc.
-      AC_MSG_CHECKING([whether -lc should be explicitly linked in])
-      $RM conftest*
-      echo "$lt_simple_compile_test_code" > conftest.$ac_ext
-
-      if AC_TRY_EVAL(ac_compile) 2>conftest.err; then
-        soname=conftest
-        lib=conftest
-        libobjs=conftest.$ac_objext
-        deplibs=
-        wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1)
-       pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1)
-        compiler_flags=-v
-        linker_flags=-v
-        verstring=
-        output_objdir=.
-        libname=conftest
-        lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1)
-        _LT_TAGVAR(allow_undefined_flag, $1)=
-        if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1)
-        then
-         _LT_TAGVAR(archive_cmds_need_lc, $1)=no
-        else
-         _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
-        fi
-        _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag
-      else
-        cat conftest.err 1>&5
-      fi
-      $RM conftest*
-      AC_MSG_RESULT([$_LT_TAGVAR(archive_cmds_need_lc, $1)])
-      ;;
-    esac
-  fi
-  ;;
-esac
-
-_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0],
-    [Whether or not to add -lc for building shared libraries])
-_LT_TAGDECL([allow_libtool_libs_with_static_runtimes],
-    [enable_shared_with_static_runtimes], [0],
-    [Whether or not to disallow shared libs when runtime libs are static])
-_LT_TAGDECL([], [export_dynamic_flag_spec], [1],
-    [Compiler flag to allow reflexive dlopens])
-_LT_TAGDECL([], [whole_archive_flag_spec], [1],
-    [Compiler flag to generate shared objects directly from archives])
-_LT_TAGDECL([], [compiler_needs_object], [1],
-    [Whether the compiler copes with passing no objects directly])
-_LT_TAGDECL([], [old_archive_from_new_cmds], [2],
-    [Create an old-style archive from a shared archive])
-_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2],
-    [Create a temporary old-style archive to link instead of a shared archive])
-_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive])
-_LT_TAGDECL([], [archive_expsym_cmds], [2])
-_LT_TAGDECL([], [module_cmds], [2],
-    [Commands used to build a loadable module if different from building
-    a shared archive.])
-_LT_TAGDECL([], [module_expsym_cmds], [2])
-_LT_TAGDECL([], [with_gnu_ld], [1],
-    [Whether we are building with GNU ld or not])
-_LT_TAGDECL([], [allow_undefined_flag], [1],
-    [Flag that allows shared libraries with undefined symbols to be built])
-_LT_TAGDECL([], [no_undefined_flag], [1],
-    [Flag that enforces no undefined symbols])
-_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1],
-    [Flag to hardcode $libdir into a binary during linking.
-    This must work even if $libdir does not exist])
-_LT_TAGDECL([], [hardcode_libdir_flag_spec_ld], [1],
-    [[If ld is used when linking, flag to hardcode $libdir into a binary
-    during linking.  This must work even if $libdir does not exist]])
-_LT_TAGDECL([], [hardcode_libdir_separator], [1],
-    [Whether we need a single "-rpath" flag with a separated argument])
-_LT_TAGDECL([], [hardcode_direct], [0],
-    [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes
-    DIR into the resulting binary])
-_LT_TAGDECL([], [hardcode_direct_absolute], [0],
-    [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes
-    DIR into the resulting binary and the resulting library dependency is
-    "absolute", i.e impossible to change by setting ${shlibpath_var} if the
-    library is relocated])
-_LT_TAGDECL([], [hardcode_minus_L], [0],
-    [Set to "yes" if using the -LDIR flag during linking hardcodes DIR
-    into the resulting binary])
-_LT_TAGDECL([], [hardcode_shlibpath_var], [0],
-    [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
-    into the resulting binary])
-_LT_TAGDECL([], [hardcode_automatic], [0],
-    [Set to "yes" if building a shared library automatically hardcodes DIR
-    into the library and all subsequent libraries and executables linked
-    against it])
-_LT_TAGDECL([], [inherit_rpath], [0],
-    [Set to yes if linker adds runtime paths of dependent libraries
-    to runtime path list])
-_LT_TAGDECL([], [link_all_deplibs], [0],
-    [Whether libtool must link a program against all its dependency libraries])
-_LT_TAGDECL([], [fix_srcfile_path], [1],
-    [Fix the shell variable $srcfile for the compiler])
-_LT_TAGDECL([], [always_export_symbols], [0],
-    [Set to "yes" if exported symbols are required])
-_LT_TAGDECL([], [export_symbols_cmds], [2],
-    [The commands to list exported symbols])
-_LT_TAGDECL([], [exclude_expsyms], [1],
-    [Symbols that should not be listed in the preloaded symbols])
-_LT_TAGDECL([], [include_expsyms], [1],
-    [Symbols that must always be exported])
-_LT_TAGDECL([], [prelink_cmds], [2],
-    [Commands necessary for linking programs (against libraries) with templates])
-_LT_TAGDECL([], [file_list_spec], [1],
-    [Specify filename containing input files])
-dnl FIXME: Not yet implemented
-dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1],
-dnl    [Compiler flag to generate thread safe objects])
-])# _LT_LINKER_SHLIBS
-
-
-# _LT_LANG_C_CONFIG([TAG])
-# ------------------------
-# Ensure that the configuration variables for a C compiler are suitably
-# defined.  These variables are subsequently used by _LT_CONFIG to write
-# the compiler configuration to `libtool'.
-m4_defun([_LT_LANG_C_CONFIG],
-[m4_require([_LT_DECL_EGREP])dnl
-lt_save_CC="$CC"
-AC_LANG_PUSH(C)
-
-# Source file extension for C test sources.
-ac_ext=c
-
-# Object file extension for compiled C test sources.
-objext=o
-_LT_TAGVAR(objext, $1)=$objext
-
-# Code to be used in simple compile tests
-lt_simple_compile_test_code="int some_variable = 0;"
-
-# Code to be used in simple link tests
-lt_simple_link_test_code='int main(){return(0);}'
-
-_LT_TAG_COMPILER
-# Save the default compiler, since it gets overwritten when the other
-# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
-compiler_DEFAULT=$CC
-
-# save warnings/boilerplate of simple test code
-_LT_COMPILER_BOILERPLATE
-_LT_LINKER_BOILERPLATE
-
-## CAVEAT EMPTOR:
-## There is no encapsulation within the following macros, do not change
-## the running order or otherwise move them around unless you know exactly
-## what you are doing...
-if test -n "$compiler"; then
-  _LT_COMPILER_NO_RTTI($1)
-  _LT_COMPILER_PIC($1)
-  _LT_COMPILER_C_O($1)
-  _LT_COMPILER_FILE_LOCKS($1)
-  _LT_LINKER_SHLIBS($1)
-  _LT_SYS_DYNAMIC_LINKER($1)
-  _LT_LINKER_HARDCODE_LIBPATH($1)
-  LT_SYS_DLOPEN_SELF
-  _LT_CMD_STRIPLIB
-
-  # Report which library types will actually be built
-  AC_MSG_CHECKING([if libtool supports shared libraries])
-  AC_MSG_RESULT([$can_build_shared])
-
-  AC_MSG_CHECKING([whether to build shared libraries])
-  test "$can_build_shared" = "no" && enable_shared=no
-
-  # On AIX, shared libraries and static libraries use the same namespace, and
-  # are all built from PIC.
-  case $host_os in
-  aix3*)
-    test "$enable_shared" = yes && enable_static=no
-    if test -n "$RANLIB"; then
-      archive_cmds="$archive_cmds~\$RANLIB \$lib"
-      postinstall_cmds='$RANLIB $lib'
-    fi
-    ;;
-
-  aix[[4-9]]*)
-    if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
-      test "$enable_shared" = yes && enable_static=no
-    fi
-    ;;
-  esac
-  AC_MSG_RESULT([$enable_shared])
-
-  AC_MSG_CHECKING([whether to build static libraries])
-  # Make sure either enable_shared or enable_static is yes.
-  test "$enable_shared" = yes || enable_static=yes
-  AC_MSG_RESULT([$enable_static])
-
-  _LT_CONFIG($1)
-fi
-AC_LANG_POP
-CC="$lt_save_CC"
-])# _LT_LANG_C_CONFIG
-
-
-# _LT_PROG_CXX
-# ------------
-# Since AC_PROG_CXX is broken, in that it returns g++ if there is no c++
-# compiler, we have our own version here.
-m4_defun([_LT_PROG_CXX],
-[
-pushdef([AC_MSG_ERROR], [_lt_caught_CXX_error=yes])
-AC_PROG_CXX
-if test -n "$CXX" && ( test "X$CXX" != "Xno" &&
-    ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) ||
-    (test "X$CXX" != "Xg++"))) ; then
-  AC_PROG_CXXCPP
-else
-  _lt_caught_CXX_error=yes
-fi
-popdef([AC_MSG_ERROR])
-])# _LT_PROG_CXX
-
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([_LT_PROG_CXX], [])
-
-
-# _LT_LANG_CXX_CONFIG([TAG])
-# --------------------------
-# Ensure that the configuration variables for a C++ compiler are suitably
-# defined.  These variables are subsequently used by _LT_CONFIG to write
-# the compiler configuration to `libtool'.
-m4_defun([_LT_LANG_CXX_CONFIG],
-[AC_REQUIRE([_LT_PROG_CXX])dnl
-m4_require([_LT_FILEUTILS_DEFAULTS])dnl
-m4_require([_LT_DECL_EGREP])dnl
-
-AC_LANG_PUSH(C++)
-_LT_TAGVAR(archive_cmds_need_lc, $1)=no
-_LT_TAGVAR(allow_undefined_flag, $1)=
-_LT_TAGVAR(always_export_symbols, $1)=no
-_LT_TAGVAR(archive_expsym_cmds, $1)=
-_LT_TAGVAR(compiler_needs_object, $1)=no
-_LT_TAGVAR(export_dynamic_flag_spec, $1)=
-_LT_TAGVAR(hardcode_direct, $1)=no
-_LT_TAGVAR(hardcode_direct_absolute, $1)=no
-_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
-_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
-_LT_TAGVAR(hardcode_libdir_separator, $1)=
-_LT_TAGVAR(hardcode_minus_L, $1)=no
-_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
-_LT_TAGVAR(hardcode_automatic, $1)=no
-_LT_TAGVAR(inherit_rpath, $1)=no
-_LT_TAGVAR(module_cmds, $1)=
-_LT_TAGVAR(module_expsym_cmds, $1)=
-_LT_TAGVAR(link_all_deplibs, $1)=unknown
-_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
-_LT_TAGVAR(no_undefined_flag, $1)=
-_LT_TAGVAR(whole_archive_flag_spec, $1)=
-_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
-
-# Source file extension for C++ test sources.
-ac_ext=cpp
-
-# Object file extension for compiled C++ test sources.
-objext=o
-_LT_TAGVAR(objext, $1)=$objext
-
-# No sense in running all these tests if we already determined that
-# the CXX compiler isn't working.  Some variables (like enable_shared)
-# are currently assumed to apply to all compilers on this platform,
-# and will be corrupted by setting them based on a non-working compiler.
-if test "$_lt_caught_CXX_error" != yes; then
-  # Code to be used in simple compile tests
-  lt_simple_compile_test_code="int some_variable = 0;"
-
-  # Code to be used in simple link tests
-  lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }'
-
-  # ltmain only uses $CC for tagged configurations so make sure $CC is set.
-  _LT_TAG_COMPILER
-
-  # save warnings/boilerplate of simple test code
-  _LT_COMPILER_BOILERPLATE
-  _LT_LINKER_BOILERPLATE
-
-  # Allow CC to be a program name with arguments.
-  lt_save_CC=$CC
-  lt_save_LD=$LD
-  lt_save_GCC=$GCC
-  GCC=$GXX
-  lt_save_with_gnu_ld=$with_gnu_ld
-  lt_save_path_LD=$lt_cv_path_LD
-  if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then
-    lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx
-  else
-    $as_unset lt_cv_prog_gnu_ld
-  fi
-  if test -n "${lt_cv_path_LDCXX+set}"; then
-    lt_cv_path_LD=$lt_cv_path_LDCXX
-  else
-    $as_unset lt_cv_path_LD
-  fi
-  test -z "${LDCXX+set}" || LD=$LDCXX
-  CC=${CXX-"c++"}
-  compiler=$CC
-  _LT_TAGVAR(compiler, $1)=$CC
-  _LT_CC_BASENAME([$compiler])
-
-  if test -n "$compiler"; then
-    # We don't want -fno-exception when compiling C++ code, so set the
-    # no_builtin_flag separately
-    if test "$GXX" = yes; then
-      _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
-    else
-      _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
-    fi
-
-    if test "$GXX" = yes; then
-      # Set up default GNU C++ configuration
-
-      LT_PATH_LD
-
-      # Check if GNU C++ uses GNU ld as the underlying linker, since the
-      # archiving commands below assume that GNU ld is being used.
-      if test "$with_gnu_ld" = yes; then
-        _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
-        _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
-
-        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
-        _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
-
-        # If archive_cmds runs LD, not CC, wlarc should be empty
-        # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to
-        #     investigate it a little bit more. (MM)
-        wlarc='${wl}'
-
-        # ancient GNU ld didn't support --whole-archive et. al.
-        if eval "`$CC -print-prog-name=ld` --help 2>&1" |
-         $GREP 'no-whole-archive' > /dev/null; then
-          _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
-        else
-          _LT_TAGVAR(whole_archive_flag_spec, $1)=
-        fi
-      else
-        with_gnu_ld=no
-        wlarc=
-
-        # A generic and very simple default shared library creation
-        # command for GNU C++ for the case where it uses the native
-        # linker, instead of GNU ld.  If possible, this setting should
-        # overridden to take advantage of the native linker features on
-        # the platform it is being used on.
-        _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
-      fi
-
-      # Commands to make compiler produce verbose output that lists
-      # what "hidden" libraries, object files and flags are used when
-      # linking a shared library.
-      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
-
-    else
-      GXX=no
-      with_gnu_ld=no
-      wlarc=
-    fi
-
-    # PORTME: fill in a description of your system's C++ link characteristics
-    AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
-    _LT_TAGVAR(ld_shlibs, $1)=yes
-    case $host_os in
-      aix3*)
-        # FIXME: insert proper C++ library support
-        _LT_TAGVAR(ld_shlibs, $1)=no
-        ;;
-      aix[[4-9]]*)
-        if test "$host_cpu" = ia64; then
-          # On IA64, the linker does run time linking by default, so we don't
-          # have to do anything special.
-          aix_use_runtimelinking=no
-          exp_sym_flag='-Bexport'
-          no_entry_flag=""
-        else
-          aix_use_runtimelinking=no
-
-          # Test if we are trying to use run time linking or normal
-          # AIX style linking. If -brtl is somewhere in LDFLAGS, we
-          # need to do runtime linking.
-          case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
-           for ld_flag in $LDFLAGS; do
-             case $ld_flag in
-             *-brtl*)
-               aix_use_runtimelinking=yes
-               break
-               ;;
-             esac
-           done
-           ;;
-          esac
-
-          exp_sym_flag='-bexport'
-          no_entry_flag='-bnoentry'
-        fi
-
-        # When large executables or shared objects are built, AIX ld can
-        # have problems creating the table of contents.  If linking a library
-        # or program results in "error TOC overflow" add -mminimal-toc to
-        # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
-        # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
-
-        _LT_TAGVAR(archive_cmds, $1)=''
-        _LT_TAGVAR(hardcode_direct, $1)=yes
-        _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
-        _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
-        _LT_TAGVAR(link_all_deplibs, $1)=yes
-        _LT_TAGVAR(file_list_spec, $1)='${wl}-f,'
-
-        if test "$GXX" = yes; then
-          case $host_os in aix4.[[012]]|aix4.[[012]].*)
-          # We only want to do this on AIX 4.2 and lower, the check
-          # below for broken collect2 doesn't work under 4.3+
-         collect2name=`${CC} -print-prog-name=collect2`
-         if test -f "$collect2name" &&
-            strings "$collect2name" | $GREP resolve_lib_name >/dev/null
-         then
-           # We have reworked collect2
-           :
-         else
-           # We have old collect2
-           _LT_TAGVAR(hardcode_direct, $1)=unsupported
-           # It fails to find uninstalled libraries when the uninstalled
-           # path is not listed in the libpath.  Setting hardcode_minus_L
-           # to unsupported forces relinking
-           _LT_TAGVAR(hardcode_minus_L, $1)=yes
-           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-           _LT_TAGVAR(hardcode_libdir_separator, $1)=
-         fi
-          esac
-          shared_flag='-shared'
-         if test "$aix_use_runtimelinking" = yes; then
-           shared_flag="$shared_flag "'${wl}-G'
-         fi
-        else
-          # not using gcc
-          if test "$host_cpu" = ia64; then
-         # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
-         # chokes on -Wl,-G. The following line is correct:
-         shared_flag='-G'
-          else
-           if test "$aix_use_runtimelinking" = yes; then
-             shared_flag='${wl}-G'
-           else
-             shared_flag='${wl}-bM:SRE'
-           fi
-          fi
-        fi
-
-        _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall'
-        # It seems that -bexpall does not export symbols beginning with
-        # underscore (_), so it is better to generate a list of symbols to
-       # export.
-        _LT_TAGVAR(always_export_symbols, $1)=yes
-        if test "$aix_use_runtimelinking" = yes; then
-          # Warning - without using the other runtime loading flags (-brtl),
-          # -berok will link without error, but may produce a broken library.
-          _LT_TAGVAR(allow_undefined_flag, $1)='-berok'
-          # Determine the default libpath from the value encoded in an empty
-          # executable.
-          _LT_SYS_MODULE_PATH_AIX
-          _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
-
-          _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
-        else
-          if test "$host_cpu" = ia64; then
-           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
-           _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
-           _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
-          else
-           # Determine the default libpath from the value encoded in an
-           # empty executable.
-           _LT_SYS_MODULE_PATH_AIX
-           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
-           # Warning - without using the other run time loading flags,
-           # -berok will link without error, but may produce a broken library.
-           _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
-           _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
-           # Exported symbols can be pulled into shared objects from archives
-           _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
-           _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
-           # This is similar to how AIX traditionally builds its shared
-           # libraries.
-           _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
-          fi
-        fi
-        ;;
-
-      beos*)
-       if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
-         _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
-         # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
-         # support --undefined.  This deserves some investigation.  FIXME
-         _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
-       else
-         _LT_TAGVAR(ld_shlibs, $1)=no
-       fi
-       ;;
-
-      chorus*)
-        case $cc_basename in
-          *)
-         # FIXME: insert proper C++ library support
-         _LT_TAGVAR(ld_shlibs, $1)=no
-         ;;
-        esac
-        ;;
-
-      cygwin* | mingw* | pw32* | cegcc*)
-        # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
-        # as there is no search path for DLLs.
-        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-        _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
-        _LT_TAGVAR(always_export_symbols, $1)=no
-        _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
-
-        if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
-          _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
-          # If the export-symbols file already is a .def file (1st line
-          # is EXPORTS), use it as is; otherwise, prepend...
-          _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
-           cp $export_symbols $output_objdir/$soname.def;
-          else
-           echo EXPORTS > $output_objdir/$soname.def;
-           cat $export_symbols >> $output_objdir/$soname.def;
-          fi~
-          $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
-        else
-          _LT_TAGVAR(ld_shlibs, $1)=no
-        fi
-        ;;
-      darwin* | rhapsody*)
-        _LT_DARWIN_LINKER_FEATURES($1)
-       ;;
-
-      dgux*)
-        case $cc_basename in
-          ec++*)
-           # FIXME: insert proper C++ library support
-           _LT_TAGVAR(ld_shlibs, $1)=no
-           ;;
-          ghcx*)
-           # Green Hills C++ Compiler
-           # FIXME: insert proper C++ library support
-           _LT_TAGVAR(ld_shlibs, $1)=no
-           ;;
-          *)
-           # FIXME: insert proper C++ library support
-           _LT_TAGVAR(ld_shlibs, $1)=no
-           ;;
-        esac
-        ;;
-
-      freebsd[[12]]*)
-        # C++ shared libraries reported to be fairly broken before
-       # switch to ELF
-        _LT_TAGVAR(ld_shlibs, $1)=no
-        ;;
-
-      freebsd-elf*)
-        _LT_TAGVAR(archive_cmds_need_lc, $1)=no
-        ;;
-
-      freebsd* | dragonfly*)
-        # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF
-        # conventions
-        _LT_TAGVAR(ld_shlibs, $1)=yes
-        ;;
-
-      gnu*)
-        ;;
-
-      hpux9*)
-        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
-        _LT_TAGVAR(hardcode_libdir_separator, $1)=:
-        _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
-        _LT_TAGVAR(hardcode_direct, $1)=yes
-        _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
-                                            # but as the default
-                                            # location of the library.
-
-        case $cc_basename in
-          CC*)
-            # FIXME: insert proper C++ library support
-            _LT_TAGVAR(ld_shlibs, $1)=no
-            ;;
-          aCC*)
-            _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
-            # Commands to make compiler produce verbose output that lists
-            # what "hidden" libraries, object files and flags are used when
-            # linking a shared library.
-            #
-            # There doesn't appear to be a way to prevent this compiler from
-            # explicitly linking system object files so we need to strip them
-            # from the output so that they don't get included in the library
-            # dependencies.
-            output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
-            ;;
-          *)
-            if test "$GXX" = yes; then
-              _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
-            else
-              # FIXME: insert proper C++ library support
-              _LT_TAGVAR(ld_shlibs, $1)=no
-            fi
-            ;;
-        esac
-        ;;
-
-      hpux10*|hpux11*)
-        if test $with_gnu_ld = no; then
-         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
-         _LT_TAGVAR(hardcode_libdir_separator, $1)=:
-
-          case $host_cpu in
-            hppa*64*|ia64*)
-              ;;
-            *)
-             _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
-              ;;
-          esac
-        fi
-        case $host_cpu in
-          hppa*64*|ia64*)
-            _LT_TAGVAR(hardcode_direct, $1)=no
-            _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-            ;;
-          *)
-            _LT_TAGVAR(hardcode_direct, $1)=yes
-            _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
-            _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
-                                                # but as the default
-                                                # location of the library.
-            ;;
-        esac
-
-        case $cc_basename in
-          CC*)
-           # FIXME: insert proper C++ library support
-           _LT_TAGVAR(ld_shlibs, $1)=no
-           ;;
-          aCC*)
-           case $host_cpu in
-             hppa*64*)
-               _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
-               ;;
-             ia64*)
-               _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
-               ;;
-             *)
-               _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
-               ;;
-           esac
-           # Commands to make compiler produce verbose output that lists
-           # what "hidden" libraries, object files and flags are used when
-           # linking a shared library.
-           #
-           # There doesn't appear to be a way to prevent this compiler from
-           # explicitly linking system object files so we need to strip them
-           # from the output so that they don't get included in the library
-           # dependencies.
-           output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
-           ;;
-          *)
-           if test "$GXX" = yes; then
-             if test $with_gnu_ld = no; then
-               case $host_cpu in
-                 hppa*64*)
-                   _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
-                   ;;
-                 ia64*)
-                   _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
-                   ;;
-                 *)
-                   _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
-                   ;;
-               esac
-             fi
-           else
-             # FIXME: insert proper C++ library support
-             _LT_TAGVAR(ld_shlibs, $1)=no
-           fi
-           ;;
-        esac
-        ;;
-
-      interix[[3-9]]*)
-       _LT_TAGVAR(hardcode_direct, $1)=no
-       _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
-       _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
-       # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
-       # Instead, shared libraries are loaded at an image base (0x10000000 by
-       # default) and relocated if they conflict, which is a slow very memory
-       # consuming and fragmenting process.  To avoid this, we pick a random,
-       # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
-       # time.  Moving up from 0x10000000 also allows more sbrk(2) space.
-       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
-       _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
-       ;;
-      irix5* | irix6*)
-        case $cc_basename in
-          CC*)
-           # SGI C++
-           _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
-
-           # Archives containing C++ object files must be created using
-           # "CC -ar", where "CC" is the IRIX C++ compiler.  This is
-           # necessary to make sure instantiated templates are included
-           # in the archive.
-           _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs'
-           ;;
-          *)
-           if test "$GXX" = yes; then
-             if test "$with_gnu_ld" = no; then
-               _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
-             else
-               _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` -o $lib'
-             fi
-           fi
-           _LT_TAGVAR(link_all_deplibs, $1)=yes
-           ;;
-        esac
-        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
-        _LT_TAGVAR(hardcode_libdir_separator, $1)=:
-        _LT_TAGVAR(inherit_rpath, $1)=yes
-        ;;
-
-      linux* | k*bsd*-gnu | kopensolaris*-gnu)
-        case $cc_basename in
-          KCC*)
-           # Kuck and Associates, Inc. (KAI) C++ Compiler
-
-           # KCC will only create a shared library if the output file
-           # ends with ".so" (or ".sl" for HP-UX), so rename the library
-           # to its proper name (with version) after linking.
-           _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
-           _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib'
-           # Commands to make compiler produce verbose output that lists
-           # what "hidden" libraries, object files and flags are used when
-           # linking a shared library.
-           #
-           # There doesn't appear to be a way to prevent this compiler from
-           # explicitly linking system object files so we need to strip them
-           # from the output so that they don't get included in the library
-           # dependencies.
-           output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
-
-           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
-           _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
-
-           # Archives containing C++ object files must be created using
-           # "CC -Bstatic", where "CC" is the KAI C++ compiler.
-           _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs'
-           ;;
-         icpc* | ecpc* )
-           # Intel C++
-           with_gnu_ld=yes
-           # version 8.0 and above of icpc choke on multiply defined symbols
-           # if we add $predep_objects and $postdep_objects, however 7.1 and
-           # earlier do not add the objects themselves.
-           case `$CC -V 2>&1` in
-             *"Version 7."*)
-               _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
-               _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
-               ;;
-             *)  # Version 8.0 or newer
-               tmp_idyn=
-               case $host_cpu in
-                 ia64*) tmp_idyn=' -i_dynamic';;
-               esac
-               _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
-               _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
-               ;;
-           esac
-           _LT_TAGVAR(archive_cmds_need_lc, $1)=no
-           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
-           _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
-           _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
-           ;;
-          pgCC* | pgcpp*)
-            # Portland Group C++ compiler
-           case `$CC -V` in
-           *pgCC\ [[1-5]]* | *pgcpp\ [[1-5]]*)
-             _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~
-               rm -rf $tpldir~
-               $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
-               compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"'
-             _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~
-               rm -rf $tpldir~
-               $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
-               $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~
-               $RANLIB $oldlib'
-             _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~
-               rm -rf $tpldir~
-               $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
-               $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
-             _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~
-               rm -rf $tpldir~
-               $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
-               $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
-             ;;
-           *) # Version 6 will use weak symbols
-             _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
-             _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
-             ;;
-           esac
-
-           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir'
-           _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
-           _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
-            ;;
-         cxx*)
-           # Compaq C++
-           _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
-           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname  -o $lib ${wl}-retain-symbols-file $wl$export_symbols'
-
-           runpath_var=LD_RUN_PATH
-           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
-           _LT_TAGVAR(hardcode_libdir_separator, $1)=:
-
-           # Commands to make compiler produce verbose output that lists
-           # what "hidden" libraries, object files and flags are used when
-           # linking a shared library.
-           #
-           # There doesn't appear to be a way to prevent this compiler from
-           # explicitly linking system object files so we need to strip them
-           # from the output so that they don't get included in the library
-           # dependencies.
-           output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
-           ;;
-         xl*)
-           # IBM XL 8.0 on PPC, with GNU ld
-           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
-           _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
-           _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
-           if test "x$supports_anon_versioning" = xyes; then
-             _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
-               cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
-               echo "local: *; };" >> $output_objdir/$libname.ver~
-               $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
-           fi
-           ;;
-         *)
-           case `$CC -V 2>&1 | sed 5q` in
-           *Sun\ C*)
-             # Sun C++ 5.9
-             _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
-             _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
-             _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols'
-             _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
-             _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
-             _LT_TAGVAR(compiler_needs_object, $1)=yes
-
-             # Not sure whether something based on
-             # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1
-             # would be better.
-             output_verbose_link_cmd='echo'
-
-             # Archives containing C++ object files must be created using
-             # "CC -xar", where "CC" is the Sun C++ compiler.  This is
-             # necessary to make sure instantiated templates are included
-             # in the archive.
-             _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
-             ;;
-           esac
-           ;;
-       esac
-       ;;
-
-      lynxos*)
-        # FIXME: insert proper C++ library support
-       _LT_TAGVAR(ld_shlibs, $1)=no
-       ;;
-
-      m88k*)
-        # FIXME: insert proper C++ library support
-        _LT_TAGVAR(ld_shlibs, $1)=no
-       ;;
-
-      mvs*)
-        case $cc_basename in
-          cxx*)
-           # FIXME: insert proper C++ library support
-           _LT_TAGVAR(ld_shlibs, $1)=no
-           ;;
-         *)
-           # FIXME: insert proper C++ library support
-           _LT_TAGVAR(ld_shlibs, $1)=no
-           ;;
-       esac
-       ;;
-
-      netbsd*)
-        if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
-         _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable  -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags'
-         wlarc=
-         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
-         _LT_TAGVAR(hardcode_direct, $1)=yes
-         _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-       fi
-       # Workaround some broken pre-1.5 toolchains
-       output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"'
-       ;;
-
-      *nto* | *qnx*)
-        _LT_TAGVAR(ld_shlibs, $1)=yes
-       ;;
-
-      openbsd2*)
-        # C++ shared libraries are fairly broken
-       _LT_TAGVAR(ld_shlibs, $1)=no
-       ;;
-
-      openbsd*)
-       if test -f /usr/libexec/ld.so; then
-         _LT_TAGVAR(hardcode_direct, $1)=yes
-         _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-         _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
-         _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
-         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
-         if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
-           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib'
-           _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
-           _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
-         fi
-         output_verbose_link_cmd=echo
-       else
-         _LT_TAGVAR(ld_shlibs, $1)=no
-       fi
-       ;;
-
-      osf3* | osf4* | osf5*)
-        case $cc_basename in
-          KCC*)
-           # Kuck and Associates, Inc. (KAI) C++ Compiler
-
-           # KCC will only create a shared library if the output file
-           # ends with ".so" (or ".sl" for HP-UX), so rename the library
-           # to its proper name (with version) after linking.
-           _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
-
-           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
-           _LT_TAGVAR(hardcode_libdir_separator, $1)=:
-
-           # Archives containing C++ object files must be created using
-           # the KAI C++ compiler.
-           case $host in
-             osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;;
-             *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;;
-           esac
-           ;;
-          RCC*)
-           # Rational C++ 2.4.1
-           # FIXME: insert proper C++ library support
-           _LT_TAGVAR(ld_shlibs, $1)=no
-           ;;
-          cxx*)
-           case $host in
-             osf3*)
-               _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
-               _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && $ECHO "X${wl}-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
-               _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
-               ;;
-             *)
-               _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
-               _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
-               _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~
-                 echo "-hidden">> $lib.exp~
-                 $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp  `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~
-                 $RM $lib.exp'
-               _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
-               ;;
-           esac
-
-           _LT_TAGVAR(hardcode_libdir_separator, $1)=:
-
-           # Commands to make compiler produce verbose output that lists
-           # what "hidden" libraries, object files and flags are used when
-           # linking a shared library.
-           #
-           # There doesn't appear to be a way to prevent this compiler from
-           # explicitly linking system object files so we need to strip them
-           # from the output so that they don't get included in the library
-           # dependencies.
-           output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
-           ;;
-         *)
-           if test "$GXX" = yes && test "$with_gnu_ld" = no; then
-             _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
-             case $host in
-               osf3*)
-                 _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
-                 ;;
-               *)
-                 _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
-                 ;;
-             esac
-
-             _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
-             _LT_TAGVAR(hardcode_libdir_separator, $1)=:
-
-             # Commands to make compiler produce verbose output that lists
-             # what "hidden" libraries, object files and flags are used when
-             # linking a shared library.
-             output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
-
-           else
-             # FIXME: insert proper C++ library support
-             _LT_TAGVAR(ld_shlibs, $1)=no
-           fi
-           ;;
-        esac
-        ;;
-
-      psos*)
-        # FIXME: insert proper C++ library support
-        _LT_TAGVAR(ld_shlibs, $1)=no
-        ;;
-
-      sunos4*)
-        case $cc_basename in
-          CC*)
-           # Sun C++ 4.x
-           # FIXME: insert proper C++ library support
-           _LT_TAGVAR(ld_shlibs, $1)=no
-           ;;
-          lcc*)
-           # Lucid
-           # FIXME: insert proper C++ library support
-           _LT_TAGVAR(ld_shlibs, $1)=no
-           ;;
-          *)
-           # FIXME: insert proper C++ library support
-           _LT_TAGVAR(ld_shlibs, $1)=no
-           ;;
-        esac
-        ;;
-
-      solaris*)
-        case $cc_basename in
-          CC*)
-           # Sun C++ 4.2, 5.x and Centerline C++
-            _LT_TAGVAR(archive_cmds_need_lc,$1)=yes
-           _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
-           _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag}  -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
-           _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
-             $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
-
-           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
-           _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-           case $host_os in
-             solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
-             *)
-               # The compiler driver will combine and reorder linker options,
-               # but understands `-z linker_flag'.
-               # Supported since Solaris 2.6 (maybe 2.5.1?)
-               _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
-               ;;
-           esac
-           _LT_TAGVAR(link_all_deplibs, $1)=yes
-
-           output_verbose_link_cmd='echo'
-
-           # Archives containing C++ object files must be created using
-           # "CC -xar", where "CC" is the Sun C++ compiler.  This is
-           # necessary to make sure instantiated templates are included
-           # in the archive.
-           _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
-           ;;
-          gcx*)
-           # Green Hills C++ Compiler
-           _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
-
-           # The C++ compiler must be used to create the archive.
-           _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs'
-           ;;
-          *)
-           # GNU C++ compiler with Solaris linker
-           if test "$GXX" = yes && test "$with_gnu_ld" = no; then
-             _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs'
-             if $CC --version | $GREP -v '^2\.7' > /dev/null; then
-               _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
-               _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
-                 $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
-
-               # Commands to make compiler produce verbose output that lists
-               # what "hidden" libraries, object files and flags are used when
-               # linking a shared library.
-               output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
-             else
-               # g++ 2.7 appears to require `-G' NOT `-shared' on this
-               # platform.
-               _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
-               _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
-                 $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
-
-               # Commands to make compiler produce verbose output that lists
-               # what "hidden" libraries, object files and flags are used when
-               # linking a shared library.
-               output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
-             fi
-
-             _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir'
-             case $host_os in
-               solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
-               *)
-                 _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
-                 ;;
-             esac
-           fi
-           ;;
-        esac
-        ;;
-
-    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
-      _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
-      _LT_TAGVAR(archive_cmds_need_lc, $1)=no
-      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-      runpath_var='LD_RUN_PATH'
-
-      case $cc_basename in
-        CC*)
-         _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
-         _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
-         ;;
-       *)
-         _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
-         _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
-         ;;
-      esac
-      ;;
-
-      sysv5* | sco3.2v5* | sco5v6*)
-       # Note: We can NOT use -z defs as we might desire, because we do not
-       # link with -lc, and that would cause any symbols used from libc to
-       # always be unresolved, which means just about no library would
-       # ever link correctly.  If we're not using GNU ld we use -z text
-       # though, which does catch some bad symbols but isn't as heavy-handed
-       # as -z defs.
-       _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
-       _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs'
-       _LT_TAGVAR(archive_cmds_need_lc, $1)=no
-       _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
-       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir'
-       _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
-       _LT_TAGVAR(link_all_deplibs, $1)=yes
-       _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
-       runpath_var='LD_RUN_PATH'
-
-       case $cc_basename in
-          CC*)
-           _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
-           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
-           ;;
-         *)
-           _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
-           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
-           ;;
-       esac
-      ;;
-
-      tandem*)
-        case $cc_basename in
-          NCC*)
-           # NonStop-UX NCC 3.20
-           # FIXME: insert proper C++ library support
-           _LT_TAGVAR(ld_shlibs, $1)=no
-           ;;
-          *)
-           # FIXME: insert proper C++ library support
-           _LT_TAGVAR(ld_shlibs, $1)=no
-           ;;
-        esac
-        ;;
-
-      vxworks*)
-        # FIXME: insert proper C++ library support
-        _LT_TAGVAR(ld_shlibs, $1)=no
-        ;;
-
-      *)
-        # FIXME: insert proper C++ library support
-        _LT_TAGVAR(ld_shlibs, $1)=no
-        ;;
-    esac
-
-    AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
-    test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
-
-    _LT_TAGVAR(GCC, $1)="$GXX"
-    _LT_TAGVAR(LD, $1)="$LD"
-
-    ## CAVEAT EMPTOR:
-    ## There is no encapsulation within the following macros, do not change
-    ## the running order or otherwise move them around unless you know exactly
-    ## what you are doing...
-    _LT_SYS_HIDDEN_LIBDEPS($1)
-    _LT_COMPILER_PIC($1)
-    _LT_COMPILER_C_O($1)
-    _LT_COMPILER_FILE_LOCKS($1)
-    _LT_LINKER_SHLIBS($1)
-    _LT_SYS_DYNAMIC_LINKER($1)
-    _LT_LINKER_HARDCODE_LIBPATH($1)
-
-    _LT_CONFIG($1)
-  fi # test -n "$compiler"
-
-  CC=$lt_save_CC
-  LDCXX=$LD
-  LD=$lt_save_LD
-  GCC=$lt_save_GCC
-  with_gnu_ld=$lt_save_with_gnu_ld
-  lt_cv_path_LDCXX=$lt_cv_path_LD
-  lt_cv_path_LD=$lt_save_path_LD
-  lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
-  lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
-fi # test "$_lt_caught_CXX_error" != yes
-
-AC_LANG_POP
-])# _LT_LANG_CXX_CONFIG
-
-
-# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME])
-# ---------------------------------
-# Figure out "hidden" library dependencies from verbose
-# compiler output when linking a shared library.
-# Parse the compiler output and extract the necessary
-# objects, libraries and library flags.
-m4_defun([_LT_SYS_HIDDEN_LIBDEPS],
-[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
-# Dependencies to place before and after the object being linked:
-_LT_TAGVAR(predep_objects, $1)=
-_LT_TAGVAR(postdep_objects, $1)=
-_LT_TAGVAR(predeps, $1)=
-_LT_TAGVAR(postdeps, $1)=
-_LT_TAGVAR(compiler_lib_search_path, $1)=
-
-dnl we can't use the lt_simple_compile_test_code here,
-dnl because it contains code intended for an executable,
-dnl not a library.  It's possible we should let each
-dnl tag define a new lt_????_link_test_code variable,
-dnl but it's only used here...
-m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF
-int a;
-void foo (void) { a = 0; }
-_LT_EOF
-], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF
-class Foo
-{
-public:
-  Foo (void) { a = 0; }
-private:
-  int a;
-};
-_LT_EOF
-], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF
-      subroutine foo
-      implicit none
-      integer*4 a
-      a=0
-      return
-      end
-_LT_EOF
-], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF
-      subroutine foo
-      implicit none
-      integer a
-      a=0
-      return
-      end
-_LT_EOF
-], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF
-public class foo {
-  private int a;
-  public void bar (void) {
-    a = 0;
-  }
-};
-_LT_EOF
-])
-dnl Parse the compiler output and extract the necessary
-dnl objects, libraries and library flags.
-if AC_TRY_EVAL(ac_compile); then
-  # Parse the compiler output and extract the necessary
-  # objects, libraries and library flags.
-
-  # Sentinel used to keep track of whether or not we are before
-  # the conftest object file.
-  pre_test_object_deps_done=no
-
-  for p in `eval "$output_verbose_link_cmd"`; do
-    case $p in
-
-    -L* | -R* | -l*)
-       # Some compilers place space between "-{L,R}" and the path.
-       # Remove the space.
-       if test $p = "-L" ||
-          test $p = "-R"; then
-        prev=$p
-        continue
-       else
-        prev=
-       fi
-
-       if test "$pre_test_object_deps_done" = no; then
-        case $p in
-        -L* | -R*)
-          # Internal compiler library paths should come after those
-          # provided the user.  The postdeps already come after the
-          # user supplied libs so there is no need to process them.
-          if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then
-            _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}"
-          else
-            _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}"
-          fi
-          ;;
-        # The "-l" case would never come before the object being
-        # linked, so don't bother handling this case.
-        esac
-       else
-        if test -z "$_LT_TAGVAR(postdeps, $1)"; then
-          _LT_TAGVAR(postdeps, $1)="${prev}${p}"
-        else
-          _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}"
-        fi
-       fi
-       ;;
-
-    *.$objext)
-       # This assumes that the test object file only shows up
-       # once in the compiler output.
-       if test "$p" = "conftest.$objext"; then
-        pre_test_object_deps_done=yes
-        continue
-       fi
-
-       if test "$pre_test_object_deps_done" = no; then
-        if test -z "$_LT_TAGVAR(predep_objects, $1)"; then
-          _LT_TAGVAR(predep_objects, $1)="$p"
-        else
-          _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p"
-        fi
-       else
-        if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then
-          _LT_TAGVAR(postdep_objects, $1)="$p"
-        else
-          _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p"
-        fi
-       fi
-       ;;
-
-    *) ;; # Ignore the rest.
-
-    esac
-  done
-
-  # Clean up.
-  rm -f a.out a.exe
-else
-  echo "libtool.m4: error: problem compiling $1 test program"
-fi
-
-$RM -f confest.$objext
-
-# PORTME: override above test on systems where it is broken
-m4_if([$1], [CXX],
-[case $host_os in
-interix[[3-9]]*)
-  # Interix 3.5 installs completely hosed .la files for C++, so rather than
-  # hack all around it, let's just trust "g++" to DTRT.
-  _LT_TAGVAR(predep_objects,$1)=
-  _LT_TAGVAR(postdep_objects,$1)=
-  _LT_TAGVAR(postdeps,$1)=
-  ;;
-
-linux*)
-  case `$CC -V 2>&1 | sed 5q` in
-  *Sun\ C*)
-    # Sun C++ 5.9
-
-    # The more standards-conforming stlport4 library is
-    # incompatible with the Cstd library. Avoid specifying
-    # it if it's in CXXFLAGS. Ignore libCrun as
-    # -library=stlport4 depends on it.
-    case " $CXX $CXXFLAGS " in
-    *" -library=stlport4 "*)
-      solaris_use_stlport4=yes
-      ;;
-    esac
-
-    if test "$solaris_use_stlport4" != yes; then
-      _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun'
-    fi
-    ;;
-  esac
-  ;;
-
-solaris*)
-  case $cc_basename in
-  CC*)
-    # The more standards-conforming stlport4 library is
-    # incompatible with the Cstd library. Avoid specifying
-    # it if it's in CXXFLAGS. Ignore libCrun as
-    # -library=stlport4 depends on it.
-    case " $CXX $CXXFLAGS " in
-    *" -library=stlport4 "*)
-      solaris_use_stlport4=yes
-      ;;
-    esac
-
-    # Adding this requires a known-good setup of shared libraries for
-    # Sun compiler versions before 5.6, else PIC objects from an old
-    # archive will be linked into the output, leading to subtle bugs.
-    if test "$solaris_use_stlport4" != yes; then
-      _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun'
-    fi
-    ;;
-  esac
-  ;;
-esac
-])
-
-case " $_LT_TAGVAR(postdeps, $1) " in
-*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;;
-esac
- _LT_TAGVAR(compiler_lib_search_dirs, $1)=
-if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then
- _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'`
-fi
-_LT_TAGDECL([], [compiler_lib_search_dirs], [1],
-    [The directories searched by this compiler when creating a shared library])
-_LT_TAGDECL([], [predep_objects], [1],
-    [Dependencies to place before and after the objects being linked to
-    create a shared library])
-_LT_TAGDECL([], [postdep_objects], [1])
-_LT_TAGDECL([], [predeps], [1])
-_LT_TAGDECL([], [postdeps], [1])
-_LT_TAGDECL([], [compiler_lib_search_path], [1],
-    [The library search path used internally by the compiler when linking
-    a shared library])
-])# _LT_SYS_HIDDEN_LIBDEPS
-
-
-# _LT_PROG_F77
-# ------------
-# Since AC_PROG_F77 is broken, in that it returns the empty string
-# if there is no fortran compiler, we have our own version here.
-m4_defun([_LT_PROG_F77],
-[
-pushdef([AC_MSG_ERROR], [_lt_disable_F77=yes])
-AC_PROG_F77
-if test -z "$F77" || test "X$F77" = "Xno"; then
-  _lt_disable_F77=yes
-fi
-popdef([AC_MSG_ERROR])
-])# _LT_PROG_F77
-
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([_LT_PROG_F77], [])
-
-
-# _LT_LANG_F77_CONFIG([TAG])
-# --------------------------
-# Ensure that the configuration variables for a Fortran 77 compiler are
-# suitably defined.  These variables are subsequently used by _LT_CONFIG
-# to write the compiler configuration to `libtool'.
-m4_defun([_LT_LANG_F77_CONFIG],
-[AC_REQUIRE([_LT_PROG_F77])dnl
-AC_LANG_PUSH(Fortran 77)
-
-_LT_TAGVAR(archive_cmds_need_lc, $1)=no
-_LT_TAGVAR(allow_undefined_flag, $1)=
-_LT_TAGVAR(always_export_symbols, $1)=no
-_LT_TAGVAR(archive_expsym_cmds, $1)=
-_LT_TAGVAR(export_dynamic_flag_spec, $1)=
-_LT_TAGVAR(hardcode_direct, $1)=no
-_LT_TAGVAR(hardcode_direct_absolute, $1)=no
-_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
-_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
-_LT_TAGVAR(hardcode_libdir_separator, $1)=
-_LT_TAGVAR(hardcode_minus_L, $1)=no
-_LT_TAGVAR(hardcode_automatic, $1)=no
-_LT_TAGVAR(inherit_rpath, $1)=no
-_LT_TAGVAR(module_cmds, $1)=
-_LT_TAGVAR(module_expsym_cmds, $1)=
-_LT_TAGVAR(link_all_deplibs, $1)=unknown
-_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
-_LT_TAGVAR(no_undefined_flag, $1)=
-_LT_TAGVAR(whole_archive_flag_spec, $1)=
-_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
-
-# Source file extension for f77 test sources.
-ac_ext=f
-
-# Object file extension for compiled f77 test sources.
-objext=o
-_LT_TAGVAR(objext, $1)=$objext
-
-# No sense in running all these tests if we already determined that
-# the F77 compiler isn't working.  Some variables (like enable_shared)
-# are currently assumed to apply to all compilers on this platform,
-# and will be corrupted by setting them based on a non-working compiler.
-if test "$_lt_disable_F77" != yes; then
-  # Code to be used in simple compile tests
-  lt_simple_compile_test_code="\
-      subroutine t
-      return
-      end
-"
-
-  # Code to be used in simple link tests
-  lt_simple_link_test_code="\
-      program t
-      end
-"
-
-  # ltmain only uses $CC for tagged configurations so make sure $CC is set.
-  _LT_TAG_COMPILER
-
-  # save warnings/boilerplate of simple test code
-  _LT_COMPILER_BOILERPLATE
-  _LT_LINKER_BOILERPLATE
-
-  # Allow CC to be a program name with arguments.
-  lt_save_CC="$CC"
-  lt_save_GCC=$GCC
-  CC=${F77-"f77"}
-  compiler=$CC
-  _LT_TAGVAR(compiler, $1)=$CC
-  _LT_CC_BASENAME([$compiler])
-  GCC=$G77
-  if test -n "$compiler"; then
-    AC_MSG_CHECKING([if libtool supports shared libraries])
-    AC_MSG_RESULT([$can_build_shared])
-
-    AC_MSG_CHECKING([whether to build shared libraries])
-    test "$can_build_shared" = "no" && enable_shared=no
-
-    # On AIX, shared libraries and static libraries use the same namespace, and
-    # are all built from PIC.
-    case $host_os in
-      aix3*)
-        test "$enable_shared" = yes && enable_static=no
-        if test -n "$RANLIB"; then
-          archive_cmds="$archive_cmds~\$RANLIB \$lib"
-          postinstall_cmds='$RANLIB $lib'
-        fi
-        ;;
-      aix[[4-9]]*)
-       if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
-         test "$enable_shared" = yes && enable_static=no
-       fi
-        ;;
-    esac
-    AC_MSG_RESULT([$enable_shared])
-
-    AC_MSG_CHECKING([whether to build static libraries])
-    # Make sure either enable_shared or enable_static is yes.
-    test "$enable_shared" = yes || enable_static=yes
-    AC_MSG_RESULT([$enable_static])
-
-    _LT_TAGVAR(GCC, $1)="$G77"
-    _LT_TAGVAR(LD, $1)="$LD"
-
-    ## CAVEAT EMPTOR:
-    ## There is no encapsulation within the following macros, do not change
-    ## the running order or otherwise move them around unless you know exactly
-    ## what you are doing...
-    _LT_COMPILER_PIC($1)
-    _LT_COMPILER_C_O($1)
-    _LT_COMPILER_FILE_LOCKS($1)
-    _LT_LINKER_SHLIBS($1)
-    _LT_SYS_DYNAMIC_LINKER($1)
-    _LT_LINKER_HARDCODE_LIBPATH($1)
-
-    _LT_CONFIG($1)
-  fi # test -n "$compiler"
-
-  GCC=$lt_save_GCC
-  CC="$lt_save_CC"
-fi # test "$_lt_disable_F77" != yes
-
-AC_LANG_POP
-])# _LT_LANG_F77_CONFIG
-
-
-# _LT_PROG_FC
-# -----------
-# Since AC_PROG_FC is broken, in that it returns the empty string
-# if there is no fortran compiler, we have our own version here.
-m4_defun([_LT_PROG_FC],
-[
-pushdef([AC_MSG_ERROR], [_lt_disable_FC=yes])
-AC_PROG_FC
-if test -z "$FC" || test "X$FC" = "Xno"; then
-  _lt_disable_FC=yes
-fi
-popdef([AC_MSG_ERROR])
-])# _LT_PROG_FC
-
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([_LT_PROG_FC], [])
-
-
-# _LT_LANG_FC_CONFIG([TAG])
-# -------------------------
-# Ensure that the configuration variables for a Fortran compiler are
-# suitably defined.  These variables are subsequently used by _LT_CONFIG
-# to write the compiler configuration to `libtool'.
-m4_defun([_LT_LANG_FC_CONFIG],
-[AC_REQUIRE([_LT_PROG_FC])dnl
-AC_LANG_PUSH(Fortran)
-
-_LT_TAGVAR(archive_cmds_need_lc, $1)=no
-_LT_TAGVAR(allow_undefined_flag, $1)=
-_LT_TAGVAR(always_export_symbols, $1)=no
-_LT_TAGVAR(archive_expsym_cmds, $1)=
-_LT_TAGVAR(export_dynamic_flag_spec, $1)=
-_LT_TAGVAR(hardcode_direct, $1)=no
-_LT_TAGVAR(hardcode_direct_absolute, $1)=no
-_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
-_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
-_LT_TAGVAR(hardcode_libdir_separator, $1)=
-_LT_TAGVAR(hardcode_minus_L, $1)=no
-_LT_TAGVAR(hardcode_automatic, $1)=no
-_LT_TAGVAR(inherit_rpath, $1)=no
-_LT_TAGVAR(module_cmds, $1)=
-_LT_TAGVAR(module_expsym_cmds, $1)=
-_LT_TAGVAR(link_all_deplibs, $1)=unknown
-_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
-_LT_TAGVAR(no_undefined_flag, $1)=
-_LT_TAGVAR(whole_archive_flag_spec, $1)=
-_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
-
-# Source file extension for fc test sources.
-ac_ext=${ac_fc_srcext-f}
-
-# Object file extension for compiled fc test sources.
-objext=o
-_LT_TAGVAR(objext, $1)=$objext
-
-# No sense in running all these tests if we already determined that
-# the FC compiler isn't working.  Some variables (like enable_shared)
-# are currently assumed to apply to all compilers on this platform,
-# and will be corrupted by setting them based on a non-working compiler.
-if test "$_lt_disable_FC" != yes; then
-  # Code to be used in simple compile tests
-  lt_simple_compile_test_code="\
-      subroutine t
-      return
-      end
-"
-
-  # Code to be used in simple link tests
-  lt_simple_link_test_code="\
-      program t
-      end
-"
-
-  # ltmain only uses $CC for tagged configurations so make sure $CC is set.
-  _LT_TAG_COMPILER
-
-  # save warnings/boilerplate of simple test code
-  _LT_COMPILER_BOILERPLATE
-  _LT_LINKER_BOILERPLATE
-
-  # Allow CC to be a program name with arguments.
-  lt_save_CC="$CC"
-  lt_save_GCC=$GCC
-  CC=${FC-"f95"}
-  compiler=$CC
-  GCC=$ac_cv_fc_compiler_gnu
-
-  _LT_TAGVAR(compiler, $1)=$CC
-  _LT_CC_BASENAME([$compiler])
-
-  if test -n "$compiler"; then
-    AC_MSG_CHECKING([if libtool supports shared libraries])
-    AC_MSG_RESULT([$can_build_shared])
-
-    AC_MSG_CHECKING([whether to build shared libraries])
-    test "$can_build_shared" = "no" && enable_shared=no
-
-    # On AIX, shared libraries and static libraries use the same namespace, and
-    # are all built from PIC.
-    case $host_os in
-      aix3*)
-        test "$enable_shared" = yes && enable_static=no
-        if test -n "$RANLIB"; then
-          archive_cmds="$archive_cmds~\$RANLIB \$lib"
-          postinstall_cmds='$RANLIB $lib'
-        fi
-        ;;
-      aix[[4-9]]*)
-       if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
-         test "$enable_shared" = yes && enable_static=no
-       fi
-        ;;
-    esac
-    AC_MSG_RESULT([$enable_shared])
-
-    AC_MSG_CHECKING([whether to build static libraries])
-    # Make sure either enable_shared or enable_static is yes.
-    test "$enable_shared" = yes || enable_static=yes
-    AC_MSG_RESULT([$enable_static])
-
-    _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu"
-    _LT_TAGVAR(LD, $1)="$LD"
-
-    ## CAVEAT EMPTOR:
-    ## There is no encapsulation within the following macros, do not change
-    ## the running order or otherwise move them around unless you know exactly
-    ## what you are doing...
-    _LT_SYS_HIDDEN_LIBDEPS($1)
-    _LT_COMPILER_PIC($1)
-    _LT_COMPILER_C_O($1)
-    _LT_COMPILER_FILE_LOCKS($1)
-    _LT_LINKER_SHLIBS($1)
-    _LT_SYS_DYNAMIC_LINKER($1)
-    _LT_LINKER_HARDCODE_LIBPATH($1)
-
-    _LT_CONFIG($1)
-  fi # test -n "$compiler"
-
-  GCC=$lt_save_GCC
-  CC="$lt_save_CC"
-fi # test "$_lt_disable_FC" != yes
-
-AC_LANG_POP
-])# _LT_LANG_FC_CONFIG
-
-
-# _LT_LANG_GCJ_CONFIG([TAG])
-# --------------------------
-# Ensure that the configuration variables for the GNU Java Compiler compiler
-# are suitably defined.  These variables are subsequently used by _LT_CONFIG
-# to write the compiler configuration to `libtool'.
-m4_defun([_LT_LANG_GCJ_CONFIG],
-[AC_REQUIRE([LT_PROG_GCJ])dnl
-AC_LANG_SAVE
-
-# Source file extension for Java test sources.
-ac_ext=java
-
-# Object file extension for compiled Java test sources.
-objext=o
-_LT_TAGVAR(objext, $1)=$objext
-
-# Code to be used in simple compile tests
-lt_simple_compile_test_code="class foo {}"
-
-# Code to be used in simple link tests
-lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }'
-
-# ltmain only uses $CC for tagged configurations so make sure $CC is set.
-_LT_TAG_COMPILER
-
-# save warnings/boilerplate of simple test code
-_LT_COMPILER_BOILERPLATE
-_LT_LINKER_BOILERPLATE
-
-# Allow CC to be a program name with arguments.
-lt_save_CC="$CC"
-lt_save_GCC=$GCC
-GCC=yes
-CC=${GCJ-"gcj"}
-compiler=$CC
-_LT_TAGVAR(compiler, $1)=$CC
-_LT_TAGVAR(LD, $1)="$LD"
-_LT_CC_BASENAME([$compiler])
-
-# GCJ did not exist at the time GCC didn't implicitly link libc in.
-_LT_TAGVAR(archive_cmds_need_lc, $1)=no
-
-_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
-
-## CAVEAT EMPTOR:
-## There is no encapsulation within the following macros, do not change
-## the running order or otherwise move them around unless you know exactly
-## what you are doing...
-if test -n "$compiler"; then
-  _LT_COMPILER_NO_RTTI($1)
-  _LT_COMPILER_PIC($1)
-  _LT_COMPILER_C_O($1)
-  _LT_COMPILER_FILE_LOCKS($1)
-  _LT_LINKER_SHLIBS($1)
-  _LT_LINKER_HARDCODE_LIBPATH($1)
-
-  _LT_CONFIG($1)
-fi
-
-AC_LANG_RESTORE
-
-GCC=$lt_save_GCC
-CC="$lt_save_CC"
-])# _LT_LANG_GCJ_CONFIG
-
-
-# _LT_LANG_RC_CONFIG([TAG])
-# -------------------------
-# Ensure that the configuration variables for the Windows resource compiler
-# are suitably defined.  These variables are subsequently used by _LT_CONFIG
-# to write the compiler configuration to `libtool'.
-m4_defun([_LT_LANG_RC_CONFIG],
-[AC_REQUIRE([LT_PROG_RC])dnl
-AC_LANG_SAVE
-
-# Source file extension for RC test sources.
-ac_ext=rc
-
-# Object file extension for compiled RC test sources.
-objext=o
-_LT_TAGVAR(objext, $1)=$objext
-
-# Code to be used in simple compile tests
-lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }'
-
-# Code to be used in simple link tests
-lt_simple_link_test_code="$lt_simple_compile_test_code"
-
-# ltmain only uses $CC for tagged configurations so make sure $CC is set.
-_LT_TAG_COMPILER
-
-# save warnings/boilerplate of simple test code
-_LT_COMPILER_BOILERPLATE
-_LT_LINKER_BOILERPLATE
-
-# Allow CC to be a program name with arguments.
-lt_save_CC="$CC"
-lt_save_GCC=$GCC
-GCC=
-CC=${RC-"windres"}
-compiler=$CC
-_LT_TAGVAR(compiler, $1)=$CC
-_LT_CC_BASENAME([$compiler])
-_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
-
-if test -n "$compiler"; then
-  :
-  _LT_CONFIG($1)
-fi
-
-GCC=$lt_save_GCC
-AC_LANG_RESTORE
-CC="$lt_save_CC"
-])# _LT_LANG_RC_CONFIG
-
-
-# LT_PROG_GCJ
-# -----------
-AC_DEFUN([LT_PROG_GCJ],
-[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ],
-  [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ],
-    [AC_CHECK_TOOL(GCJ, gcj,)
-      test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2"
-      AC_SUBST(GCJFLAGS)])])[]dnl
-])
-
-# Old name:
-AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ])
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([LT_AC_PROG_GCJ], [])
-
-
-# LT_PROG_RC
-# ----------
-AC_DEFUN([LT_PROG_RC],
-[AC_CHECK_TOOL(RC, windres,)
-])
-
-# Old name:
-AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC])
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([LT_AC_PROG_RC], [])
-
-
-# _LT_DECL_EGREP
-# --------------
-# If we don't have a new enough Autoconf to choose the best grep
-# available, choose the one first in the user's PATH.
-m4_defun([_LT_DECL_EGREP],
-[AC_REQUIRE([AC_PROG_EGREP])dnl
-AC_REQUIRE([AC_PROG_FGREP])dnl
-test -z "$GREP" && GREP=grep
-_LT_DECL([], [GREP], [1], [A grep program that handles long lines])
-_LT_DECL([], [EGREP], [1], [An ERE matcher])
-_LT_DECL([], [FGREP], [1], [A literal string matcher])
-dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too
-AC_SUBST([GREP])
-])
-
-
-# _LT_DECL_OBJDUMP
-# --------------
-# If we don't have a new enough Autoconf to choose the best objdump
-# available, choose the one first in the user's PATH.
-m4_defun([_LT_DECL_OBJDUMP],
-[AC_CHECK_TOOL(OBJDUMP, objdump, false)
-test -z "$OBJDUMP" && OBJDUMP=objdump
-_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper])
-AC_SUBST([OBJDUMP])
-])
-
-
-# _LT_DECL_SED
-# ------------
-# Check for a fully-functional sed program, that truncates
-# as few characters as possible.  Prefer GNU sed if found.
-m4_defun([_LT_DECL_SED],
-[AC_PROG_SED
-test -z "$SED" && SED=sed
-Xsed="$SED -e 1s/^X//"
-_LT_DECL([], [SED], [1], [A sed program that does not truncate output])
-_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"],
-    [Sed that helps us avoid accidentally triggering echo(1) options like -n])
-])# _LT_DECL_SED
-
-m4_ifndef([AC_PROG_SED], [
-############################################################
-# NOTE: This macro has been submitted for inclusion into   #
-#  GNU Autoconf as AC_PROG_SED.  When it is available in   #
-#  a released version of Autoconf we should remove this    #
-#  macro and use it instead.                               #
-############################################################
-
-m4_defun([AC_PROG_SED],
-[AC_MSG_CHECKING([for a sed that does not truncate output])
-AC_CACHE_VAL(lt_cv_path_SED,
-[# Loop through the user's path and test for sed and gsed.
-# Then use that list of sed's as ones to test for truncation.
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  for lt_ac_prog in sed gsed; do
-    for ac_exec_ext in '' $ac_executable_extensions; do
-      if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then
-        lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext"
-      fi
-    done
-  done
-done
-IFS=$as_save_IFS
-lt_ac_max=0
-lt_ac_count=0
-# Add /usr/xpg4/bin/sed as it is typically found on Solaris
-# along with /bin/sed that truncates output.
-for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
-  test ! -f $lt_ac_sed && continue
-  cat /dev/null > conftest.in
-  lt_ac_count=0
-  echo $ECHO_N "0123456789$ECHO_C" >conftest.in
-  # Check for GNU sed and select it if it is found.
-  if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then
-    lt_cv_path_SED=$lt_ac_sed
-    break
-  fi
-  while true; do
-    cat conftest.in conftest.in >conftest.tmp
-    mv conftest.tmp conftest.in
-    cp conftest.in conftest.nl
-    echo >>conftest.nl
-    $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break
-    cmp -s conftest.out conftest.nl || break
-    # 10000 chars as input seems more than enough
-    test $lt_ac_count -gt 10 && break
-    lt_ac_count=`expr $lt_ac_count + 1`
-    if test $lt_ac_count -gt $lt_ac_max; then
-      lt_ac_max=$lt_ac_count
-      lt_cv_path_SED=$lt_ac_sed
-    fi
-  done
-done
-])
-SED=$lt_cv_path_SED
-AC_SUBST([SED])
-AC_MSG_RESULT([$SED])
-])#AC_PROG_SED
-])#m4_ifndef
-
-# Old name:
-AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED])
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([LT_AC_PROG_SED], [])
-
-
-# _LT_CHECK_SHELL_FEATURES
-# ------------------------
-# Find out whether the shell is Bourne or XSI compatible,
-# or has some other useful features.
-m4_defun([_LT_CHECK_SHELL_FEATURES],
-[AC_MSG_CHECKING([whether the shell understands some XSI constructs])
-# Try some XSI features
-xsi_shell=no
-( _lt_dummy="a/b/c"
-  test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \
-      = c,a/b,, \
-    && eval 'test $(( 1 + 1 )) -eq 2 \
-    && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
-  && xsi_shell=yes
-AC_MSG_RESULT([$xsi_shell])
-_LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell'])
-
-AC_MSG_CHECKING([whether the shell understands "+="])
-lt_shell_append=no
-( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \
-    >/dev/null 2>&1 \
-  && lt_shell_append=yes
-AC_MSG_RESULT([$lt_shell_append])
-_LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append'])
-
-if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
-  lt_unset=unset
-else
-  lt_unset=false
-fi
-_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl
-
-# test EBCDIC or ASCII
-case `echo X|tr X '\101'` in
- A) # ASCII based system
-    # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
-  lt_SP2NL='tr \040 \012'
-  lt_NL2SP='tr \015\012 \040\040'
-  ;;
- *) # EBCDIC based system
-  lt_SP2NL='tr \100 \n'
-  lt_NL2SP='tr \r\n \100\100'
-  ;;
-esac
-_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl
-_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl
-])# _LT_CHECK_SHELL_FEATURES
-
-
-# _LT_PROG_XSI_SHELLFNS
-# ---------------------
-# Bourne and XSI compatible variants of some useful shell functions.
-m4_defun([_LT_PROG_XSI_SHELLFNS],
-[case $xsi_shell in
-  yes)
-    cat << \_LT_EOF >> "$cfgfile"
-
-# func_dirname file append nondir_replacement
-# Compute the dirname of FILE.  If nonempty, add APPEND to the result,
-# otherwise set result to NONDIR_REPLACEMENT.
-func_dirname ()
-{
-  case ${1} in
-    */*) func_dirname_result="${1%/*}${2}" ;;
-    *  ) func_dirname_result="${3}" ;;
-  esac
-}
-
-# func_basename file
-func_basename ()
-{
-  func_basename_result="${1##*/}"
-}
-
-# func_dirname_and_basename file append nondir_replacement
-# perform func_basename and func_dirname in a single function
-# call:
-#   dirname:  Compute the dirname of FILE.  If nonempty,
-#             add APPEND to the result, otherwise set result
-#             to NONDIR_REPLACEMENT.
-#             value returned in "$func_dirname_result"
-#   basename: Compute filename of FILE.
-#             value retuned in "$func_basename_result"
-# Implementation must be kept synchronized with func_dirname
-# and func_basename. For efficiency, we do not delegate to
-# those functions but instead duplicate the functionality here.
-func_dirname_and_basename ()
-{
-  case ${1} in
-    */*) func_dirname_result="${1%/*}${2}" ;;
-    *  ) func_dirname_result="${3}" ;;
-  esac
-  func_basename_result="${1##*/}"
-}
-
-# func_stripname prefix suffix name
-# strip PREFIX and SUFFIX off of NAME.
-# PREFIX and SUFFIX must not contain globbing or regex special
-# characters, hashes, percent signs, but SUFFIX may contain a leading
-# dot (in which case that matches only a dot).
-func_stripname ()
-{
-  # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
-  # positional parameters, so assign one to ordinary parameter first.
-  func_stripname_result=${3}
-  func_stripname_result=${func_stripname_result#"${1}"}
-  func_stripname_result=${func_stripname_result%"${2}"}
-}
-
-# func_opt_split
-func_opt_split ()
-{
-  func_opt_split_opt=${1%%=*}
-  func_opt_split_arg=${1#*=}
-}
-
-# func_lo2o object
-func_lo2o ()
-{
-  case ${1} in
-    *.lo) func_lo2o_result=${1%.lo}.${objext} ;;
-    *)    func_lo2o_result=${1} ;;
-  esac
-}
-
-# func_xform libobj-or-source
-func_xform ()
-{
-  func_xform_result=${1%.*}.lo
-}
-
-# func_arith arithmetic-term...
-func_arith ()
-{
-  func_arith_result=$(( $[*] ))
-}
-
-# func_len string
-# STRING may not start with a hyphen.
-func_len ()
-{
-  func_len_result=${#1}
-}
-
-_LT_EOF
-    ;;
-  *) # Bourne compatible functions.
-    cat << \_LT_EOF >> "$cfgfile"
-
-# func_dirname file append nondir_replacement
-# Compute the dirname of FILE.  If nonempty, add APPEND to the result,
-# otherwise set result to NONDIR_REPLACEMENT.
-func_dirname ()
-{
-  # Extract subdirectory from the argument.
-  func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"`
-  if test "X$func_dirname_result" = "X${1}"; then
-    func_dirname_result="${3}"
-  else
-    func_dirname_result="$func_dirname_result${2}"
-  fi
-}
-
-# func_basename file
-func_basename ()
-{
-  func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"`
-}
-
-dnl func_dirname_and_basename
-dnl A portable version of this function is already defined in general.m4sh
-dnl so there is no need for it here.
-
-# func_stripname prefix suffix name
-# strip PREFIX and SUFFIX off of NAME.
-# PREFIX and SUFFIX must not contain globbing or regex special
-# characters, hashes, percent signs, but SUFFIX may contain a leading
-# dot (in which case that matches only a dot).
-# func_strip_suffix prefix name
-func_stripname ()
-{
-  case ${2} in
-    .*) func_stripname_result=`$ECHO "X${3}" \
-           | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;;
-    *)  func_stripname_result=`$ECHO "X${3}" \
-           | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;;
-  esac
-}
-
-# sed scripts:
-my_sed_long_opt='1s/^\(-[[^=]]*\)=.*/\1/;q'
-my_sed_long_arg='1s/^-[[^=]]*=//'
-
-# func_opt_split
-func_opt_split ()
-{
-  func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"`
-  func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"`
-}
-
-# func_lo2o object
-func_lo2o ()
-{
-  func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"`
-}
-
-# func_xform libobj-or-source
-func_xform ()
-{
-  func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[[^.]]*$/.lo/'`
-}
-
-# func_arith arithmetic-term...
-func_arith ()
-{
-  func_arith_result=`expr "$[@]"`
-}
-
-# func_len string
-# STRING may not start with a hyphen.
-func_len ()
-{
-  func_len_result=`expr "$[1]" : ".*" 2>/dev/null || echo $max_cmd_len`
-}
-
-_LT_EOF
-esac
-
-case $lt_shell_append in
-  yes)
-    cat << \_LT_EOF >> "$cfgfile"
-
-# func_append var value
-# Append VALUE to the end of shell variable VAR.
-func_append ()
-{
-  eval "$[1]+=\$[2]"
-}
-_LT_EOF
-    ;;
-  *)
-    cat << \_LT_EOF >> "$cfgfile"
-
-# func_append var value
-# Append VALUE to the end of shell variable VAR.
-func_append ()
-{
-  eval "$[1]=\$$[1]\$[2]"
-}
-
-_LT_EOF
-    ;;
-  esac
-])
diff --git a/m4/ltoptions.m4 b/m4/ltoptions.m4
deleted file mode 100644 (file)
index 34151a3..0000000
+++ /dev/null
@@ -1,368 +0,0 @@
-# Helper functions for option handling.                    -*- Autoconf -*-
-#
-#   Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
-#   Written by Gary V. Vaughan, 2004
-#
-# This file is free software; the Free Software Foundation gives
-# unlimited permission to copy and/or distribute it, with or without
-# modifications, as long as this notice is preserved.
-
-# serial 6 ltoptions.m4
-
-# This is to help aclocal find these macros, as it can't see m4_define.
-AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
-
-
-# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
-# ------------------------------------------
-m4_define([_LT_MANGLE_OPTION],
-[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
-
-
-# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
-# ---------------------------------------
-# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
-# matching handler defined, dispatch to it.  Other OPTION-NAMEs are
-# saved as a flag.
-m4_define([_LT_SET_OPTION],
-[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
-m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
-        _LT_MANGLE_DEFUN([$1], [$2]),
-    [m4_warning([Unknown $1 option `$2'])])[]dnl
-])
-
-
-# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
-# ------------------------------------------------------------
-# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
-m4_define([_LT_IF_OPTION],
-[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
-
-
-# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
-# -------------------------------------------------------
-# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
-# are set.
-m4_define([_LT_UNLESS_OPTIONS],
-[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
-           [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
-                     [m4_define([$0_found])])])[]dnl
-m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
-])[]dnl
-])
-
-
-# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
-# ----------------------------------------
-# OPTION-LIST is a space-separated list of Libtool options associated
-# with MACRO-NAME.  If any OPTION has a matching handler declared with
-# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
-# the unknown option and exit.
-m4_defun([_LT_SET_OPTIONS],
-[# Set options
-m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
-    [_LT_SET_OPTION([$1], _LT_Option)])
-
-m4_if([$1],[LT_INIT],[
-  dnl
-  dnl Simply set some default values (i.e off) if boolean options were not
-  dnl specified:
-  _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
-  ])
-  _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
-  ])
-  dnl
-  dnl If no reference was made to various pairs of opposing options, then
-  dnl we run the default mode handler for the pair.  For example, if neither
-  dnl `shared' nor `disable-shared' was passed, we enable building of shared
-  dnl archives by default:
-  _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
-  _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
-  _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
-  _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
-                  [_LT_ENABLE_FAST_INSTALL])
-  ])
-])# _LT_SET_OPTIONS
-
-
-## --------------------------------- ##
-## Macros to handle LT_INIT options. ##
-## --------------------------------- ##
-
-# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
-# -----------------------------------------
-m4_define([_LT_MANGLE_DEFUN],
-[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
-
-
-# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
-# -----------------------------------------------
-m4_define([LT_OPTION_DEFINE],
-[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
-])# LT_OPTION_DEFINE
-
-
-# dlopen
-# ------
-LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
-])
-
-AU_DEFUN([AC_LIBTOOL_DLOPEN],
-[_LT_SET_OPTION([LT_INIT], [dlopen])
-AC_DIAGNOSE([obsolete],
-[$0: Remove this warning and the call to _LT_SET_OPTION when you
-put the `dlopen' option into LT_INIT's first parameter.])
-])
-
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
-
-
-# win32-dll
-# ---------
-# Declare package support for building win32 dll's.
-LT_OPTION_DEFINE([LT_INIT], [win32-dll],
-[enable_win32_dll=yes
-
-case $host in
-*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-cegcc*)
-  AC_CHECK_TOOL(AS, as, false)
-  AC_CHECK_TOOL(DLLTOOL, dlltool, false)
-  AC_CHECK_TOOL(OBJDUMP, objdump, false)
-  ;;
-esac
-
-test -z "$AS" && AS=as
-_LT_DECL([], [AS],      [0], [Assembler program])dnl
-
-test -z "$DLLTOOL" && DLLTOOL=dlltool
-_LT_DECL([], [DLLTOOL], [0], [DLL creation program])dnl
-
-test -z "$OBJDUMP" && OBJDUMP=objdump
-_LT_DECL([], [OBJDUMP], [0], [Object dumper program])dnl
-])# win32-dll
-
-AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
-[AC_REQUIRE([AC_CANONICAL_HOST])dnl
-_LT_SET_OPTION([LT_INIT], [win32-dll])
-AC_DIAGNOSE([obsolete],
-[$0: Remove this warning and the call to _LT_SET_OPTION when you
-put the `win32-dll' option into LT_INIT's first parameter.])
-])
-
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
-
-
-# _LT_ENABLE_SHARED([DEFAULT])
-# ----------------------------
-# implement the --enable-shared flag, and supports the `shared' and
-# `disable-shared' LT_INIT options.
-# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
-m4_define([_LT_ENABLE_SHARED],
-[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
-AC_ARG_ENABLE([shared],
-    [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
-       [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
-    [p=${PACKAGE-default}
-    case $enableval in
-    yes) enable_shared=yes ;;
-    no) enable_shared=no ;;
-    *)
-      enable_shared=no
-      # Look at the argument we got.  We use all the common list separators.
-      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
-      for pkg in $enableval; do
-       IFS="$lt_save_ifs"
-       if test "X$pkg" = "X$p"; then
-         enable_shared=yes
-       fi
-      done
-      IFS="$lt_save_ifs"
-      ;;
-    esac],
-    [enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
-
-    _LT_DECL([build_libtool_libs], [enable_shared], [0],
-       [Whether or not to build shared libraries])
-])# _LT_ENABLE_SHARED
-
-LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
-LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
-
-# Old names:
-AC_DEFUN([AC_ENABLE_SHARED],
-[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
-])
-
-AC_DEFUN([AC_DISABLE_SHARED],
-[_LT_SET_OPTION([LT_INIT], [disable-shared])
-])
-
-AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
-AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
-
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([AM_ENABLE_SHARED], [])
-dnl AC_DEFUN([AM_DISABLE_SHARED], [])
-
-
-
-# _LT_ENABLE_STATIC([DEFAULT])
-# ----------------------------
-# implement the --enable-static flag, and support the `static' and
-# `disable-static' LT_INIT options.
-# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
-m4_define([_LT_ENABLE_STATIC],
-[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
-AC_ARG_ENABLE([static],
-    [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
-       [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
-    [p=${PACKAGE-default}
-    case $enableval in
-    yes) enable_static=yes ;;
-    no) enable_static=no ;;
-    *)
-     enable_static=no
-      # Look at the argument we got.  We use all the common list separators.
-      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
-      for pkg in $enableval; do
-       IFS="$lt_save_ifs"
-       if test "X$pkg" = "X$p"; then
-         enable_static=yes
-       fi
-      done
-      IFS="$lt_save_ifs"
-      ;;
-    esac],
-    [enable_static=]_LT_ENABLE_STATIC_DEFAULT)
-
-    _LT_DECL([build_old_libs], [enable_static], [0],
-       [Whether or not to build static libraries])
-])# _LT_ENABLE_STATIC
-
-LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
-LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
-
-# Old names:
-AC_DEFUN([AC_ENABLE_STATIC],
-[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
-])
-
-AC_DEFUN([AC_DISABLE_STATIC],
-[_LT_SET_OPTION([LT_INIT], [disable-static])
-])
-
-AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
-AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
-
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([AM_ENABLE_STATIC], [])
-dnl AC_DEFUN([AM_DISABLE_STATIC], [])
-
-
-
-# _LT_ENABLE_FAST_INSTALL([DEFAULT])
-# ----------------------------------
-# implement the --enable-fast-install flag, and support the `fast-install'
-# and `disable-fast-install' LT_INIT options.
-# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
-m4_define([_LT_ENABLE_FAST_INSTALL],
-[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
-AC_ARG_ENABLE([fast-install],
-    [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
-    [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
-    [p=${PACKAGE-default}
-    case $enableval in
-    yes) enable_fast_install=yes ;;
-    no) enable_fast_install=no ;;
-    *)
-      enable_fast_install=no
-      # Look at the argument we got.  We use all the common list separators.
-      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
-      for pkg in $enableval; do
-       IFS="$lt_save_ifs"
-       if test "X$pkg" = "X$p"; then
-         enable_fast_install=yes
-       fi
-      done
-      IFS="$lt_save_ifs"
-      ;;
-    esac],
-    [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
-
-_LT_DECL([fast_install], [enable_fast_install], [0],
-        [Whether or not to optimize for fast installation])dnl
-])# _LT_ENABLE_FAST_INSTALL
-
-LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
-LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
-
-# Old names:
-AU_DEFUN([AC_ENABLE_FAST_INSTALL],
-[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
-AC_DIAGNOSE([obsolete],
-[$0: Remove this warning and the call to _LT_SET_OPTION when you put
-the `fast-install' option into LT_INIT's first parameter.])
-])
-
-AU_DEFUN([AC_DISABLE_FAST_INSTALL],
-[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
-AC_DIAGNOSE([obsolete],
-[$0: Remove this warning and the call to _LT_SET_OPTION when you put
-the `disable-fast-install' option into LT_INIT's first parameter.])
-])
-
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
-dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
-
-
-# _LT_WITH_PIC([MODE])
-# --------------------
-# implement the --with-pic flag, and support the `pic-only' and `no-pic'
-# LT_INIT options.
-# MODE is either `yes' or `no'.  If omitted, it defaults to `both'.
-m4_define([_LT_WITH_PIC],
-[AC_ARG_WITH([pic],
-    [AS_HELP_STRING([--with-pic],
-       [try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
-    [pic_mode="$withval"],
-    [pic_mode=default])
-
-test -z "$pic_mode" && pic_mode=m4_default([$1], [default])
-
-_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
-])# _LT_WITH_PIC
-
-LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
-LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
-
-# Old name:
-AU_DEFUN([AC_LIBTOOL_PICMODE],
-[_LT_SET_OPTION([LT_INIT], [pic-only])
-AC_DIAGNOSE([obsolete],
-[$0: Remove this warning and the call to _LT_SET_OPTION when you
-put the `pic-only' option into LT_INIT's first parameter.])
-])
-
-dnl aclocal-1.4 backwards compatibility:
-dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
-
-## ----------------- ##
-## LTDL_INIT Options ##
-## ----------------- ##
-
-m4_define([_LTDL_MODE], [])
-LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
-                [m4_define([_LTDL_MODE], [nonrecursive])])
-LT_OPTION_DEFINE([LTDL_INIT], [recursive],
-                [m4_define([_LTDL_MODE], [recursive])])
-LT_OPTION_DEFINE([LTDL_INIT], [subproject],
-                [m4_define([_LTDL_MODE], [subproject])])
-
-m4_define([_LTDL_TYPE], [])
-LT_OPTION_DEFINE([LTDL_INIT], [installable],
-                [m4_define([_LTDL_TYPE], [installable])])
-LT_OPTION_DEFINE([LTDL_INIT], [convenience],
-                [m4_define([_LTDL_TYPE], [convenience])])
diff --git a/m4/ltsugar.m4 b/m4/ltsugar.m4
deleted file mode 100644 (file)
index 9000a05..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-# ltsugar.m4 -- libtool m4 base layer.                         -*-Autoconf-*-
-#
-# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
-# Written by Gary V. Vaughan, 2004
-#
-# This file is free software; the Free Software Foundation gives
-# unlimited permission to copy and/or distribute it, with or without
-# modifications, as long as this notice is preserved.
-
-# serial 6 ltsugar.m4
-
-# This is to help aclocal find these macros, as it can't see m4_define.
-AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
-
-
-# lt_join(SEP, ARG1, [ARG2...])
-# -----------------------------
-# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
-# associated separator.
-# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
-# versions in m4sugar had bugs.
-m4_define([lt_join],
-[m4_if([$#], [1], [],
-       [$#], [2], [[$2]],
-       [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
-m4_define([_lt_join],
-[m4_if([$#$2], [2], [],
-       [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
-
-
-# lt_car(LIST)
-# lt_cdr(LIST)
-# ------------
-# Manipulate m4 lists.
-# These macros are necessary as long as will still need to support
-# Autoconf-2.59 which quotes differently.
-m4_define([lt_car], [[$1]])
-m4_define([lt_cdr],
-[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
-       [$#], 1, [],
-       [m4_dquote(m4_shift($@))])])
-m4_define([lt_unquote], $1)
-
-
-# lt_append(MACRO-NAME, STRING, [SEPARATOR])
-# ------------------------------------------
-# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'.
-# Note that neither SEPARATOR nor STRING are expanded; they are appended
-# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
-# No SEPARATOR is output if MACRO-NAME was previously undefined (different
-# than defined and empty).
-#
-# This macro is needed until we can rely on Autoconf 2.62, since earlier
-# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
-m4_define([lt_append],
-[m4_define([$1],
-          m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
-
-
-
-# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
-# ----------------------------------------------------------
-# Produce a SEP delimited list of all paired combinations of elements of
-# PREFIX-LIST with SUFFIX1 through SUFFIXn.  Each element of the list
-# has the form PREFIXmINFIXSUFFIXn.
-# Needed until we can rely on m4_combine added in Autoconf 2.62.
-m4_define([lt_combine],
-[m4_if(m4_eval([$# > 3]), [1],
-       [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
-[[m4_foreach([_Lt_prefix], [$2],
-            [m4_foreach([_Lt_suffix],
-               ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
-       [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
-
-
-# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
-# -----------------------------------------------------------------------
-# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
-# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
-m4_define([lt_if_append_uniq],
-[m4_ifdef([$1],
-         [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
-                [lt_append([$1], [$2], [$3])$4],
-                [$5])],
-         [lt_append([$1], [$2], [$3])$4])])
-
-
-# lt_dict_add(DICT, KEY, VALUE)
-# -----------------------------
-m4_define([lt_dict_add],
-[m4_define([$1($2)], [$3])])
-
-
-# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
-# --------------------------------------------
-m4_define([lt_dict_add_subkey],
-[m4_define([$1($2:$3)], [$4])])
-
-
-# lt_dict_fetch(DICT, KEY, [SUBKEY])
-# ----------------------------------
-m4_define([lt_dict_fetch],
-[m4_ifval([$3],
-       m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
-    m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
-
-
-# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
-# -----------------------------------------------------------------
-m4_define([lt_if_dict_fetch],
-[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
-       [$5],
-    [$6])])
-
-
-# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
-# --------------------------------------------------------------
-m4_define([lt_dict_filter],
-[m4_if([$5], [], [],
-  [lt_join(m4_quote(m4_default([$4], [[, ]])),
-           lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
-                     [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
-])
diff --git a/m4/ltversion.m4 b/m4/ltversion.m4
deleted file mode 100644 (file)
index f3c5309..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-# ltversion.m4 -- version numbers                      -*- Autoconf -*-
-#
-#   Copyright (C) 2004 Free Software Foundation, Inc.
-#   Written by Scott James Remnant, 2004
-#
-# This file is free software; the Free Software Foundation gives
-# unlimited permission to copy and/or distribute it, with or without
-# modifications, as long as this notice is preserved.
-
-# Generated from ltversion.in.
-
-# serial 3017 ltversion.m4
-# This file is part of GNU Libtool
-
-m4_define([LT_PACKAGE_VERSION], [2.2.6b])
-m4_define([LT_PACKAGE_REVISION], [1.3017])
-
-AC_DEFUN([LTVERSION_VERSION],
-[macro_version='2.2.6b'
-macro_revision='1.3017'
-_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
-_LT_DECL(, macro_revision, 0)
-])
diff --git a/m4/lt~obsolete.m4 b/m4/lt~obsolete.m4
deleted file mode 100644 (file)
index 637bb20..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-# lt~obsolete.m4 -- aclocal satisfying obsolete definitions.    -*-Autoconf-*-
-#
-#   Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
-#   Written by Scott James Remnant, 2004.
-#
-# This file is free software; the Free Software Foundation gives
-# unlimited permission to copy and/or distribute it, with or without
-# modifications, as long as this notice is preserved.
-
-# serial 4 lt~obsolete.m4
-
-# These exist entirely to fool aclocal when bootstrapping libtool.
-#
-# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN)
-# which have later been changed to m4_define as they aren't part of the
-# exported API, or moved to Autoconf or Automake where they belong.
-#
-# The trouble is, aclocal is a bit thick.  It'll see the old AC_DEFUN
-# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
-# using a macro with the same name in our local m4/libtool.m4 it'll
-# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
-# and doesn't know about Autoconf macros at all.)
-#
-# So we provide this file, which has a silly filename so it's always
-# included after everything else.  This provides aclocal with the
-# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
-# because those macros already exist, or will be overwritten later.
-# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. 
-#
-# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
-# Yes, that means every name once taken will need to remain here until
-# we give up compatibility with versions before 1.7, at which point
-# we need to keep only those names which we still refer to.
-
-# This is to help aclocal find these macros, as it can't see m4_define.
-AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
-
-m4_ifndef([AC_LIBTOOL_LINKER_OPTION],  [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
-m4_ifndef([AC_PROG_EGREP],             [AC_DEFUN([AC_PROG_EGREP])])
-m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH],        [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
-m4_ifndef([_LT_AC_SHELL_INIT],         [AC_DEFUN([_LT_AC_SHELL_INIT])])
-m4_ifndef([_LT_AC_SYS_LIBPATH_AIX],    [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
-m4_ifndef([_LT_PROG_LTMAIN],           [AC_DEFUN([_LT_PROG_LTMAIN])])
-m4_ifndef([_LT_AC_TAGVAR],             [AC_DEFUN([_LT_AC_TAGVAR])])
-m4_ifndef([AC_LTDL_ENABLE_INSTALL],    [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
-m4_ifndef([AC_LTDL_PREOPEN],           [AC_DEFUN([AC_LTDL_PREOPEN])])
-m4_ifndef([_LT_AC_SYS_COMPILER],       [AC_DEFUN([_LT_AC_SYS_COMPILER])])
-m4_ifndef([_LT_AC_LOCK],               [AC_DEFUN([_LT_AC_LOCK])])
-m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE],        [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
-m4_ifndef([_LT_AC_TRY_DLOPEN_SELF],    [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
-m4_ifndef([AC_LIBTOOL_PROG_CC_C_O],    [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
-m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
-m4_ifndef([AC_LIBTOOL_OBJDIR],         [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
-m4_ifndef([AC_LTDL_OBJDIR],            [AC_DEFUN([AC_LTDL_OBJDIR])])
-m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
-m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP],  [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
-m4_ifndef([AC_PATH_MAGIC],             [AC_DEFUN([AC_PATH_MAGIC])])
-m4_ifndef([AC_PROG_LD_GNU],            [AC_DEFUN([AC_PROG_LD_GNU])])
-m4_ifndef([AC_PROG_LD_RELOAD_FLAG],    [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
-m4_ifndef([AC_DEPLIBS_CHECK_METHOD],   [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
-m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
-m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
-m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
-m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
-m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
-m4_ifndef([LT_AC_PROG_EGREP],          [AC_DEFUN([LT_AC_PROG_EGREP])])
-m4_ifndef([LT_AC_PROG_SED],            [AC_DEFUN([LT_AC_PROG_SED])])
-m4_ifndef([_LT_CC_BASENAME],           [AC_DEFUN([_LT_CC_BASENAME])])
-m4_ifndef([_LT_COMPILER_BOILERPLATE],  [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
-m4_ifndef([_LT_LINKER_BOILERPLATE],    [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
-m4_ifndef([_AC_PROG_LIBTOOL],          [AC_DEFUN([_AC_PROG_LIBTOOL])])
-m4_ifndef([AC_LIBTOOL_SETUP],          [AC_DEFUN([AC_LIBTOOL_SETUP])])
-m4_ifndef([_LT_AC_CHECK_DLFCN],                [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
-m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER],     [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
-m4_ifndef([_LT_AC_TAGCONFIG],          [AC_DEFUN([_LT_AC_TAGCONFIG])])
-m4_ifndef([AC_DISABLE_FAST_INSTALL],   [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
-m4_ifndef([_LT_AC_LANG_CXX],           [AC_DEFUN([_LT_AC_LANG_CXX])])
-m4_ifndef([_LT_AC_LANG_F77],           [AC_DEFUN([_LT_AC_LANG_F77])])
-m4_ifndef([_LT_AC_LANG_GCJ],           [AC_DEFUN([_LT_AC_LANG_GCJ])])
-m4_ifndef([AC_LIBTOOL_RC],             [AC_DEFUN([AC_LIBTOOL_RC])])
-m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG],  [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
-m4_ifndef([_LT_AC_LANG_C_CONFIG],      [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
-m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG],        [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
-m4_ifndef([_LT_AC_LANG_CXX_CONFIG],    [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
-m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG],        [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
-m4_ifndef([_LT_AC_LANG_F77_CONFIG],    [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
-m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG],        [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
-m4_ifndef([_LT_AC_LANG_GCJ_CONFIG],    [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
-m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
-m4_ifndef([_LT_AC_LANG_RC_CONFIG],     [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
-m4_ifndef([AC_LIBTOOL_CONFIG],         [AC_DEFUN([AC_LIBTOOL_CONFIG])])
-m4_ifndef([_LT_AC_FILE_LTDLL_C],       [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
index ff3d5dd..5bf8686 100644 (file)
@@ -1,4 +1,4 @@
-/* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*- 
+/* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
  * vim: ts=4 sw=4 noet ai cindent syntax=c
  */
 
@@ -64,6 +64,7 @@ double get_acpi_temperature(int fd);
 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size, const char *adapter);
 void get_acpi_fan(char *, size_t);
 void get_battery_stuff(char *buf, unsigned int n, const char *bat, int item);
+void get_dbus_stuff(char *buffer,unsigned int intMax_length, int item);
 int get_battery_perct(const char *bat);
 int get_battery_perct_bar(const char *bat);
 void get_battery_short_status(char *buf, unsigned int n, const char *bat);
index bade353..86be8ed 100644 (file)
@@ -894,6 +894,12 @@ void generate_text_internal(char *p, int p_max_size,
                        OBJ(battery_short) {
                                get_battery_short_status(p, p_max_size, obj->data.s);
                        }
+                       OBJ(cell_radio_dbm) {
+                               get_dbus_stuff(p, p_max_size, DBUS_CELL_DBM);
+                       }
+                       OBJ(cell_radio_percent) {
+                               get_dbus_stuff(p, p_max_size, DBUS_CELL_PERCENT);
+                       }
 #endif /* __OpenBSD__ */
 
                        OBJ(buffers) {
@@ -6029,7 +6035,7 @@ static void signal_handler(int sig)
 {
        /* signal handler is light as a feather, as it should be.
         * we will poll g_signal_pending with each loop of conky
-        * and do any signal processing there, NOT here (except 
+        * and do any signal processing there, NOT here (except
         * SIGALRM because this is caused when conky is hanging) */
        if(sig == SIGALRM) {
                alarm_handler();
index b717e38..442211d 100644 (file)
@@ -197,13 +197,15 @@ extern struct conftree *currentconffile;
 #define MAX_TEMPLATES 10
 char **get_templates(void);
 
-/* get_battery_stuff() item selector
+/* get_xxxxx_stuff() item selector
  * needed by conky.c, linux.c and freebsd.c */
 enum {
        BATTERY_STATUS,
        BATTERY_TIME,
        BATTERY_VOLTS,
-       BATTERY_TEMP
+       BATTERY_TEMP,
+       DBUS_CELL_DBM,
+       DBUS_CELL_PERCENT
 };
 
 /* if_up strictness selector
index 009cb27..2ca922d 100644 (file)
@@ -80,6 +80,7 @@
 #include <string.h>
 #include <ctype.h>
 
+
 /* strip a leading /dev/ if any, following symlinks first
  *
  * BEWARE: this function returns a pointer to static content
@@ -160,12 +161,12 @@ struct text_object *construct_text_object(const char *s, const char *arg, long
                if(arg) {
 #ifdef __linux__
                        if(strpbrk(arg, "/.") != NULL) {
-                               /* 
+                               /*
                                 * a bit of paranoia. screen out funky paths
                                 * i hope no device will have a '.' in its name
                                 */
                                NORM_ERR("acpiacadapter: arg must not contain '/' or '.'");
-                       } else 
+                       } else
                                obj->data.opaque = strdup(arg);
 #else
                        NORM_ERR("acpiacadapter: arg is only used on linux");
@@ -301,6 +302,8 @@ struct text_object *construct_text_object(const char *s, const char *arg, long
                        strcpy(bat, "bq27200-0");
                }
                obj->data.s = strndup(bat, text_buffer_size);
+       END OBJ(cell_radio_dbm, 0)
+       END OBJ(cell_radio_percent, 0)
 #endif /* !__OpenBSD__ */
 
 #if defined(__linux__)
diff --git a/src/dbus/.deps/dbus-address.Plo b/src/dbus/.deps/dbus-address.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-auth-script.Plo b/src/dbus/.deps/dbus-auth-script.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-auth-util.Plo b/src/dbus/.deps/dbus-auth-util.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-auth.Plo b/src/dbus/.deps/dbus-auth.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-bus.Plo b/src/dbus/.deps/dbus-bus.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-connection.Plo b/src/dbus/.deps/dbus-connection.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-credentials-util.Plo b/src/dbus/.deps/dbus-credentials-util.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-credentials.Plo b/src/dbus/.deps/dbus-credentials.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-dataslot.Plo b/src/dbus/.deps/dbus-dataslot.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-errors.Plo b/src/dbus/.deps/dbus-errors.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-hash.Plo b/src/dbus/.deps/dbus-hash.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-internals.Plo b/src/dbus/.deps/dbus-internals.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-keyring.Plo b/src/dbus/.deps/dbus-keyring.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-list.Plo b/src/dbus/.deps/dbus-list.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-mainloop.Plo b/src/dbus/.deps/dbus-mainloop.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-marshal-basic.Plo b/src/dbus/.deps/dbus-marshal-basic.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-marshal-byteswap-util.Plo b/src/dbus/.deps/dbus-marshal-byteswap-util.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-marshal-byteswap.Plo b/src/dbus/.deps/dbus-marshal-byteswap.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-marshal-header.Plo b/src/dbus/.deps/dbus-marshal-header.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-marshal-recursive-util.Plo b/src/dbus/.deps/dbus-marshal-recursive-util.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-marshal-recursive.Plo b/src/dbus/.deps/dbus-marshal-recursive.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-marshal-validate-util.Plo b/src/dbus/.deps/dbus-marshal-validate-util.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-marshal-validate.Plo b/src/dbus/.deps/dbus-marshal-validate.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-memory.Plo b/src/dbus/.deps/dbus-memory.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-mempool.Plo b/src/dbus/.deps/dbus-mempool.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-message-factory.Plo b/src/dbus/.deps/dbus-message-factory.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-message-util.Plo b/src/dbus/.deps/dbus-message-util.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-message.Plo b/src/dbus/.deps/dbus-message.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-misc.Plo b/src/dbus/.deps/dbus-misc.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-object-tree.Plo b/src/dbus/.deps/dbus-object-tree.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-pending-call.Plo b/src/dbus/.deps/dbus-pending-call.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-resources.Plo b/src/dbus/.deps/dbus-resources.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-server-debug-pipe.Plo b/src/dbus/.deps/dbus-server-debug-pipe.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-server-socket.Plo b/src/dbus/.deps/dbus-server-socket.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-server-unix.Plo b/src/dbus/.deps/dbus-server-unix.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-server.Plo b/src/dbus/.deps/dbus-server.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-sha.Plo b/src/dbus/.deps/dbus-sha.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-shell.Plo b/src/dbus/.deps/dbus-shell.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-signature.Plo b/src/dbus/.deps/dbus-signature.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-spawn.Plo b/src/dbus/.deps/dbus-spawn.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-string-util.Plo b/src/dbus/.deps/dbus-string-util.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-string.Plo b/src/dbus/.deps/dbus-string.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-sysdeps-pthread.Plo b/src/dbus/.deps/dbus-sysdeps-pthread.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-sysdeps-unix.Plo b/src/dbus/.deps/dbus-sysdeps-unix.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-sysdeps-util-unix.Plo b/src/dbus/.deps/dbus-sysdeps-util-unix.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-sysdeps-util.Plo b/src/dbus/.deps/dbus-sysdeps-util.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-sysdeps.Plo b/src/dbus/.deps/dbus-sysdeps.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-test.Plo b/src/dbus/.deps/dbus-test.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-threads.Plo b/src/dbus/.deps/dbus-threads.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-timeout.Plo b/src/dbus/.deps/dbus-timeout.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-transport-socket.Plo b/src/dbus/.deps/dbus-transport-socket.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-transport-unix.Plo b/src/dbus/.deps/dbus-transport-unix.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-transport.Plo b/src/dbus/.deps/dbus-transport.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-userdb-util.Plo b/src/dbus/.deps/dbus-userdb-util.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-userdb.Plo b/src/dbus/.deps/dbus-userdb.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-uuidgen.Plo b/src/dbus/.deps/dbus-uuidgen.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/.deps/dbus-watch.Plo b/src/dbus/.deps/dbus-watch.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/dbus/Makefile b/src/dbus/Makefile
new file mode 100644 (file)
index 0000000..6a6266c
--- /dev/null
@@ -0,0 +1,1026 @@
+# Makefile.in generated by automake 1.8.5 from Makefile.am.
+# src/dbus/Makefile.  Generated from Makefile.in by configure.
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004  Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+
+
+
+SOURCES = $(libdbus_1_la_SOURCES) $(libdbus_convenience_la_SOURCES) $(dbus_test_SOURCES)
+
+srcdir = .
+top_srcdir = ../..
+
+pkgdatadir = $(datadir)/conky
+pkglibdir = $(libdir)/conky
+pkgincludedir = $(includedir)/conky
+top_builddir = ../..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = /scratchbox/tools/bin/install -c
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+host_triplet = arm-unknown-linux-gnueabi
+noinst_PROGRAMS = $(am__EXEEXT_1)
+subdir = src/dbus
+DIST_COMMON = $(dbusarchinclude_HEADERS) $(dbusinclude_HEADERS) \
+       $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/pkg.m4 \
+       $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+       $(ACLOCAL_M4)
+mkinstalldirs = $(mkdir_p)
+CONFIG_HEADER = $(top_builddir)/src/config.h
+CONFIG_CLEAN_FILES =
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(dbusarchincludedir)" "$(DESTDIR)$(dbusincludedir)"
+libLTLIBRARIES_INSTALL = $(INSTALL)
+LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES)
+libdbus_1_la_DEPENDENCIES =
+am__objects_1 = dbus-address.lo dbus-auth.lo dbus-auth-script.lo \
+       dbus-bus.lo dbus-connection.lo dbus-credentials.lo \
+       dbus-errors.lo dbus-keyring.lo dbus-marshal-header.lo \
+       dbus-marshal-byteswap.lo dbus-marshal-recursive.lo \
+       dbus-marshal-validate.lo dbus-message.lo dbus-misc.lo \
+       dbus-object-tree.lo dbus-pending-call.lo dbus-resources.lo \
+       dbus-server.lo dbus-server-debug-pipe.lo dbus-server-socket.lo \
+       dbus-server-unix.lo dbus-sha.lo dbus-signature.lo \
+       dbus-timeout.lo dbus-threads.lo dbus-transport.lo \
+       dbus-transport-socket.lo dbus-transport-unix.lo \
+       dbus-uuidgen.lo dbus-watch.lo
+am__objects_2 = dbus-dataslot.lo dbus-hash.lo dbus-internals.lo \
+       dbus-list.lo dbus-marshal-basic.lo dbus-memory.lo \
+       dbus-mempool.lo dbus-string.lo dbus-sysdeps.lo \
+       dbus-sysdeps-pthread.lo dbus-sysdeps-unix.lo dbus-userdb.lo
+am_libdbus_1_la_OBJECTS = $(am__objects_1) $(am__objects_2)
+libdbus_1_la_OBJECTS = $(am_libdbus_1_la_OBJECTS)
+libdbus_convenience_la_LIBADD =
+am__objects_3 = dbus-auth-util.lo dbus-credentials-util.lo \
+       dbus-mainloop.lo dbus-marshal-byteswap-util.lo \
+       dbus-marshal-recursive-util.lo dbus-marshal-validate-util.lo \
+       dbus-message-factory.lo dbus-message-util.lo dbus-shell.lo \
+       dbus-spawn.lo dbus-string-util.lo dbus-sysdeps-util.lo \
+       dbus-sysdeps-util-unix.lo dbus-test.lo dbus-userdb-util.lo
+am_libdbus_convenience_la_OBJECTS = $(am__objects_1) $(am__objects_2) \
+       $(am__objects_3)
+libdbus_convenience_la_OBJECTS = $(am_libdbus_convenience_la_OBJECTS)
+@DBUS_BUILD_TESTS_TRUE@am__EXEEXT_1 = dbus-test$(EXEEXT)
+PROGRAMS = $(noinst_PROGRAMS)
+am_dbus_test_OBJECTS = dbus-test-main.$(OBJEXT)
+dbus_test_OBJECTS = $(am_dbus_test_OBJECTS)
+dbus_test_DEPENDENCIES = libdbus-convenience.la
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/src
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+DEP_FILES = ./$(DEPDIR)/dbus-address.Plo \
+       ./$(DEPDIR)/dbus-auth-script.Plo \
+       ./$(DEPDIR)/dbus-auth-util.Plo \
+       ./$(DEPDIR)/dbus-auth.Plo ./$(DEPDIR)/dbus-bus.Plo \
+       ./$(DEPDIR)/dbus-connection.Plo \
+       ./$(DEPDIR)/dbus-credentials-util.Plo \
+       ./$(DEPDIR)/dbus-credentials.Plo \
+       ./$(DEPDIR)/dbus-dataslot.Plo \
+       ./$(DEPDIR)/dbus-errors.Plo \
+       ./$(DEPDIR)/dbus-hash.Plo \
+       ./$(DEPDIR)/dbus-internals.Plo \
+       ./$(DEPDIR)/dbus-keyring.Plo \
+       ./$(DEPDIR)/dbus-list.Plo \
+       ./$(DEPDIR)/dbus-mainloop.Plo \
+       ./$(DEPDIR)/dbus-marshal-basic.Plo \
+       ./$(DEPDIR)/dbus-marshal-byteswap-util.Plo \
+       ./$(DEPDIR)/dbus-marshal-byteswap.Plo \
+       ./$(DEPDIR)/dbus-marshal-header.Plo \
+       ./$(DEPDIR)/dbus-marshal-recursive-util.Plo \
+       ./$(DEPDIR)/dbus-marshal-recursive.Plo \
+       ./$(DEPDIR)/dbus-marshal-validate-util.Plo \
+       ./$(DEPDIR)/dbus-marshal-validate.Plo \
+       ./$(DEPDIR)/dbus-memory.Plo \
+       ./$(DEPDIR)/dbus-mempool.Plo \
+       ./$(DEPDIR)/dbus-message-factory.Plo \
+       ./$(DEPDIR)/dbus-message-util.Plo \
+       ./$(DEPDIR)/dbus-message.Plo \
+       ./$(DEPDIR)/dbus-misc.Plo \
+       ./$(DEPDIR)/dbus-object-tree.Plo \
+       ./$(DEPDIR)/dbus-pending-call.Plo \
+       ./$(DEPDIR)/dbus-resources.Plo \
+       ./$(DEPDIR)/dbus-server-debug-pipe.Plo \
+       ./$(DEPDIR)/dbus-server-socket.Plo \
+       ./$(DEPDIR)/dbus-server-unix.Plo \
+       ./$(DEPDIR)/dbus-server.Plo \
+       ./$(DEPDIR)/dbus-sha.Plo \
+       ./$(DEPDIR)/dbus-shell.Plo \
+       ./$(DEPDIR)/dbus-signature.Plo \
+       ./$(DEPDIR)/dbus-spawn.Plo \
+       ./$(DEPDIR)/dbus-string-util.Plo \
+       ./$(DEPDIR)/dbus-string.Plo \
+       ./$(DEPDIR)/dbus-sysdeps-pthread.Plo \
+       ./$(DEPDIR)/dbus-sysdeps-unix.Plo \
+       ./$(DEPDIR)/dbus-sysdeps-util-unix.Plo \
+       ./$(DEPDIR)/dbus-sysdeps-util.Plo \
+       ./$(DEPDIR)/dbus-sysdeps.Plo \
+       ./$(DEPDIR)/dbus-test-main.Po \
+       ./$(DEPDIR)/dbus-test.Plo \
+       ./$(DEPDIR)/dbus-threads.Plo \
+       ./$(DEPDIR)/dbus-timeout.Plo \
+       ./$(DEPDIR)/dbus-transport-socket.Plo \
+       ./$(DEPDIR)/dbus-transport-unix.Plo \
+       ./$(DEPDIR)/dbus-transport.Plo \
+       ./$(DEPDIR)/dbus-userdb-util.Plo \
+       ./$(DEPDIR)/dbus-userdb.Plo \
+       ./$(DEPDIR)/dbus-uuidgen.Plo \
+       ./$(DEPDIR)/dbus-watch.Plo
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+       $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) \
+       $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+       $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+       $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libdbus_1_la_SOURCES) $(libdbus_convenience_la_SOURCES) \
+       $(dbus_test_SOURCES)
+DIST_SOURCES = $(libdbus_1_la_SOURCES) \
+       $(libdbus_convenience_la_SOURCES) $(dbus_test_SOURCES)
+dbusarchincludeHEADERS_INSTALL = $(INSTALL_HEADER)
+dbusincludeHEADERS_INSTALL = $(INSTALL_HEADER)
+HEADERS = $(dbusarchinclude_HEADERS) $(dbusinclude_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = ${SHELL} /home/lance/shared/workspace/conky/missing --run aclocal-1.8
+AMDEP_FALSE = #
+AMDEP_TRUE = 
+AMTAR = ${SHELL} /home/lance/shared/workspace/conky/missing --run tar
+AR = ar
+AUTOCONF = ${SHELL} /home/lance/shared/workspace/conky/missing --run autoconf
+AUTOHEADER = ${SHELL} /home/lance/shared/workspace/conky/missing --run autoheader
+AUTOMAKE = ${SHELL} /home/lance/shared/workspace/conky/missing --run automake-1.8
+AWK = gawk
+Audacious_CFLAGS = 
+Audacious_LIBS = 
+BMPx_CFLAGS = 
+BMPx_LIBS = 
+BUILD_APCUPSD_FALSE = #
+BUILD_APCUPSD_TRUE = 
+BUILD_ARCH = Linux 2.6.32-24-generic (arm)
+BUILD_AUDACIOUS_FALSE = 
+BUILD_AUDACIOUS_TRUE = #
+BUILD_BMPX_FALSE = 
+BUILD_BMPX_TRUE = #
+BUILD_CONFIG_OUTPUT_FALSE = #
+BUILD_CONFIG_OUTPUT_TRUE = 
+BUILD_CURL_FALSE = 
+BUILD_CURL_TRUE = #
+BUILD_DATE = Tue Oct 19 21:04:16 PDT 2010
+BUILD_EVE_FALSE = 
+BUILD_EVE_TRUE = #
+BUILD_FREEBSD_FALSE = 
+BUILD_FREEBSD_TRUE = #
+BUILD_HDDTEMP_FALSE = #
+BUILD_HDDTEMP_TRUE = 
+BUILD_IBM_FALSE = 
+BUILD_IBM_TRUE = #
+BUILD_ICONV_FALSE = #
+BUILD_ICONV_TRUE = 
+BUILD_IMLIB2_FALSE = 
+BUILD_IMLIB2_TRUE = #
+BUILD_LINUX_FALSE = #
+BUILD_LINUX_TRUE = 
+BUILD_LUA_CAIRO_FALSE = 
+BUILD_LUA_CAIRO_TRUE = #
+BUILD_LUA_FALSE = #
+BUILD_LUA_IMLIB2_FALSE = 
+BUILD_LUA_IMLIB2_TRUE = #
+BUILD_LUA_TRUE = 
+BUILD_MATH_FALSE = #
+BUILD_MATH_TRUE = 
+BUILD_MOC_FALSE = #
+BUILD_MOC_TRUE = 
+BUILD_MPD_FALSE = #
+BUILD_MPD_TRUE = 
+BUILD_NCURSES_FALSE = #
+BUILD_NCURSES_TRUE = 
+BUILD_NVIDIA_FALSE = 
+BUILD_NVIDIA_TRUE = #
+BUILD_OPENBSD_FALSE = 
+BUILD_OPENBSD_TRUE = #
+BUILD_PORT_MONITORS_FALSE = #
+BUILD_PORT_MONITORS_TRUE = 
+BUILD_RSS_FALSE = 
+BUILD_RSS_TRUE = #
+BUILD_WEATHER_FALSE = 
+BUILD_WEATHER_TRUE = #
+BUILD_WLAN_FALSE = 
+BUILD_WLAN_TRUE = #
+BUILD_X11_FALSE = #
+BUILD_X11_TRUE = 
+BUILD_XMMS2_FALSE = 
+BUILD_XMMS2_TRUE = #
+BUILD_XOAP_FALSE = 
+BUILD_XOAP_TRUE = #
+CC = gcc
+CCDEPMODE = depmode=gcc3
+CFLAGS = -O2 -g
+CPP = gcc -E
+CPPFLAGS = 
+CXX = g++
+CXXCPP = g++ -E
+CXXDEPMODE = depmode=gcc3
+CXXFLAGS = -O2 -g
+CYGPATH_W = echo
+DEFS = -DHAVE_CONFIG_H
+DEPDIR = .deps
+DSYMUTIL = 
+ECHO = echo
+ECHO_C = 
+ECHO_N = -n
+ECHO_T = 
+EGREP = /scratchbox/tools/bin/grep -E
+EXEEXT = 
+F77 = 
+FFLAGS = 
+GLib2_CFLAGS = -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include  
+GLib2_LIBS = -lglib-2.0  
+GREP = /scratchbox/tools/bin/grep
+HAVE_DOCSTUFF_FALSE = 
+HAVE_DOCSTUFF_TRUE = #
+HAVE_PKGCONFIG = yes
+INSTALL_DATA = ${INSTALL} -m 644
+INSTALL_PROGRAM = ${INSTALL}
+INSTALL_SCRIPT = ${INSTALL}
+INSTALL_STRIP_PROGRAM = ${SHELL} $(install_sh) -c -s
+Imlib2_CFLAGS = 
+Imlib2_LIBS = 
+LDFLAGS = 
+LIBICONV = 
+LIBOBJS = 
+LIBS = -lrt 
+LIBTOOL = $(SHELL) $(top_builddir)/libtool
+LN_S = ln
+LTLIBICONV = 
+LTLIBOBJS = 
+LUA51_CFLAGS = -I/usr/include/lua5.1  
+LUA51_LIBS = -llua5.1  
+LUA_CFLAGS = 
+LUA_LIBS = 
+MAKEINFO = ${SHELL} /home/lance/shared/workspace/conky/missing --run makeinfo
+NMEDIT = 
+OBJEXT = o
+PACKAGE = conky
+PACKAGE_BUGREPORT = brenden1@users.sourceforge.net
+PACKAGE_NAME = Conky
+PACKAGE_STRING = Conky 1.8.0
+PACKAGE_TARNAME = conky
+PACKAGE_VERSION = 1.8.0
+PATH_SEPARATOR = :
+PKG_CONFIG = /scratchbox/tools/bin/pkg-config
+RANLIB = ranlib
+SED = /scratchbox/tools/bin/sed
+SET_MAKE = 
+SHELL = /bin/sh
+STRIP = strip
+VERSION = 1.8.0
+X11_CFLAGS =  
+X11_LIBS = -lX11  
+XDamage_CFLAGS =  
+XDamage_LIBS = -lXdamage -lXfixes  
+XMKMF = 
+XMMS2_CFLAGS = 
+XMMS2_LIBS = 
+X_CFLAGS = 
+X_EXTRA_LIBS = 
+X_LIBS = 
+X_PRE_LIBS = 
+Xext_CFLAGS =  
+Xext_LIBS = -lXext  
+Xft_CFLAGS = -I/usr/include/freetype2  
+Xft_LIBS = -lXft -lfontconfig  
+ac_ct_CC = gcc
+ac_ct_CXX = g++
+ac_ct_F77 = 
+am__fastdepCC_FALSE = #
+am__fastdepCC_TRUE = 
+am__fastdepCXX_FALSE = #
+am__fastdepCXX_TRUE = 
+am__include = include
+am__leading_dot = .
+am__quote = 
+bindir = ${exec_prefix}/bin
+build = arm-unknown-linux-gnueabi
+build_alias = 
+build_cpu = arm
+build_os = linux-gnueabi
+build_vendor = unknown
+cairo_CFLAGS = 
+cairo_LIBS = 
+cairo_xlib_CFLAGS = 
+cairo_xlib_LIBS = 
+conky_CFLAGS =     -I/usr/include/lua5.1       -I/usr/include/freetype2   -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include   -Wall -W
+conky_LIBS =  -lncurses -lm -lX11    -llua5.1    -lXext   -lXdamage -lXfixes   -lXft -lfontconfig   -lglib-2.0   -lasound -lrt 
+datadir = ${datarootdir}
+datarootdir = ${prefix}/share
+db2x_manxml_cmd = 
+db2x_xsltproc_cmd = 
+docdir = ${datarootdir}/doc/${PACKAGE_TARNAME}
+dvidir = ${docdir}
+exec_prefix = ${prefix}
+host = arm-unknown-linux-gnueabi
+host_alias = 
+host_cpu = arm
+host_os = linux-gnueabi
+host_vendor = unknown
+htmldir = ${docdir}
+includedir = ${prefix}/include
+infodir = ${datarootdir}/info
+install_sh = /home/lance/shared/workspace/conky/install-sh
+libcurl_CFLAGS = 
+libcurl_LIBS = 
+libdir = ${exec_prefix}/lib
+libexecdir = ${exec_prefix}/libexec
+libxml2_CFLAGS = 
+libxml2_LIBS = 
+localedir = ${datarootdir}/locale
+localstatedir = ${prefix}/var
+mandir = ${datarootdir}/man
+mkdir_p = mkdir -p -- .
+oldincludedir = /usr/include
+pdfdir = ${docdir}
+prefix = /usr/local
+program_transform_name = s,x,x,
+psdir = ${docdir}
+sbindir = ${exec_prefix}/sbin
+sharedstatedir = ${prefix}/com
+sysconfdir = ${prefix}/etc
+target_alias = 
+tolua_CFLAGS = 
+tolua_LIBS = 
+toluapp = 
+xsltproc_cmd = xsltproc
+configdir = $(sysconfdir)/dbus-1
+INCLUDES = -I$(top_builddir) -I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) @PIC_CFLAGS@ -DDBUS_COMPILATION     \
+       -DDBUS_MACHINE_UUID_FILE=\""$(localstatedir)/lib/dbus/machine-id"\"             \
+       -DDBUS_SYSTEM_CONFIG_FILE=\""$(configdir)/system.conf"\"                        \
+       -DDBUS_SESSION_CONFIG_FILE=\""$(configdir)/session.conf"\"
+
+dbusincludedir = $(includedir)/dbus-1.0/dbus
+dbusarchincludedir = $(libdir)/dbus-1.0/include/dbus
+lib_LTLIBRARIES = libdbus-1.la
+dbusinclude_HEADERS = \
+       dbus.h                                  \
+       dbus-address.h                          \
+       dbus-bus.h                              \
+       dbus-connection.h                       \
+       dbus-errors.h                           \
+       dbus-macros.h                           \
+       dbus-memory.h                           \
+       dbus-message.h                          \
+       dbus-misc.h                             \
+       dbus-pending-call.h                     \
+       dbus-protocol.h                         \
+       dbus-server.h                           \
+       dbus-shared.h                           \
+       dbus-signature.h                        \
+       dbus-threads.h                          \
+       dbus-types.h
+
+dbusarchinclude_HEADERS = \
+       dbus-arch-deps.h
+
+
+### source code that goes in the installed client library
+### and is specific to library functionality
+DBUS_LIB_SOURCES = \
+       dbus-address.c                          \
+       dbus-auth.c                             \
+       dbus-auth.h                             \
+       dbus-auth-script.c                      \
+       dbus-auth-script.h                      \
+       dbus-bus.c                              \
+       dbus-connection.c                       \
+       dbus-connection-internal.h              \
+       dbus-credentials.c                      \
+       dbus-credentials.h                      \
+       dbus-errors.c                           \
+       dbus-keyring.c                          \
+       dbus-keyring.h                          \
+       dbus-marshal-header.c                   \
+       dbus-marshal-header.h                   \
+       dbus-marshal-byteswap.c                 \
+       dbus-marshal-byteswap.h                 \
+       dbus-marshal-recursive.c                \
+       dbus-marshal-recursive.h                \
+       dbus-marshal-validate.c                 \
+       dbus-marshal-validate.h                 \
+       dbus-message.c                          \
+       dbus-message-internal.h                 \
+       dbus-message-private.h                  \
+       dbus-misc.c                             \
+       dbus-object-tree.c                      \
+       dbus-object-tree.h                      \
+       dbus-pending-call.c                     \
+       dbus-pending-call-internal.h            \
+       dbus-resources.c                        \
+       dbus-resources.h                        \
+       dbus-server.c                           \
+       dbus-server-debug-pipe.c                \
+       dbus-server-debug-pipe.h                \
+       dbus-server-protected.h                 \
+       dbus-server-socket.c                    \
+       dbus-server-socket.h                    \
+       dbus-server-unix.c                      \
+       dbus-server-unix.h                      \
+       dbus-sha.c                              \
+       dbus-sha.h                              \
+       dbus-signature.c                        \
+       dbus-timeout.c                          \
+       dbus-timeout.h                          \
+       dbus-threads-internal.h                 \
+       dbus-threads.c                          \
+       dbus-transport.c                        \
+       dbus-transport.h                        \
+       dbus-transport-protected.h              \
+       dbus-transport-socket.c                 \
+       dbus-transport-socket.h                 \
+       dbus-transport-unix.c                   \
+       dbus-transport-unix.h                   \
+       dbus-uuidgen.c                          \
+       dbus-uuidgen.h                          \
+       dbus-watch.c                            \
+       dbus-watch.h
+
+
+### source code that goes in the installed client library
+### AND is generic utility functionality used by the 
+### daemon or test programs (all symbols in here should 
+### be underscore-prefixed)
+DBUS_SHARED_SOURCES = \
+       dbus-dataslot.c                         \
+       dbus-dataslot.h                         \
+       dbus-hash.c                             \
+       dbus-hash.h                             \
+       dbus-internals.c                        \
+       dbus-internals.h                        \
+       dbus-list.c                             \
+       dbus-list.h                             \
+       dbus-marshal-basic.c                    \
+       dbus-marshal-basic.h                    \
+       dbus-memory.c                           \
+       dbus-mempool.c                          \
+       dbus-mempool.h                          \
+       dbus-string.c                           \
+       dbus-string.h                           \
+       dbus-string-private.h                   \
+       dbus-sysdeps.c                          \
+       dbus-sysdeps.h                          \
+       dbus-sysdeps-pthread.c                  \
+       dbus-sysdeps-unix.c                     \
+       dbus-sysdeps-unix.h                     \
+       dbus-userdb.c                           \
+       dbus-userdb.h
+
+
+### source code that is generic utility functionality used
+### by the bus daemon or test apps, but is NOT included
+### in the D-Bus client library (all symbols in here 
+### should be underscore-prefixed but don't really need 
+### to be unless they move to DBUS_SHARED_SOURCES later)
+DBUS_UTIL_SOURCES = \
+       dbus-auth-util.c                        \
+       dbus-credentials-util.c                 \
+       dbus-mainloop.c                         \
+       dbus-mainloop.h                         \
+       dbus-marshal-byteswap-util.c            \
+       dbus-marshal-recursive-util.c           \
+       dbus-marshal-validate-util.c            \
+       dbus-message-factory.c                  \
+       dbus-message-factory.h                  \
+       dbus-message-util.c                     \
+       dbus-shell.c                            \
+       dbus-shell.h                            \
+       dbus-spawn.c                            \
+       dbus-spawn.h                            \
+       dbus-string-util.c                      \
+       dbus-sysdeps-util.c                     \
+       dbus-sysdeps-util-unix.c                \
+       dbus-test.c                             \
+       dbus-test.h                             \
+       dbus-userdb-util.c
+
+libdbus_1_la_SOURCES = \
+       $(DBUS_LIB_SOURCES)                     \
+       $(DBUS_SHARED_SOURCES)
+
+libdbus_convenience_la_SOURCES = \
+       $(DBUS_LIB_SOURCES)                     \
+       $(DBUS_SHARED_SOURCES)                  \
+       $(DBUS_UTIL_SOURCES)
+
+BUILT_SOURCES = $(dbusarchinclude_HEADERS)
+EXTRA_DIST = dbus-arch-deps.h.in
+noinst_LTLIBRARIES = libdbus-convenience.la
+libdbus_1_la_LIBADD = $(DBUS_CLIENT_LIBS)
+libdbus_1_la_LDFLAGS = -export-symbols-regex "^[^_].*" -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) -no-undefined @R_DYNAMIC_LDFLAG@ @PIC_LDFLAGS@
+libdbus_convenience_la_LDFLAGS = @R_DYNAMIC_LDFLAG@
+@DBUS_BUILD_TESTS_TRUE@TESTS_ENVIRONMENT = DBUS_TEST_DATA=$(top_builddir)/test/data DBUS_TEST_HOMEDIR=$(top_builddir)/dbus
+@DBUS_BUILD_TESTS_FALSE@TESTS = 
+@DBUS_BUILD_TESTS_TRUE@TESTS = dbus-test
+dbus_test_SOURCES = \
+       dbus-test-main.c
+
+dbus_test_LDADD = libdbus-convenience.la $(DBUS_TEST_LIBS)
+dbus_test_LDFLAGS = @R_DYNAMIC_LDFLAG@
+all: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+       @for dep in $?; do \
+         case '$(am__configure_deps)' in \
+           *$$dep*) \
+             cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+               && exit 0; \
+             exit 1;; \
+         esac; \
+       done; \
+       echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu  src/dbus/Makefile'; \
+       cd $(top_srcdir) && \
+         $(AUTOMAKE) --gnu  src/dbus/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+       @case '$?' in \
+         *config.status*) \
+           cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+         *) \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+       esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+       @$(NORMAL_INSTALL)
+       test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)"
+       @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+         if test -f $$p; then \
+           f="`echo $$p | sed -e 's|^.*/||'`"; \
+           echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
+           $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
+         else :; fi; \
+       done
+
+uninstall-libLTLIBRARIES:
+       @$(NORMAL_UNINSTALL)
+       @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+           p="`echo $$p | sed -e 's|^.*/||'`"; \
+         echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \
+         $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \
+       done
+
+clean-libLTLIBRARIES:
+       -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+       @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+         dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+         test "$$dir" != "$$p" || dir=.; \
+         echo "rm -f \"$${dir}/so_locations\""; \
+         rm -f "$${dir}/so_locations"; \
+       done
+
+clean-noinstLTLIBRARIES:
+       -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+       @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+         dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+         test "$$dir" != "$$p" || dir=.; \
+         echo "rm -f \"$${dir}/so_locations\""; \
+         rm -f "$${dir}/so_locations"; \
+       done
+libdbus-1.la: $(libdbus_1_la_OBJECTS) $(libdbus_1_la_DEPENDENCIES) 
+       $(LINK) -rpath $(libdir) $(libdbus_1_la_LDFLAGS) $(libdbus_1_la_OBJECTS) $(libdbus_1_la_LIBADD) $(LIBS)
+libdbus-convenience.la: $(libdbus_convenience_la_OBJECTS) $(libdbus_convenience_la_DEPENDENCIES) 
+       $(LINK)  $(libdbus_convenience_la_LDFLAGS) $(libdbus_convenience_la_OBJECTS) $(libdbus_convenience_la_LIBADD) $(LIBS)
+
+clean-noinstPROGRAMS:
+       @list='$(noinst_PROGRAMS)'; for p in $$list; do \
+         f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+         echo " rm -f $$p $$f"; \
+         rm -f $$p $$f ; \
+       done
+dbus-test$(EXEEXT): $(dbus_test_OBJECTS) $(dbus_test_DEPENDENCIES) 
+       @rm -f dbus-test$(EXEEXT)
+       $(LINK) $(dbus_test_LDFLAGS) $(dbus_test_OBJECTS) $(dbus_test_LDADD) $(LIBS)
+
+mostlyclean-compile:
+       -rm -f *.$(OBJEXT)
+
+distclean-compile:
+       -rm -f *.tab.c
+
+include ./$(DEPDIR)/dbus-address.Plo
+include ./$(DEPDIR)/dbus-auth-script.Plo
+include ./$(DEPDIR)/dbus-auth-util.Plo
+include ./$(DEPDIR)/dbus-auth.Plo
+include ./$(DEPDIR)/dbus-bus.Plo
+include ./$(DEPDIR)/dbus-connection.Plo
+include ./$(DEPDIR)/dbus-credentials-util.Plo
+include ./$(DEPDIR)/dbus-credentials.Plo
+include ./$(DEPDIR)/dbus-dataslot.Plo
+include ./$(DEPDIR)/dbus-errors.Plo
+include ./$(DEPDIR)/dbus-hash.Plo
+include ./$(DEPDIR)/dbus-internals.Plo
+include ./$(DEPDIR)/dbus-keyring.Plo
+include ./$(DEPDIR)/dbus-list.Plo
+include ./$(DEPDIR)/dbus-mainloop.Plo
+include ./$(DEPDIR)/dbus-marshal-basic.Plo
+include ./$(DEPDIR)/dbus-marshal-byteswap-util.Plo
+include ./$(DEPDIR)/dbus-marshal-byteswap.Plo
+include ./$(DEPDIR)/dbus-marshal-header.Plo
+include ./$(DEPDIR)/dbus-marshal-recursive-util.Plo
+include ./$(DEPDIR)/dbus-marshal-recursive.Plo
+include ./$(DEPDIR)/dbus-marshal-validate-util.Plo
+include ./$(DEPDIR)/dbus-marshal-validate.Plo
+include ./$(DEPDIR)/dbus-memory.Plo
+include ./$(DEPDIR)/dbus-mempool.Plo
+include ./$(DEPDIR)/dbus-message-factory.Plo
+include ./$(DEPDIR)/dbus-message-util.Plo
+include ./$(DEPDIR)/dbus-message.Plo
+include ./$(DEPDIR)/dbus-misc.Plo
+include ./$(DEPDIR)/dbus-object-tree.Plo
+include ./$(DEPDIR)/dbus-pending-call.Plo
+include ./$(DEPDIR)/dbus-resources.Plo
+include ./$(DEPDIR)/dbus-server-debug-pipe.Plo
+include ./$(DEPDIR)/dbus-server-socket.Plo
+include ./$(DEPDIR)/dbus-server-unix.Plo
+include ./$(DEPDIR)/dbus-server.Plo
+include ./$(DEPDIR)/dbus-sha.Plo
+include ./$(DEPDIR)/dbus-shell.Plo
+include ./$(DEPDIR)/dbus-signature.Plo
+include ./$(DEPDIR)/dbus-spawn.Plo
+include ./$(DEPDIR)/dbus-string-util.Plo
+include ./$(DEPDIR)/dbus-string.Plo
+include ./$(DEPDIR)/dbus-sysdeps-pthread.Plo
+include ./$(DEPDIR)/dbus-sysdeps-unix.Plo
+include ./$(DEPDIR)/dbus-sysdeps-util-unix.Plo
+include ./$(DEPDIR)/dbus-sysdeps-util.Plo
+include ./$(DEPDIR)/dbus-sysdeps.Plo
+include ./$(DEPDIR)/dbus-test-main.Po
+include ./$(DEPDIR)/dbus-test.Plo
+include ./$(DEPDIR)/dbus-threads.Plo
+include ./$(DEPDIR)/dbus-timeout.Plo
+include ./$(DEPDIR)/dbus-transport-socket.Plo
+include ./$(DEPDIR)/dbus-transport-unix.Plo
+include ./$(DEPDIR)/dbus-transport.Plo
+include ./$(DEPDIR)/dbus-userdb-util.Plo
+include ./$(DEPDIR)/dbus-userdb.Plo
+include ./$(DEPDIR)/dbus-uuidgen.Plo
+include ./$(DEPDIR)/dbus-watch.Plo
+
+.c.o:
+       if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+       then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+#      source='$<' object='$@' libtool=no \
+#      depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' \
+#      $(CCDEPMODE) $(depcomp) \
+#      $(COMPILE) -c $<
+
+.c.obj:
+       if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+       then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+#      source='$<' object='$@' libtool=no \
+#      depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' \
+#      $(CCDEPMODE) $(depcomp) \
+#      $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+       if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+       then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+#      source='$<' object='$@' libtool=yes \
+#      depfile='$(DEPDIR)/$*.Plo' tmpdepfile='$(DEPDIR)/$*.TPlo' \
+#      $(CCDEPMODE) $(depcomp) \
+#      $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+       -rm -f *.lo
+
+clean-libtool:
+       -rm -rf .libs _libs
+
+distclean-libtool:
+       -rm -f libtool
+uninstall-info-am:
+install-dbusarchincludeHEADERS: $(dbusarchinclude_HEADERS)
+       @$(NORMAL_INSTALL)
+       test -z "$(dbusarchincludedir)" || $(mkdir_p) "$(DESTDIR)$(dbusarchincludedir)"
+       @list='$(dbusarchinclude_HEADERS)'; for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         f="`echo $$p | sed -e 's|^.*/||'`"; \
+         echo " $(dbusarchincludeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(dbusarchincludedir)/$$f'"; \
+         $(dbusarchincludeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(dbusarchincludedir)/$$f"; \
+       done
+
+uninstall-dbusarchincludeHEADERS:
+       @$(NORMAL_UNINSTALL)
+       @list='$(dbusarchinclude_HEADERS)'; for p in $$list; do \
+         f="`echo $$p | sed -e 's|^.*/||'`"; \
+         echo " rm -f '$(DESTDIR)$(dbusarchincludedir)/$$f'"; \
+         rm -f "$(DESTDIR)$(dbusarchincludedir)/$$f"; \
+       done
+install-dbusincludeHEADERS: $(dbusinclude_HEADERS)
+       @$(NORMAL_INSTALL)
+       test -z "$(dbusincludedir)" || $(mkdir_p) "$(DESTDIR)$(dbusincludedir)"
+       @list='$(dbusinclude_HEADERS)'; for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         f="`echo $$p | sed -e 's|^.*/||'`"; \
+         echo " $(dbusincludeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(dbusincludedir)/$$f'"; \
+         $(dbusincludeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(dbusincludedir)/$$f"; \
+       done
+
+uninstall-dbusincludeHEADERS:
+       @$(NORMAL_UNINSTALL)
+       @list='$(dbusinclude_HEADERS)'; for p in $$list; do \
+         f="`echo $$p | sed -e 's|^.*/||'`"; \
+         echo " rm -f '$(DESTDIR)$(dbusincludedir)/$$f'"; \
+         rm -f "$(DESTDIR)$(dbusincludedir)/$$f"; \
+       done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+       list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '    { files[$$0] = 1; } \
+              END { for (i in files) print i; }'`; \
+       mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       tags=; \
+       here=`pwd`; \
+       list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '    { files[$$0] = 1; } \
+              END { for (i in files) print i; }'`; \
+       if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+         test -n "$$unique" || unique=$$empty_fix; \
+         $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+           $$tags $$unique; \
+       fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       tags=; \
+       here=`pwd`; \
+       list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '    { files[$$0] = 1; } \
+              END { for (i in files) print i; }'`; \
+       test -z "$(CTAGS_ARGS)$$tags$$unique" \
+         || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+            $$tags $$unique
+
+GTAGS:
+       here=`$(am__cd) $(top_builddir) && pwd` \
+         && cd $(top_srcdir) \
+         && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+       -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+check-TESTS: $(TESTS)
+       @failed=0; all=0; xfail=0; xpass=0; skip=0; \
+       srcdir=$(srcdir); export srcdir; \
+       list='$(TESTS)'; \
+       if test -n "$$list"; then \
+         for tst in $$list; do \
+           if test -f ./$$tst; then dir=./; \
+           elif test -f $$tst; then dir=; \
+           else dir="$(srcdir)/"; fi; \
+           if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
+             all=`expr $$all + 1`; \
+             case " $(XFAIL_TESTS) " in \
+             *" $$tst "*) \
+               xpass=`expr $$xpass + 1`; \
+               failed=`expr $$failed + 1`; \
+               echo "XPASS: $$tst"; \
+             ;; \
+             *) \
+               echo "PASS: $$tst"; \
+             ;; \
+             esac; \
+           elif test $$? -ne 77; then \
+             all=`expr $$all + 1`; \
+             case " $(XFAIL_TESTS) " in \
+             *" $$tst "*) \
+               xfail=`expr $$xfail + 1`; \
+               echo "XFAIL: $$tst"; \
+             ;; \
+             *) \
+               failed=`expr $$failed + 1`; \
+               echo "FAIL: $$tst"; \
+             ;; \
+             esac; \
+           else \
+             skip=`expr $$skip + 1`; \
+             echo "SKIP: $$tst"; \
+           fi; \
+         done; \
+         if test "$$failed" -eq 0; then \
+           if test "$$xfail" -eq 0; then \
+             banner="All $$all tests passed"; \
+           else \
+             banner="All $$all tests behaved as expected ($$xfail expected failures)"; \
+           fi; \
+         else \
+           if test "$$xpass" -eq 0; then \
+             banner="$$failed of $$all tests failed"; \
+           else \
+             banner="$$failed of $$all tests did not behave as expected ($$xpass unexpected passes)"; \
+           fi; \
+         fi; \
+         dashes="$$banner"; \
+         skipped=""; \
+         if test "$$skip" -ne 0; then \
+           skipped="($$skip tests were not run)"; \
+           test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+             dashes="$$skipped"; \
+         fi; \
+         report=""; \
+         if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+           report="Please report to $(PACKAGE_BUGREPORT)"; \
+           test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+             dashes="$$report"; \
+         fi; \
+         dashes=`echo "$$dashes" | sed s/./=/g`; \
+         echo "$$dashes"; \
+         echo "$$banner"; \
+         test -z "$$skipped" || echo "$$skipped"; \
+         test -z "$$report" || echo "$$report"; \
+         echo "$$dashes"; \
+         test "$$failed" -eq 0; \
+       else :; fi
+
+distdir: $(DISTFILES)
+       @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+       topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+       list='$(DISTFILES)'; for file in $$list; do \
+         case $$file in \
+           $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+           $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+         esac; \
+         if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+         dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+         if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+           dir="/$$dir"; \
+           $(mkdir_p) "$(distdir)$$dir"; \
+         else \
+           dir=''; \
+         fi; \
+         if test -d $$d/$$file; then \
+           if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+             cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+           fi; \
+           cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+         else \
+           test -f $(distdir)/$$file \
+           || cp -p $$d/$$file $(distdir)/$$file \
+           || exit 1; \
+         fi; \
+       done
+check-am: all-am
+       $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS)
+installdirs:
+       for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(dbusarchincludedir)" "$(DESTDIR)$(dbusincludedir)"; do \
+         test -z "$$dir" || $(mkdir_p) "$$dir"; \
+       done
+install: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+       $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+         install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+         `test -z '$(STRIP)' || \
+           echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+       -rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+       @echo "This command is intended for maintainers to use"
+       @echo "it deletes files that may require special tools to rebuild."
+       -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool clean-local \
+       clean-noinstLTLIBRARIES clean-noinstPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+       -rm -rf ./$(DEPDIR)
+       -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+       distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-dbusarchincludeHEADERS \
+       install-dbusincludeHEADERS
+
+install-exec-am: install-libLTLIBRARIES
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+       -rm -rf ./$(DEPDIR)
+       -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+       mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-dbusarchincludeHEADERS \
+       uninstall-dbusincludeHEADERS uninstall-info-am \
+       uninstall-libLTLIBRARIES
+
+.PHONY: CTAGS GTAGS all all-am check check-TESTS check-am clean \
+       clean-generic clean-libLTLIBRARIES clean-libtool clean-local \
+       clean-noinstLTLIBRARIES clean-noinstPROGRAMS ctags distclean \
+       distclean-compile distclean-generic distclean-libtool \
+       distclean-tags distdir dvi dvi-am html html-am info info-am \
+       install install-am install-data install-data-am \
+       install-dbusarchincludeHEADERS install-dbusincludeHEADERS \
+       install-exec install-exec-am install-info install-info-am \
+       install-libLTLIBRARIES install-man install-strip installcheck \
+       installcheck-am installdirs maintainer-clean \
+       maintainer-clean-generic mostlyclean mostlyclean-compile \
+       mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+       tags uninstall uninstall-am uninstall-dbusarchincludeHEADERS \
+       uninstall-dbusincludeHEADERS uninstall-info-am \
+       uninstall-libLTLIBRARIES
+
+
+clean-local:
+       /bin/rm *.bb *.bbg *.da *.gcov .libs/*.da .libs/*.bbg || true
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/dbus/Makefile.am b/src/dbus/Makefile.am
new file mode 100644 (file)
index 0000000..e966a43
--- /dev/null
@@ -0,0 +1,202 @@
+
+configdir=$(sysconfdir)/dbus-1
+
+INCLUDES=-I$(top_builddir) -I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) @PIC_CFLAGS@ -DDBUS_COMPILATION       \
+       -DDBUS_MACHINE_UUID_FILE=\""$(localstatedir)/lib/dbus/machine-id"\"             \
+       -DDBUS_SYSTEM_CONFIG_FILE=\""$(configdir)/system.conf"\"                        \
+       -DDBUS_SESSION_CONFIG_FILE=\""$(configdir)/session.conf"\"
+
+dbusincludedir=$(includedir)/dbus-1.0/dbus
+dbusarchincludedir=$(libdir)/dbus-1.0/include/dbus
+
+lib_LTLIBRARIES=libdbus-1.la
+
+dbusinclude_HEADERS=                           \
+       dbus.h                                  \
+       dbus-address.h                          \
+       dbus-bus.h                              \
+       dbus-connection.h                       \
+       dbus-errors.h                           \
+       dbus-macros.h                           \
+       dbus-memory.h                           \
+       dbus-message.h                          \
+       dbus-misc.h                             \
+       dbus-pending-call.h                     \
+       dbus-protocol.h                         \
+       dbus-server.h                           \
+       dbus-shared.h                           \
+       dbus-signature.h                        \
+       dbus-threads.h                          \
+       dbus-types.h
+
+
+dbusarchinclude_HEADERS=                       \
+       dbus-arch-deps.h
+
+### source code that goes in the installed client library
+### and is specific to library functionality
+DBUS_LIB_SOURCES=                              \
+       dbus-address.c                          \
+       dbus-auth.c                             \
+       dbus-auth.h                             \
+       dbus-auth-script.c                      \
+       dbus-auth-script.h                      \
+       dbus-bus.c                              \
+       dbus-connection.c                       \
+       dbus-connection-internal.h              \
+       dbus-credentials.c                      \
+       dbus-credentials.h                      \
+       dbus-errors.c                           \
+       dbus-keyring.c                          \
+       dbus-keyring.h                          \
+       dbus-marshal-header.c                   \
+       dbus-marshal-header.h                   \
+       dbus-marshal-byteswap.c                 \
+       dbus-marshal-byteswap.h                 \
+       dbus-marshal-recursive.c                \
+       dbus-marshal-recursive.h                \
+       dbus-marshal-validate.c                 \
+       dbus-marshal-validate.h                 \
+       dbus-message.c                          \
+       dbus-message-internal.h                 \
+       dbus-message-private.h                  \
+       dbus-misc.c                             \
+       dbus-object-tree.c                      \
+       dbus-object-tree.h                      \
+       dbus-pending-call.c                     \
+       dbus-pending-call-internal.h            \
+       dbus-resources.c                        \
+       dbus-resources.h                        \
+       dbus-server.c                           \
+       dbus-server-debug-pipe.c                \
+       dbus-server-debug-pipe.h                \
+       dbus-server-protected.h                 \
+       dbus-server-socket.c                    \
+       dbus-server-socket.h                    \
+       dbus-server-unix.c                      \
+       dbus-server-unix.h                      \
+       dbus-sha.c                              \
+       dbus-sha.h                              \
+       dbus-signature.c                        \
+       dbus-timeout.c                          \
+       dbus-timeout.h                          \
+       dbus-threads-internal.h                 \
+       dbus-threads.c                          \
+       dbus-transport.c                        \
+       dbus-transport.h                        \
+       dbus-transport-protected.h              \
+       dbus-transport-socket.c                 \
+       dbus-transport-socket.h                 \
+       dbus-transport-unix.c                   \
+       dbus-transport-unix.h                   \
+       dbus-uuidgen.c                          \
+       dbus-uuidgen.h                          \
+       dbus-watch.c                            \
+       dbus-watch.h
+
+##     dbus-md5.c                              \
+##     dbus-md5.h                              \
+
+### source code that goes in the installed client library
+### AND is generic utility functionality used by the 
+### daemon or test programs (all symbols in here should 
+### be underscore-prefixed)
+DBUS_SHARED_SOURCES=                           \
+       dbus-dataslot.c                         \
+       dbus-dataslot.h                         \
+       dbus-hash.c                             \
+       dbus-hash.h                             \
+       dbus-internals.c                        \
+       dbus-internals.h                        \
+       dbus-list.c                             \
+       dbus-list.h                             \
+       dbus-marshal-basic.c                    \
+       dbus-marshal-basic.h                    \
+       dbus-memory.c                           \
+       dbus-mempool.c                          \
+       dbus-mempool.h                          \
+       dbus-string.c                           \
+       dbus-string.h                           \
+       dbus-string-private.h                   \
+       dbus-sysdeps.c                          \
+       dbus-sysdeps.h                          \
+       dbus-sysdeps-pthread.c                  \
+       dbus-sysdeps-unix.c                     \
+       dbus-sysdeps-unix.h                     \
+       dbus-userdb.c                           \
+       dbus-userdb.h
+
+### source code that is generic utility functionality used
+### by the bus daemon or test apps, but is NOT included
+### in the D-Bus client library (all symbols in here 
+### should be underscore-prefixed but don't really need 
+### to be unless they move to DBUS_SHARED_SOURCES later)
+DBUS_UTIL_SOURCES=                             \
+       dbus-auth-util.c                        \
+       dbus-credentials-util.c                 \
+       dbus-mainloop.c                         \
+       dbus-mainloop.h                         \
+       dbus-marshal-byteswap-util.c            \
+       dbus-marshal-recursive-util.c           \
+       dbus-marshal-validate-util.c            \
+       dbus-message-factory.c                  \
+       dbus-message-factory.h                  \
+       dbus-message-util.c                     \
+       dbus-shell.c                            \
+       dbus-shell.h                            \
+       dbus-spawn.c                            \
+       dbus-spawn.h                            \
+       dbus-string-util.c                      \
+       dbus-sysdeps-util.c                     \
+       dbus-sysdeps-util-unix.c                \
+       dbus-test.c                             \
+       dbus-test.h                             \
+       dbus-userdb-util.c
+
+libdbus_1_la_SOURCES=                          \
+       $(DBUS_LIB_SOURCES)                     \
+       $(DBUS_SHARED_SOURCES)
+
+libdbus_convenience_la_SOURCES=                        \
+       $(DBUS_LIB_SOURCES)                     \
+       $(DBUS_SHARED_SOURCES)                  \
+       $(DBUS_UTIL_SOURCES)
+
+
+BUILT_SOURCES=$(dbusarchinclude_HEADERS)
+EXTRA_DIST=dbus-arch-deps.h.in
+
+## this library is the same as libdbus, but exports all the symbols
+## and is only used for static linking within the dbus package.
+noinst_LTLIBRARIES=libdbus-convenience.la
+
+libdbus_1_la_LIBADD= $(DBUS_CLIENT_LIBS)
+## don't export symbols that start with "_" (we use this 
+## convention for internal symbols)
+libdbus_1_la_LDFLAGS= -export-symbols-regex "^[^_].*" -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) -no-undefined @R_DYNAMIC_LDFLAG@ @PIC_LDFLAGS@
+
+libdbus_convenience_la_LDFLAGS=@R_DYNAMIC_LDFLAG@
+
+## note that TESTS has special meaning (stuff to use in make check)
+## so if adding tests not to be run in make check, don't add them to 
+## TESTS
+if DBUS_BUILD_TESTS
+TESTS_ENVIRONMENT=DBUS_TEST_DATA=$(top_builddir)/test/data DBUS_TEST_HOMEDIR=$(top_builddir)/dbus
+TESTS=dbus-test
+else
+TESTS=
+endif
+
+## we use noinst_PROGRAMS not check_PROGRAMS so that we build 
+## even when not doing "make check"
+noinst_PROGRAMS=$(TESTS)
+
+dbus_test_SOURCES=                             \
+       dbus-test-main.c
+
+dbus_test_LDADD=libdbus-convenience.la $(DBUS_TEST_LIBS)
+dbus_test_LDFLAGS=@R_DYNAMIC_LDFLAG@
+
+## mop up the gcov files
+clean-local:
+       /bin/rm *.bb *.bbg *.da *.gcov .libs/*.da .libs/*.bbg || true
diff --git a/src/dbus/Makefile.in b/src/dbus/Makefile.in
new file mode 100644 (file)
index 0000000..caf9380
--- /dev/null
@@ -0,0 +1,1026 @@
+# Makefile.in generated by automake 1.8.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004  Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+SOURCES = $(libdbus_1_la_SOURCES) $(libdbus_convenience_la_SOURCES) $(dbus_test_SOURCES)
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ../..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+host_triplet = @host@
+noinst_PROGRAMS = $(am__EXEEXT_1)
+subdir = src/dbus
+DIST_COMMON = $(dbusarchinclude_HEADERS) $(dbusinclude_HEADERS) \
+       $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/pkg.m4 \
+       $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+       $(ACLOCAL_M4)
+mkinstalldirs = $(mkdir_p)
+CONFIG_HEADER = $(top_builddir)/src/config.h
+CONFIG_CLEAN_FILES =
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(dbusarchincludedir)" "$(DESTDIR)$(dbusincludedir)"
+libLTLIBRARIES_INSTALL = $(INSTALL)
+LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES)
+libdbus_1_la_DEPENDENCIES =
+am__objects_1 = dbus-address.lo dbus-auth.lo dbus-auth-script.lo \
+       dbus-bus.lo dbus-connection.lo dbus-credentials.lo \
+       dbus-errors.lo dbus-keyring.lo dbus-marshal-header.lo \
+       dbus-marshal-byteswap.lo dbus-marshal-recursive.lo \
+       dbus-marshal-validate.lo dbus-message.lo dbus-misc.lo \
+       dbus-object-tree.lo dbus-pending-call.lo dbus-resources.lo \
+       dbus-server.lo dbus-server-debug-pipe.lo dbus-server-socket.lo \
+       dbus-server-unix.lo dbus-sha.lo dbus-signature.lo \
+       dbus-timeout.lo dbus-threads.lo dbus-transport.lo \
+       dbus-transport-socket.lo dbus-transport-unix.lo \
+       dbus-uuidgen.lo dbus-watch.lo
+am__objects_2 = dbus-dataslot.lo dbus-hash.lo dbus-internals.lo \
+       dbus-list.lo dbus-marshal-basic.lo dbus-memory.lo \
+       dbus-mempool.lo dbus-string.lo dbus-sysdeps.lo \
+       dbus-sysdeps-pthread.lo dbus-sysdeps-unix.lo dbus-userdb.lo
+am_libdbus_1_la_OBJECTS = $(am__objects_1) $(am__objects_2)
+libdbus_1_la_OBJECTS = $(am_libdbus_1_la_OBJECTS)
+libdbus_convenience_la_LIBADD =
+am__objects_3 = dbus-auth-util.lo dbus-credentials-util.lo \
+       dbus-mainloop.lo dbus-marshal-byteswap-util.lo \
+       dbus-marshal-recursive-util.lo dbus-marshal-validate-util.lo \
+       dbus-message-factory.lo dbus-message-util.lo dbus-shell.lo \
+       dbus-spawn.lo dbus-string-util.lo dbus-sysdeps-util.lo \
+       dbus-sysdeps-util-unix.lo dbus-test.lo dbus-userdb-util.lo
+am_libdbus_convenience_la_OBJECTS = $(am__objects_1) $(am__objects_2) \
+       $(am__objects_3)
+libdbus_convenience_la_OBJECTS = $(am_libdbus_convenience_la_OBJECTS)
+@DBUS_BUILD_TESTS_TRUE@am__EXEEXT_1 = dbus-test$(EXEEXT)
+PROGRAMS = $(noinst_PROGRAMS)
+am_dbus_test_OBJECTS = dbus-test-main.$(OBJEXT)
+dbus_test_OBJECTS = $(am_dbus_test_OBJECTS)
+dbus_test_DEPENDENCIES = libdbus-convenience.la
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/src
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/dbus-address.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-auth-script.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-auth-util.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-auth.Plo ./$(DEPDIR)/dbus-bus.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-connection.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-credentials-util.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-credentials.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-dataslot.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-errors.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-hash.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-internals.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-keyring.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-list.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-mainloop.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-marshal-basic.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-marshal-byteswap-util.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-marshal-byteswap.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-marshal-header.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-marshal-recursive-util.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-marshal-recursive.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-marshal-validate-util.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-marshal-validate.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-memory.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-mempool.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-message-factory.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-message-util.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-message.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-misc.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-object-tree.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-pending-call.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-resources.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-server-debug-pipe.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-server-socket.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-server-unix.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-server.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-sha.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-shell.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-signature.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-spawn.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-string-util.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-string.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-sysdeps-pthread.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-sysdeps-unix.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-sysdeps-util-unix.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-sysdeps-util.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-sysdeps.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-test-main.Po \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-test.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-threads.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-timeout.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-transport-socket.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-transport-unix.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-transport.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-userdb-util.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-userdb.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-uuidgen.Plo \
+@AMDEP_TRUE@   ./$(DEPDIR)/dbus-watch.Plo
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+       $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) \
+       $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+       $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+       $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libdbus_1_la_SOURCES) $(libdbus_convenience_la_SOURCES) \
+       $(dbus_test_SOURCES)
+DIST_SOURCES = $(libdbus_1_la_SOURCES) \
+       $(libdbus_convenience_la_SOURCES) $(dbus_test_SOURCES)
+dbusarchincludeHEADERS_INSTALL = $(INSTALL_HEADER)
+dbusincludeHEADERS_INSTALL = $(INSTALL_HEADER)
+HEADERS = $(dbusarchinclude_HEADERS) $(dbusinclude_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+Audacious_CFLAGS = @Audacious_CFLAGS@
+Audacious_LIBS = @Audacious_LIBS@
+BMPx_CFLAGS = @BMPx_CFLAGS@
+BMPx_LIBS = @BMPx_LIBS@
+BUILD_APCUPSD_FALSE = @BUILD_APCUPSD_FALSE@
+BUILD_APCUPSD_TRUE = @BUILD_APCUPSD_TRUE@
+BUILD_ARCH = @BUILD_ARCH@
+BUILD_AUDACIOUS_FALSE = @BUILD_AUDACIOUS_FALSE@
+BUILD_AUDACIOUS_TRUE = @BUILD_AUDACIOUS_TRUE@
+BUILD_BMPX_FALSE = @BUILD_BMPX_FALSE@
+BUILD_BMPX_TRUE = @BUILD_BMPX_TRUE@
+BUILD_CONFIG_OUTPUT_FALSE = @BUILD_CONFIG_OUTPUT_FALSE@
+BUILD_CONFIG_OUTPUT_TRUE = @BUILD_CONFIG_OUTPUT_TRUE@
+BUILD_CURL_FALSE = @BUILD_CURL_FALSE@
+BUILD_CURL_TRUE = @BUILD_CURL_TRUE@
+BUILD_DATE = @BUILD_DATE@
+BUILD_EVE_FALSE = @BUILD_EVE_FALSE@
+BUILD_EVE_TRUE = @BUILD_EVE_TRUE@
+BUILD_FREEBSD_FALSE = @BUILD_FREEBSD_FALSE@
+BUILD_FREEBSD_TRUE = @BUILD_FREEBSD_TRUE@
+BUILD_HDDTEMP_FALSE = @BUILD_HDDTEMP_FALSE@
+BUILD_HDDTEMP_TRUE = @BUILD_HDDTEMP_TRUE@
+BUILD_IBM_FALSE = @BUILD_IBM_FALSE@
+BUILD_IBM_TRUE = @BUILD_IBM_TRUE@
+BUILD_ICONV_FALSE = @BUILD_ICONV_FALSE@
+BUILD_ICONV_TRUE = @BUILD_ICONV_TRUE@
+BUILD_IMLIB2_FALSE = @BUILD_IMLIB2_FALSE@
+BUILD_IMLIB2_TRUE = @BUILD_IMLIB2_TRUE@
+BUILD_LINUX_FALSE = @BUILD_LINUX_FALSE@
+BUILD_LINUX_TRUE = @BUILD_LINUX_TRUE@
+BUILD_LUA_CAIRO_FALSE = @BUILD_LUA_CAIRO_FALSE@
+BUILD_LUA_CAIRO_TRUE = @BUILD_LUA_CAIRO_TRUE@
+BUILD_LUA_FALSE = @BUILD_LUA_FALSE@
+BUILD_LUA_IMLIB2_FALSE = @BUILD_LUA_IMLIB2_FALSE@
+BUILD_LUA_IMLIB2_TRUE = @BUILD_LUA_IMLIB2_TRUE@
+BUILD_LUA_TRUE = @BUILD_LUA_TRUE@
+BUILD_MATH_FALSE = @BUILD_MATH_FALSE@
+BUILD_MATH_TRUE = @BUILD_MATH_TRUE@
+BUILD_MOC_FALSE = @BUILD_MOC_FALSE@
+BUILD_MOC_TRUE = @BUILD_MOC_TRUE@
+BUILD_MPD_FALSE = @BUILD_MPD_FALSE@
+BUILD_MPD_TRUE = @BUILD_MPD_TRUE@
+BUILD_NCURSES_FALSE = @BUILD_NCURSES_FALSE@
+BUILD_NCURSES_TRUE = @BUILD_NCURSES_TRUE@
+BUILD_NVIDIA_FALSE = @BUILD_NVIDIA_FALSE@
+BUILD_NVIDIA_TRUE = @BUILD_NVIDIA_TRUE@
+BUILD_OPENBSD_FALSE = @BUILD_OPENBSD_FALSE@
+BUILD_OPENBSD_TRUE = @BUILD_OPENBSD_TRUE@
+BUILD_PORT_MONITORS_FALSE = @BUILD_PORT_MONITORS_FALSE@
+BUILD_PORT_MONITORS_TRUE = @BUILD_PORT_MONITORS_TRUE@
+BUILD_RSS_FALSE = @BUILD_RSS_FALSE@
+BUILD_RSS_TRUE = @BUILD_RSS_TRUE@
+BUILD_WEATHER_FALSE = @BUILD_WEATHER_FALSE@
+BUILD_WEATHER_TRUE = @BUILD_WEATHER_TRUE@
+BUILD_WLAN_FALSE = @BUILD_WLAN_FALSE@
+BUILD_WLAN_TRUE = @BUILD_WLAN_TRUE@
+BUILD_X11_FALSE = @BUILD_X11_FALSE@
+BUILD_X11_TRUE = @BUILD_X11_TRUE@
+BUILD_XMMS2_FALSE = @BUILD_XMMS2_FALSE@
+BUILD_XMMS2_TRUE = @BUILD_XMMS2_TRUE@
+BUILD_XOAP_FALSE = @BUILD_XOAP_FALSE@
+BUILD_XOAP_TRUE = @BUILD_XOAP_TRUE@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GLib2_CFLAGS = @GLib2_CFLAGS@
+GLib2_LIBS = @GLib2_LIBS@
+GREP = @GREP@
+HAVE_DOCSTUFF_FALSE = @HAVE_DOCSTUFF_FALSE@
+HAVE_DOCSTUFF_TRUE = @HAVE_DOCSTUFF_TRUE@
+HAVE_PKGCONFIG = @HAVE_PKGCONFIG@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+Imlib2_CFLAGS = @Imlib2_CFLAGS@
+Imlib2_LIBS = @Imlib2_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBOBJS = @LTLIBOBJS@
+LUA51_CFLAGS = @LUA51_CFLAGS@
+LUA51_LIBS = @LUA51_LIBS@
+LUA_CFLAGS = @LUA_CFLAGS@
+LUA_LIBS = @LUA_LIBS@
+MAKEINFO = @MAKEINFO@
+NMEDIT = @NMEDIT@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+X11_CFLAGS = @X11_CFLAGS@
+X11_LIBS = @X11_LIBS@
+XDamage_CFLAGS = @XDamage_CFLAGS@
+XDamage_LIBS = @XDamage_LIBS@
+XMKMF = @XMKMF@
+XMMS2_CFLAGS = @XMMS2_CFLAGS@
+XMMS2_LIBS = @XMMS2_LIBS@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Xext_CFLAGS = @Xext_CFLAGS@
+Xext_LIBS = @Xext_LIBS@
+Xft_CFLAGS = @Xft_CFLAGS@
+Xft_LIBS = @Xft_LIBS@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+cairo_CFLAGS = @cairo_CFLAGS@
+cairo_LIBS = @cairo_LIBS@
+cairo_xlib_CFLAGS = @cairo_xlib_CFLAGS@
+cairo_xlib_LIBS = @cairo_xlib_LIBS@
+conky_CFLAGS = @conky_CFLAGS@
+conky_LIBS = @conky_LIBS@
+datadir = @datadir@
+datarootdir = @datarootdir@
+db2x_manxml_cmd = @db2x_manxml_cmd@
+db2x_xsltproc_cmd = @db2x_xsltproc_cmd@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libcurl_CFLAGS = @libcurl_CFLAGS@
+libcurl_LIBS = @libcurl_LIBS@
+libdir = @libdir@
+libexecdir = @libexecdir@
+libxml2_CFLAGS = @libxml2_CFLAGS@
+libxml2_LIBS = @libxml2_LIBS@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+tolua_CFLAGS = @tolua_CFLAGS@
+tolua_LIBS = @tolua_LIBS@
+toluapp = @toluapp@
+xsltproc_cmd = @xsltproc_cmd@
+configdir = $(sysconfdir)/dbus-1
+INCLUDES = -I$(top_builddir) -I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) @PIC_CFLAGS@ -DDBUS_COMPILATION     \
+       -DDBUS_MACHINE_UUID_FILE=\""$(localstatedir)/lib/dbus/machine-id"\"             \
+       -DDBUS_SYSTEM_CONFIG_FILE=\""$(configdir)/system.conf"\"                        \
+       -DDBUS_SESSION_CONFIG_FILE=\""$(configdir)/session.conf"\"
+
+dbusincludedir = $(includedir)/dbus-1.0/dbus
+dbusarchincludedir = $(libdir)/dbus-1.0/include/dbus
+lib_LTLIBRARIES = libdbus-1.la
+dbusinclude_HEADERS = \
+       dbus.h                                  \
+       dbus-address.h                          \
+       dbus-bus.h                              \
+       dbus-connection.h                       \
+       dbus-errors.h                           \
+       dbus-macros.h                           \
+       dbus-memory.h                           \
+       dbus-message.h                          \
+       dbus-misc.h                             \
+       dbus-pending-call.h                     \
+       dbus-protocol.h                         \
+       dbus-server.h                           \
+       dbus-shared.h                           \
+       dbus-signature.h                        \
+       dbus-threads.h                          \
+       dbus-types.h
+
+dbusarchinclude_HEADERS = \
+       dbus-arch-deps.h
+
+
+### source code that goes in the installed client library
+### and is specific to library functionality
+DBUS_LIB_SOURCES = \
+       dbus-address.c                          \
+       dbus-auth.c                             \
+       dbus-auth.h                             \
+       dbus-auth-script.c                      \
+       dbus-auth-script.h                      \
+       dbus-bus.c                              \
+       dbus-connection.c                       \
+       dbus-connection-internal.h              \
+       dbus-credentials.c                      \
+       dbus-credentials.h                      \
+       dbus-errors.c                           \
+       dbus-keyring.c                          \
+       dbus-keyring.h                          \
+       dbus-marshal-header.c                   \
+       dbus-marshal-header.h                   \
+       dbus-marshal-byteswap.c                 \
+       dbus-marshal-byteswap.h                 \
+       dbus-marshal-recursive.c                \
+       dbus-marshal-recursive.h                \
+       dbus-marshal-validate.c                 \
+       dbus-marshal-validate.h                 \
+       dbus-message.c                          \
+       dbus-message-internal.h                 \
+       dbus-message-private.h                  \
+       dbus-misc.c                             \
+       dbus-object-tree.c                      \
+       dbus-object-tree.h                      \
+       dbus-pending-call.c                     \
+       dbus-pending-call-internal.h            \
+       dbus-resources.c                        \
+       dbus-resources.h                        \
+       dbus-server.c                           \
+       dbus-server-debug-pipe.c                \
+       dbus-server-debug-pipe.h                \
+       dbus-server-protected.h                 \
+       dbus-server-socket.c                    \
+       dbus-server-socket.h                    \
+       dbus-server-unix.c                      \
+       dbus-server-unix.h                      \
+       dbus-sha.c                              \
+       dbus-sha.h                              \
+       dbus-signature.c                        \
+       dbus-timeout.c                          \
+       dbus-timeout.h                          \
+       dbus-threads-internal.h                 \
+       dbus-threads.c                          \
+       dbus-transport.c                        \
+       dbus-transport.h                        \
+       dbus-transport-protected.h              \
+       dbus-transport-socket.c                 \
+       dbus-transport-socket.h                 \
+       dbus-transport-unix.c                   \
+       dbus-transport-unix.h                   \
+       dbus-uuidgen.c                          \
+       dbus-uuidgen.h                          \
+       dbus-watch.c                            \
+       dbus-watch.h
+
+
+### source code that goes in the installed client library
+### AND is generic utility functionality used by the 
+### daemon or test programs (all symbols in here should 
+### be underscore-prefixed)
+DBUS_SHARED_SOURCES = \
+       dbus-dataslot.c                         \
+       dbus-dataslot.h                         \
+       dbus-hash.c                             \
+       dbus-hash.h                             \
+       dbus-internals.c                        \
+       dbus-internals.h                        \
+       dbus-list.c                             \
+       dbus-list.h                             \
+       dbus-marshal-basic.c                    \
+       dbus-marshal-basic.h                    \
+       dbus-memory.c                           \
+       dbus-mempool.c                          \
+       dbus-mempool.h                          \
+       dbus-string.c                           \
+       dbus-string.h                           \
+       dbus-string-private.h                   \
+       dbus-sysdeps.c                          \
+       dbus-sysdeps.h                          \
+       dbus-sysdeps-pthread.c                  \
+       dbus-sysdeps-unix.c                     \
+       dbus-sysdeps-unix.h                     \
+       dbus-userdb.c                           \
+       dbus-userdb.h
+
+
+### source code that is generic utility functionality used
+### by the bus daemon or test apps, but is NOT included
+### in the D-Bus client library (all symbols in here 
+### should be underscore-prefixed but don't really need 
+### to be unless they move to DBUS_SHARED_SOURCES later)
+DBUS_UTIL_SOURCES = \
+       dbus-auth-util.c                        \
+       dbus-credentials-util.c                 \
+       dbus-mainloop.c                         \
+       dbus-mainloop.h                         \
+       dbus-marshal-byteswap-util.c            \
+       dbus-marshal-recursive-util.c           \
+       dbus-marshal-validate-util.c            \
+       dbus-message-factory.c                  \
+       dbus-message-factory.h                  \
+       dbus-message-util.c                     \
+       dbus-shell.c                            \
+       dbus-shell.h                            \
+       dbus-spawn.c                            \
+       dbus-spawn.h                            \
+       dbus-string-util.c                      \
+       dbus-sysdeps-util.c                     \
+       dbus-sysdeps-util-unix.c                \
+       dbus-test.c                             \
+       dbus-test.h                             \
+       dbus-userdb-util.c
+
+libdbus_1_la_SOURCES = \
+       $(DBUS_LIB_SOURCES)                     \
+       $(DBUS_SHARED_SOURCES)
+
+libdbus_convenience_la_SOURCES = \
+       $(DBUS_LIB_SOURCES)                     \
+       $(DBUS_SHARED_SOURCES)                  \
+       $(DBUS_UTIL_SOURCES)
+
+BUILT_SOURCES = $(dbusarchinclude_HEADERS)
+EXTRA_DIST = dbus-arch-deps.h.in
+noinst_LTLIBRARIES = libdbus-convenience.la
+libdbus_1_la_LIBADD = $(DBUS_CLIENT_LIBS)
+libdbus_1_la_LDFLAGS = -export-symbols-regex "^[^_].*" -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) -no-undefined @R_DYNAMIC_LDFLAG@ @PIC_LDFLAGS@
+libdbus_convenience_la_LDFLAGS = @R_DYNAMIC_LDFLAG@
+@DBUS_BUILD_TESTS_TRUE@TESTS_ENVIRONMENT = DBUS_TEST_DATA=$(top_builddir)/test/data DBUS_TEST_HOMEDIR=$(top_builddir)/dbus
+@DBUS_BUILD_TESTS_FALSE@TESTS = 
+@DBUS_BUILD_TESTS_TRUE@TESTS = dbus-test
+dbus_test_SOURCES = \
+       dbus-test-main.c
+
+dbus_test_LDADD = libdbus-convenience.la $(DBUS_TEST_LIBS)
+dbus_test_LDFLAGS = @R_DYNAMIC_LDFLAG@
+all: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+       @for dep in $?; do \
+         case '$(am__configure_deps)' in \
+           *$$dep*) \
+             cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+               && exit 0; \
+             exit 1;; \
+         esac; \
+       done; \
+       echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu  src/dbus/Makefile'; \
+       cd $(top_srcdir) && \
+         $(AUTOMAKE) --gnu  src/dbus/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+       @case '$?' in \
+         *config.status*) \
+           cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+         *) \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+       esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+       @$(NORMAL_INSTALL)
+       test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)"
+       @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+         if test -f $$p; then \
+           f="`echo $$p | sed -e 's|^.*/||'`"; \
+           echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
+           $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
+         else :; fi; \
+       done
+
+uninstall-libLTLIBRARIES:
+       @$(NORMAL_UNINSTALL)
+       @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+           p="`echo $$p | sed -e 's|^.*/||'`"; \
+         echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \
+         $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \
+       done
+
+clean-libLTLIBRARIES:
+       -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+       @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+         dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+         test "$$dir" != "$$p" || dir=.; \
+         echo "rm -f \"$${dir}/so_locations\""; \
+         rm -f "$${dir}/so_locations"; \
+       done
+
+clean-noinstLTLIBRARIES:
+       -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+       @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+         dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+         test "$$dir" != "$$p" || dir=.; \
+         echo "rm -f \"$${dir}/so_locations\""; \
+         rm -f "$${dir}/so_locations"; \
+       done
+libdbus-1.la: $(libdbus_1_la_OBJECTS) $(libdbus_1_la_DEPENDENCIES) 
+       $(LINK) -rpath $(libdir) $(libdbus_1_la_LDFLAGS) $(libdbus_1_la_OBJECTS) $(libdbus_1_la_LIBADD) $(LIBS)
+libdbus-convenience.la: $(libdbus_convenience_la_OBJECTS) $(libdbus_convenience_la_DEPENDENCIES) 
+       $(LINK)  $(libdbus_convenience_la_LDFLAGS) $(libdbus_convenience_la_OBJECTS) $(libdbus_convenience_la_LIBADD) $(LIBS)
+
+clean-noinstPROGRAMS:
+       @list='$(noinst_PROGRAMS)'; for p in $$list; do \
+         f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+         echo " rm -f $$p $$f"; \
+         rm -f $$p $$f ; \
+       done
+dbus-test$(EXEEXT): $(dbus_test_OBJECTS) $(dbus_test_DEPENDENCIES) 
+       @rm -f dbus-test$(EXEEXT)
+       $(LINK) $(dbus_test_LDFLAGS) $(dbus_test_OBJECTS) $(dbus_test_LDADD) $(LIBS)
+
+mostlyclean-compile:
+       -rm -f *.$(OBJEXT)
+
+distclean-compile:
+       -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-address.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-auth-script.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-auth-util.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-auth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-bus.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-connection.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-credentials-util.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-credentials.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-dataslot.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-errors.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-hash.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-internals.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-keyring.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-list.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-mainloop.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-marshal-basic.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-marshal-byteswap-util.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-marshal-byteswap.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-marshal-header.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-marshal-recursive-util.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-marshal-recursive.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-marshal-validate-util.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-marshal-validate.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-memory.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-mempool.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-message-factory.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-message-util.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-message.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-misc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-object-tree.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-pending-call.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-resources.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-server-debug-pipe.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-server-socket.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-server-unix.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-server.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-sha.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-shell.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-signature.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-spawn.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-string-util.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-string.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-sysdeps-pthread.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-sysdeps-unix.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-sysdeps-util-unix.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-sysdeps-util.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-sysdeps.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-test-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-test.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-threads.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-timeout.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-transport-socket.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-transport-unix.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-transport.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-userdb-util.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-userdb.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-uuidgen.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-watch.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@   if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@   if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@   if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      depfile='$(DEPDIR)/$*.Plo' tmpdepfile='$(DEPDIR)/$*.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+       -rm -f *.lo
+
+clean-libtool:
+       -rm -rf .libs _libs
+
+distclean-libtool:
+       -rm -f libtool
+uninstall-info-am:
+install-dbusarchincludeHEADERS: $(dbusarchinclude_HEADERS)
+       @$(NORMAL_INSTALL)
+       test -z "$(dbusarchincludedir)" || $(mkdir_p) "$(DESTDIR)$(dbusarchincludedir)"
+       @list='$(dbusarchinclude_HEADERS)'; for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         f="`echo $$p | sed -e 's|^.*/||'`"; \
+         echo " $(dbusarchincludeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(dbusarchincludedir)/$$f'"; \
+         $(dbusarchincludeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(dbusarchincludedir)/$$f"; \
+       done
+
+uninstall-dbusarchincludeHEADERS:
+       @$(NORMAL_UNINSTALL)
+       @list='$(dbusarchinclude_HEADERS)'; for p in $$list; do \
+         f="`echo $$p | sed -e 's|^.*/||'`"; \
+         echo " rm -f '$(DESTDIR)$(dbusarchincludedir)/$$f'"; \
+         rm -f "$(DESTDIR)$(dbusarchincludedir)/$$f"; \
+       done
+install-dbusincludeHEADERS: $(dbusinclude_HEADERS)
+       @$(NORMAL_INSTALL)
+       test -z "$(dbusincludedir)" || $(mkdir_p) "$(DESTDIR)$(dbusincludedir)"
+       @list='$(dbusinclude_HEADERS)'; for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         f="`echo $$p | sed -e 's|^.*/||'`"; \
+         echo " $(dbusincludeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(dbusincludedir)/$$f'"; \
+         $(dbusincludeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(dbusincludedir)/$$f"; \
+       done
+
+uninstall-dbusincludeHEADERS:
+       @$(NORMAL_UNINSTALL)
+       @list='$(dbusinclude_HEADERS)'; for p in $$list; do \
+         f="`echo $$p | sed -e 's|^.*/||'`"; \
+         echo " rm -f '$(DESTDIR)$(dbusincludedir)/$$f'"; \
+         rm -f "$(DESTDIR)$(dbusincludedir)/$$f"; \
+       done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+       list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '    { files[$$0] = 1; } \
+              END { for (i in files) print i; }'`; \
+       mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       tags=; \
+       here=`pwd`; \
+       list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '    { files[$$0] = 1; } \
+              END { for (i in files) print i; }'`; \
+       if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+         test -n "$$unique" || unique=$$empty_fix; \
+         $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+           $$tags $$unique; \
+       fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       tags=; \
+       here=`pwd`; \
+       list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '    { files[$$0] = 1; } \
+              END { for (i in files) print i; }'`; \
+       test -z "$(CTAGS_ARGS)$$tags$$unique" \
+         || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+            $$tags $$unique
+
+GTAGS:
+       here=`$(am__cd) $(top_builddir) && pwd` \
+         && cd $(top_srcdir) \
+         && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+       -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+check-TESTS: $(TESTS)
+       @failed=0; all=0; xfail=0; xpass=0; skip=0; \
+       srcdir=$(srcdir); export srcdir; \
+       list='$(TESTS)'; \
+       if test -n "$$list"; then \
+         for tst in $$list; do \
+           if test -f ./$$tst; then dir=./; \
+           elif test -f $$tst; then dir=; \
+           else dir="$(srcdir)/"; fi; \
+           if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
+             all=`expr $$all + 1`; \
+             case " $(XFAIL_TESTS) " in \
+             *" $$tst "*) \
+               xpass=`expr $$xpass + 1`; \
+               failed=`expr $$failed + 1`; \
+               echo "XPASS: $$tst"; \
+             ;; \
+             *) \
+               echo "PASS: $$tst"; \
+             ;; \
+             esac; \
+           elif test $$? -ne 77; then \
+             all=`expr $$all + 1`; \
+             case " $(XFAIL_TESTS) " in \
+             *" $$tst "*) \
+               xfail=`expr $$xfail + 1`; \
+               echo "XFAIL: $$tst"; \
+             ;; \
+             *) \
+               failed=`expr $$failed + 1`; \
+               echo "FAIL: $$tst"; \
+             ;; \
+             esac; \
+           else \
+             skip=`expr $$skip + 1`; \
+             echo "SKIP: $$tst"; \
+           fi; \
+         done; \
+         if test "$$failed" -eq 0; then \
+           if test "$$xfail" -eq 0; then \
+             banner="All $$all tests passed"; \
+           else \
+             banner="All $$all tests behaved as expected ($$xfail expected failures)"; \
+           fi; \
+         else \
+           if test "$$xpass" -eq 0; then \
+             banner="$$failed of $$all tests failed"; \
+           else \
+             banner="$$failed of $$all tests did not behave as expected ($$xpass unexpected passes)"; \
+           fi; \
+         fi; \
+         dashes="$$banner"; \
+         skipped=""; \
+         if test "$$skip" -ne 0; then \
+           skipped="($$skip tests were not run)"; \
+           test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+             dashes="$$skipped"; \
+         fi; \
+         report=""; \
+         if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+           report="Please report to $(PACKAGE_BUGREPORT)"; \
+           test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+             dashes="$$report"; \
+         fi; \
+         dashes=`echo "$$dashes" | sed s/./=/g`; \
+         echo "$$dashes"; \
+         echo "$$banner"; \
+         test -z "$$skipped" || echo "$$skipped"; \
+         test -z "$$report" || echo "$$report"; \
+         echo "$$dashes"; \
+         test "$$failed" -eq 0; \
+       else :; fi
+
+distdir: $(DISTFILES)
+       @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+       topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+       list='$(DISTFILES)'; for file in $$list; do \
+         case $$file in \
+           $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+           $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+         esac; \
+         if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+         dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+         if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+           dir="/$$dir"; \
+           $(mkdir_p) "$(distdir)$$dir"; \
+         else \
+           dir=''; \
+         fi; \
+         if test -d $$d/$$file; then \
+           if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+             cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+           fi; \
+           cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+         else \
+           test -f $(distdir)/$$file \
+           || cp -p $$d/$$file $(distdir)/$$file \
+           || exit 1; \
+         fi; \
+       done
+check-am: all-am
+       $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS)
+installdirs:
+       for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(dbusarchincludedir)" "$(DESTDIR)$(dbusincludedir)"; do \
+         test -z "$$dir" || $(mkdir_p) "$$dir"; \
+       done
+install: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+       $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+         install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+         `test -z '$(STRIP)' || \
+           echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+       -rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+       @echo "This command is intended for maintainers to use"
+       @echo "it deletes files that may require special tools to rebuild."
+       -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool clean-local \
+       clean-noinstLTLIBRARIES clean-noinstPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+       -rm -rf ./$(DEPDIR)
+       -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+       distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-dbusarchincludeHEADERS \
+       install-dbusincludeHEADERS
+
+install-exec-am: install-libLTLIBRARIES
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+       -rm -rf ./$(DEPDIR)
+       -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+       mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-dbusarchincludeHEADERS \
+       uninstall-dbusincludeHEADERS uninstall-info-am \
+       uninstall-libLTLIBRARIES
+
+.PHONY: CTAGS GTAGS all all-am check check-TESTS check-am clean \
+       clean-generic clean-libLTLIBRARIES clean-libtool clean-local \
+       clean-noinstLTLIBRARIES clean-noinstPROGRAMS ctags distclean \
+       distclean-compile distclean-generic distclean-libtool \
+       distclean-tags distdir dvi dvi-am html html-am info info-am \
+       install install-am install-data install-data-am \
+       install-dbusarchincludeHEADERS install-dbusincludeHEADERS \
+       install-exec install-exec-am install-info install-info-am \
+       install-libLTLIBRARIES install-man install-strip installcheck \
+       installcheck-am installdirs maintainer-clean \
+       maintainer-clean-generic mostlyclean mostlyclean-compile \
+       mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+       tags uninstall uninstall-am uninstall-dbusarchincludeHEADERS \
+       uninstall-dbusincludeHEADERS uninstall-info-am \
+       uninstall-libLTLIBRARIES
+
+
+clean-local:
+       /bin/rm *.bb *.bbg *.da *.gcov .libs/*.da .libs/*.bbg || true
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/dbus/dbus-address.c b/src/dbus/dbus-address.c
new file mode 100644 (file)
index 0000000..28e8842
--- /dev/null
@@ -0,0 +1,826 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-address.c  Server address parser.
+ *
+ * Copyright (C) 2003  CodeFactory AB
+ * Copyright (C) 2004,2005  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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 <config.h>
+#include "dbus-address.h"
+#include "dbus-internals.h"
+#include "dbus-list.h"
+#include "dbus-string.h"
+#include "dbus-protocol.h"
+
+/**
+ * @defgroup DBusAddressInternals Address parsing
+ * @ingroup  DBusInternals
+ * @brief Implementation of parsing addresses of D-Bus servers.
+ *
+ * @{
+ */
+
+/**
+ * Internals of DBusAddressEntry 
+ */
+struct DBusAddressEntry
+{
+  DBusString method; /**< The address type (unix, tcp, etc.) */
+
+  DBusList *keys;    /**< List of keys */
+  DBusList *values;  /**< List of values */
+};
+
+
+/**
+ *
+ * Sets #DBUS_ERROR_BAD_ADDRESS.
+ * If address_problem_type and address_problem_field are not #NULL,
+ * sets an error message about how the field is no good. Otherwise, sets
+ * address_problem_other as the error message.
+ * 
+ * @param error the error to set
+ * @param address_problem_type the address type of the bad address or #NULL
+ * @param address_problem_field the missing field of the bad address or #NULL
+ * @param address_problem_other any other error message or #NULL
+ */
+void
+_dbus_set_bad_address (DBusError  *error,
+                       const char *address_problem_type,
+                       const char *address_problem_field,
+                       const char *address_problem_other)
+{
+  if (address_problem_type != NULL)
+    dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+                    "Server address of type %s was missing argument %s",
+                    address_problem_type, address_problem_field);
+  else
+    dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+                    "Could not parse server address: %s",
+                    address_problem_other);
+}
+
+/**
+ * #TRUE if the byte need not be escaped when found in a dbus address.
+ * All other bytes are required to be escaped in a valid address.
+ */
+#define _DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE(b)        \
+         (((b) >= 'a' && (b) <= 'z') ||                 \
+          ((b) >= 'A' && (b) <= 'Z') ||                 \
+          ((b) >= '0' && (b) <= '9') ||                 \
+          (b) == '-' ||                                 \
+          (b) == '_' ||                                 \
+          (b) == '/' ||                                 \
+          (b) == '\\' ||                                \
+          (b) == '*' ||                                \
+          (b) == '.')
+
+/**
+ * Appends an escaped version of one string to another string,
+ * using the D-Bus address escaping mechanism
+ *
+ * @param escaped the string to append to
+ * @param unescaped the string to escape
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_address_append_escaped (DBusString       *escaped,
+                              const DBusString *unescaped)
+{
+  const char *p;
+  const char *end;
+  dbus_bool_t ret;
+  int orig_len;
+
+  ret = FALSE;
+
+  orig_len = _dbus_string_get_length (escaped);
+  p = _dbus_string_get_const_data (unescaped);
+  end = p + _dbus_string_get_length (unescaped);
+  while (p != end)
+    {
+      if (_DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE (*p))
+        {
+          if (!_dbus_string_append_byte (escaped, *p))
+            goto out;
+        }
+      else
+        {
+          if (!_dbus_string_append_byte (escaped, '%'))
+            goto out;
+          if (!_dbus_string_append_byte_as_hex (escaped, *p))
+            goto out;
+        }
+      
+      ++p;
+    }
+
+  ret = TRUE;
+  
+ out:
+  if (!ret)
+    _dbus_string_set_length (escaped, orig_len);
+  return ret;
+}
+
+/** @} */ /* End of internals */
+
+static void
+dbus_address_entry_free (DBusAddressEntry *entry)
+{
+  DBusList *link;
+  
+  _dbus_string_free (&entry->method);
+
+  link = _dbus_list_get_first_link (&entry->keys);
+  while (link != NULL)
+    {
+      _dbus_string_free (link->data);
+      dbus_free (link->data);
+      
+      link = _dbus_list_get_next_link (&entry->keys, link);
+    }
+  _dbus_list_clear (&entry->keys);
+  
+  link = _dbus_list_get_first_link (&entry->values);
+  while (link != NULL)
+    {
+      _dbus_string_free (link->data);
+      dbus_free (link->data);
+      
+      link = _dbus_list_get_next_link (&entry->values, link);
+    }
+  _dbus_list_clear (&entry->values);
+  
+  dbus_free (entry);
+}
+
+/**
+ * @defgroup DBusAddress Address parsing
+ * @ingroup  DBus
+ * @brief Parsing addresses of D-Bus servers.
+ *
+ * @{
+ */
+
+/**
+ * Frees a #NULL-terminated array of address entries.
+ *
+ * @param entries the array.
+ */
+void
+dbus_address_entries_free (DBusAddressEntry **entries)
+{
+  int i;
+  
+  for (i = 0; entries[i] != NULL; i++)
+    dbus_address_entry_free (entries[i]);
+  dbus_free (entries);
+}
+
+static DBusAddressEntry *
+create_entry (void)
+{
+  DBusAddressEntry *entry;
+
+  entry = dbus_new0 (DBusAddressEntry, 1);
+
+  if (entry == NULL)
+    return NULL;
+
+  if (!_dbus_string_init (&entry->method))
+    {
+      dbus_free (entry);
+      return NULL;
+    }
+
+  return entry;
+}
+
+/**
+ * Returns the method string of an address entry.  For example, given
+ * the address entry "tcp:host=example.com" it would return the string
+ * "tcp"
+ *
+ * @param entry the entry.
+ * @returns a string describing the method. This string
+ * must not be freed.
+ */
+const char *
+dbus_address_entry_get_method (DBusAddressEntry *entry)
+{
+  return _dbus_string_get_const_data (&entry->method);
+}
+
+/**
+ * Returns a value from a key of an entry. For example,
+ * given the address "tcp:host=example.com,port=8073" if you asked
+ * for the key "host" you would get the value "example.com"
+ *
+ * The returned value is already unescaped.
+ * 
+ * @param entry the entry.
+ * @param key the key.
+ * @returns the key value. This string must not be freed.
+ */
+const char *
+dbus_address_entry_get_value (DBusAddressEntry *entry,
+                             const char       *key)
+{
+  DBusList *values, *keys;
+
+  keys = _dbus_list_get_first_link (&entry->keys);
+  values = _dbus_list_get_first_link (&entry->values);
+
+  while (keys != NULL)
+    {
+      _dbus_assert (values != NULL);
+
+      if (_dbus_string_equal_c_str (keys->data, key))
+        return _dbus_string_get_const_data (values->data);
+
+      keys = _dbus_list_get_next_link (&entry->keys, keys);
+      values = _dbus_list_get_next_link (&entry->values, values);
+    }
+  
+  return NULL;
+}
+
+static dbus_bool_t
+append_unescaped_value (DBusString       *unescaped,
+                        const DBusString *escaped,
+                        int               escaped_start,
+                        int               escaped_len,
+                        DBusError        *error)
+{
+  const char *p;
+  const char *end;
+  dbus_bool_t ret;
+  
+  ret = FALSE;
+
+  p = _dbus_string_get_const_data (escaped) + escaped_start;
+  end = p + escaped_len;
+  while (p != end)
+    {
+      if (_DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE (*p))
+        {
+          if (!_dbus_string_append_byte (unescaped, *p))
+            goto out;
+        }
+      else if (*p == '%')
+        {
+          /* Efficiency is king */
+          char buf[3];
+          DBusString hex;
+          int hex_end;
+          
+          ++p;
+
+          if ((p + 2) > end)
+            {
+              dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+                              "In D-Bus address, percent character was not followed by two hex digits");
+              goto out;
+            }
+            
+          buf[0] = *p;
+          ++p;
+          buf[1] = *p;
+          buf[2] = '\0';
+
+          _dbus_string_init_const (&hex, buf);
+
+          if (!_dbus_string_hex_decode (&hex, 0, &hex_end,
+                                        unescaped,
+                                        _dbus_string_get_length (unescaped)))
+            goto out;
+
+          if (hex_end != 2)
+            {
+              dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+                              "In D-Bus address, percent character was followed by characters other than hex digits");
+              goto out;
+            }
+        }
+      else
+        {
+          /* Error, should have been escaped */
+          dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+                          "In D-Bus address, character '%c' should have been escaped\n",
+                          *p);
+          goto out;
+        }
+      
+      ++p;
+    }
+
+  ret = TRUE;
+  
+ out:
+  if (!ret && error && !dbus_error_is_set (error))
+    _DBUS_SET_OOM (error);
+
+  _dbus_assert (ret || error == NULL || dbus_error_is_set (error));
+  
+  return ret;
+}
+
+/**
+ * Parses an address string of the form:
+ *
+ * method:key=value,key=value;method:key=value
+ *
+ * See the D-Bus specification for complete docs on the format.
+ *
+ * When connecting to an address, the first address entries
+ * in the semicolon-separated list should be tried first.
+ * 
+ * @param address the address.
+ * @param entry return location to an array of entries.
+ * @param array_len return location for array length.
+ * @param error address where an error can be returned.
+ * @returns #TRUE on success, #FALSE otherwise.
+ */
+dbus_bool_t
+dbus_parse_address (const char         *address,
+                   DBusAddressEntry ***entry,
+                   int                *array_len,
+                    DBusError          *error)
+{
+  DBusString str;
+  int pos, end_pos, len, i;
+  DBusList *entries, *link;
+  DBusAddressEntry **entry_array;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  _dbus_string_init_const (&str, address);
+
+  entries = NULL;
+  pos = 0;
+  len = _dbus_string_get_length (&str);
+
+  if (len == 0)
+  {
+    dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+                    "Empty address '%s'", address);
+    goto error;
+  }
+  
+  while (pos < len)
+    {
+      DBusAddressEntry *entry;
+
+      int found_pos;
+
+      entry = create_entry ();
+      if (!entry)
+       {
+         dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+
+         goto error;
+       }
+      
+      /* Append the entry */
+      if (!_dbus_list_append (&entries, entry))
+       {
+         dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+         dbus_address_entry_free (entry);
+         goto error;
+       }
+      
+      /* Look for a semi-colon */
+      if (!_dbus_string_find (&str, pos, ";", &end_pos))
+       end_pos = len;
+      
+      /* Look for the colon : */
+      if (!_dbus_string_find_to (&str, pos, end_pos, ":", &found_pos))
+       {
+         dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, "Address does not contain a colon");
+         goto error;
+       }
+
+      if (!_dbus_string_copy_len (&str, pos, found_pos - pos, &entry->method, 0))
+       {
+         dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+         goto error;
+       }
+         
+      pos = found_pos + 1;
+
+      while (pos < end_pos)
+       {
+         int comma_pos, equals_pos;
+
+         if (!_dbus_string_find_to (&str, pos, end_pos, ",", &comma_pos))
+           comma_pos = end_pos;
+         
+         if (!_dbus_string_find_to (&str, pos, comma_pos, "=", &equals_pos) ||
+             equals_pos == pos || equals_pos + 1 == comma_pos)
+           {
+             dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+                              "'=' character not found or has no value following it");
+              goto error;
+           }
+         else
+           {
+             DBusString *key;
+             DBusString *value;
+
+             key = dbus_new0 (DBusString, 1);
+
+             if (!key)
+               {
+                 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);             
+                 goto error;
+               }
+
+             value = dbus_new0 (DBusString, 1);
+             if (!value)
+               {
+                 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+                 dbus_free (key);
+                 goto error;
+               }
+             
+             if (!_dbus_string_init (key))
+               {
+                 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+                 dbus_free (key);
+                 dbus_free (value);
+                 
+                 goto error;
+               }
+             
+             if (!_dbus_string_init (value))
+               {
+                 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+                 _dbus_string_free (key);
+
+                 dbus_free (key);
+                 dbus_free (value);              
+                 goto error;
+               }
+
+             if (!_dbus_string_copy_len (&str, pos, equals_pos - pos, key, 0))
+               {
+                 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+                 _dbus_string_free (key);
+                 _dbus_string_free (value);
+
+                 dbus_free (key);
+                 dbus_free (value);              
+                 goto error;
+               }
+
+             if (!append_unescaped_value (value, &str, equals_pos + 1,
+                                           comma_pos - equals_pos - 1, error))
+               {
+                  _dbus_assert (error == NULL || dbus_error_is_set (error));
+                 _dbus_string_free (key);
+                 _dbus_string_free (value);
+
+                 dbus_free (key);
+                 dbus_free (value);              
+                 goto error;
+               }
+
+             if (!_dbus_list_append (&entry->keys, key))
+               {
+                 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);             
+                 _dbus_string_free (key);
+                 _dbus_string_free (value);
+
+                 dbus_free (key);
+                 dbus_free (value);              
+                 goto error;
+               }
+
+             if (!_dbus_list_append (&entry->values, value))
+               {
+                 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);             
+                 _dbus_string_free (value);
+
+                 dbus_free (value);
+                 goto error;             
+               }
+           }
+
+         pos = comma_pos + 1;
+       }
+
+      pos = end_pos + 1;
+    }
+
+  *array_len = _dbus_list_get_length (&entries);
+  
+  entry_array = dbus_new (DBusAddressEntry *, *array_len + 1);
+
+  if (!entry_array)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      
+      goto error;
+    }
+  
+  entry_array [*array_len] = NULL;
+
+  link = _dbus_list_get_first_link (&entries);
+  i = 0;
+  while (link != NULL)
+    {
+      entry_array[i] = link->data;
+      i++;
+      link = _dbus_list_get_next_link (&entries, link);
+    }
+
+  _dbus_list_clear (&entries);
+  *entry = entry_array;
+
+  return TRUE;
+
+ error:
+  
+  link = _dbus_list_get_first_link (&entries);
+  while (link != NULL)
+    {
+      dbus_address_entry_free (link->data);
+      link = _dbus_list_get_next_link (&entries, link);
+    }
+
+  _dbus_list_clear (&entries);
+  
+  return FALSE;
+  
+}
+
+/**
+ * Escapes the given string as a value in a key=value pair
+ * for a D-Bus address.
+ *
+ * @param value the unescaped value
+ * @returns newly-allocated escaped value or #NULL if no memory
+ */
+char*
+dbus_address_escape_value (const char *value)
+{
+  DBusString escaped;
+  DBusString unescaped;
+  char *ret;
+
+  ret = NULL;
+
+  _dbus_string_init_const (&unescaped, value);
+  
+  if (!_dbus_string_init (&escaped))
+    return NULL;
+
+  if (!_dbus_address_append_escaped (&escaped, &unescaped))
+    goto out;
+  
+  if (!_dbus_string_steal_data (&escaped, &ret))
+    goto out;
+
+ out:
+  _dbus_string_free (&escaped);
+  return ret;
+}
+
+/**
+ * Unescapes the given string as a value in a key=value pair
+ * for a D-Bus address. Note that dbus_address_entry_get_value()
+ * returns an already-unescaped value.
+ *
+ * @param value the escaped value
+ * @param error error to set if the unescaping fails
+ * @returns newly-allocated unescaped value or #NULL if no memory
+ */
+char*
+dbus_address_unescape_value (const char *value,
+                             DBusError  *error)
+{
+  DBusString unescaped;
+  DBusString escaped;
+  char *ret;
+  
+  ret = NULL;
+
+  _dbus_string_init_const (&escaped, value);
+  
+  if (!_dbus_string_init (&unescaped))
+    return NULL;
+
+  if (!append_unescaped_value (&unescaped, &escaped,
+                               0, _dbus_string_get_length (&escaped),
+                               error))
+    goto out;
+  
+  if (!_dbus_string_steal_data (&unescaped, &ret))
+    goto out;
+
+ out:
+  if (ret == NULL && error && !dbus_error_is_set (error))
+    _DBUS_SET_OOM (error);
+
+  _dbus_assert (ret != NULL || error == NULL || dbus_error_is_set (error));
+  
+  _dbus_string_free (&unescaped);
+  return ret;
+}
+
+/** @} */ /* End of public API */
+
+#ifdef DBUS_BUILD_TESTS
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+#include "dbus-test.h"
+#include <stdlib.h>
+
+typedef struct
+{
+  const char *escaped;
+  const char *unescaped;
+} EscapeTest;
+
+static const EscapeTest escape_tests[] = {
+  { "abcde", "abcde" },
+  { "", "" },
+  { "%20%20", "  " },
+  { "%24", "$" },
+  { "%25", "%" },
+  { "abc%24", "abc$" },
+  { "%24abc", "$abc" },
+  { "abc%24abc", "abc$abc" },
+  { "/", "/" },
+  { "-", "-" },
+  { "_", "_" },
+  { "A", "A" },
+  { "I", "I" },
+  { "Z", "Z" },
+  { "a", "a" },
+  { "i", "i" },
+  { "z", "z" }
+};
+
+static const char* invalid_escaped_values[] = {
+  "%a",
+  "%q",
+  "%az",
+  "%%",
+  "%$$",
+  "abc%a",
+  "%axyz",
+  "%",
+  "$",
+  " ",
+};
+
+dbus_bool_t
+_dbus_address_test (void)
+{
+  DBusAddressEntry **entries;
+  int len;
+  DBusError error = DBUS_ERROR_INIT;
+  int i;
+
+  i = 0;
+  while (i < _DBUS_N_ELEMENTS (escape_tests))
+    {
+      const EscapeTest *test = &escape_tests[i];
+      char *escaped;
+      char *unescaped;
+
+      escaped = dbus_address_escape_value (test->unescaped);
+      if (escaped == NULL)
+        _dbus_assert_not_reached ("oom");
+
+      if (strcmp (escaped, test->escaped) != 0)
+        {
+          _dbus_warn ("Escaped '%s' as '%s' should have been '%s'\n",
+                      test->unescaped, escaped, test->escaped);
+          exit (1);
+        }
+      dbus_free (escaped);
+
+      unescaped = dbus_address_unescape_value (test->escaped, &error);
+      if (unescaped == NULL)
+        {
+          _dbus_warn ("Failed to unescape '%s': %s\n",
+                      test->escaped, error.message);
+          dbus_error_free (&error);
+          exit (1);
+        }
+
+      if (strcmp (unescaped, test->unescaped) != 0)
+        {
+          _dbus_warn ("Unescaped '%s' as '%s' should have been '%s'\n",
+                      test->escaped, unescaped, test->unescaped);
+          exit (1);
+        }
+      dbus_free (unescaped);
+      
+      ++i;
+    }
+
+  i = 0;
+  while (i < _DBUS_N_ELEMENTS (invalid_escaped_values))
+    {
+      char *unescaped;
+
+      unescaped = dbus_address_unescape_value (invalid_escaped_values[i],
+                                               &error);
+      if (unescaped != NULL)
+        {
+          _dbus_warn ("Should not have successfully unescaped '%s' to '%s'\n",
+                      invalid_escaped_values[i], unescaped);
+          dbus_free (unescaped);
+          exit (1);
+        }
+
+      _dbus_assert (dbus_error_is_set (&error));
+      dbus_error_free (&error);
+
+      ++i;
+    }
+  
+  if (!dbus_parse_address ("unix:path=/tmp/foo;debug:name=test,sliff=sloff;",
+                          &entries, &len, &error))
+    _dbus_assert_not_reached ("could not parse address");
+  _dbus_assert (len == 2);
+  _dbus_assert (strcmp (dbus_address_entry_get_value (entries[0], "path"), "/tmp/foo") == 0);
+  _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "name"), "test") == 0);
+  _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "sliff"), "sloff") == 0);
+  
+  dbus_address_entries_free (entries);
+
+  /* Different possible errors */
+  if (dbus_parse_address ("", &entries, &len, &error))
+    _dbus_assert_not_reached ("Parsed incorrect address.");
+  else
+    dbus_error_free (&error);
+
+  if (dbus_parse_address ("foo", &entries, &len, &error))
+    _dbus_assert_not_reached ("Parsed incorrect address.");
+  else
+    dbus_error_free (&error);
+  
+  if (dbus_parse_address ("foo:bar", &entries, &len, &error))
+    _dbus_assert_not_reached ("Parsed incorrect address.");
+  else
+    dbus_error_free (&error);
+  
+  if (dbus_parse_address ("foo:bar,baz", &entries, &len, &error))
+    _dbus_assert_not_reached ("Parsed incorrect address.");
+  else
+    dbus_error_free (&error);
+  
+  if (dbus_parse_address ("foo:bar=foo,baz", &entries, &len, &error))
+    _dbus_assert_not_reached ("Parsed incorrect address.");
+  else
+    dbus_error_free (&error);
+  
+  if (dbus_parse_address ("foo:bar=foo;baz", &entries, &len, &error))
+    _dbus_assert_not_reached ("Parsed incorrect address.");
+  else
+    dbus_error_free (&error);
+  
+  if (dbus_parse_address ("foo:=foo", &entries, &len, &error))
+    _dbus_assert_not_reached ("Parsed incorrect address.");
+  else
+    dbus_error_free (&error);
+  
+  if (dbus_parse_address ("foo:foo=", &entries, &len, &error))
+    _dbus_assert_not_reached ("Parsed incorrect address.");
+  else
+    dbus_error_free (&error);
+
+  if (dbus_parse_address ("foo:foo,bar=baz", &entries, &len, &error))
+    _dbus_assert_not_reached ("Parsed incorrect address.");
+  else
+    dbus_error_free (&error);
+
+  return TRUE;
+}
+
+#endif /* !DOXYGEN_SHOULD_SKIP_THIS */
+
+#endif
diff --git a/src/dbus/dbus-address.h b/src/dbus/dbus-address.h
new file mode 100644 (file)
index 0000000..c569044
--- /dev/null
@@ -0,0 +1,61 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-address.h  Server address parser.
+ *
+ * Copyright (C) 2003  CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_ADDRESS_H
+#define DBUS_ADDRESS_H
+
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-errors.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusAddress
+ * @{
+ */
+
+/** Opaque type representing one of the semicolon-separated items in an address */
+typedef struct DBusAddressEntry DBusAddressEntry;
+
+dbus_bool_t dbus_parse_address            (const char         *address,
+                                          DBusAddressEntry ***entry,
+                                          int                *array_len,
+                                          DBusError          *error);
+const char *dbus_address_entry_get_value  (DBusAddressEntry   *entry,
+                                          const char         *key);
+const char *dbus_address_entry_get_method (DBusAddressEntry   *entry);
+void        dbus_address_entries_free     (DBusAddressEntry  **entries);
+
+char* dbus_address_escape_value   (const char *value);
+char* dbus_address_unescape_value (const char *value,
+                                   DBusError  *error);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_ADDRESS_H */
+
diff --git a/src/dbus/dbus-arch-deps.h b/src/dbus/dbus-arch-deps.h
new file mode 100644 (file)
index 0000000..c526a2c
--- /dev/null
@@ -0,0 +1,67 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-arch-deps.h Header with architecture/compiler specific information, installed to libdir
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.0
+ * 
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_ARCH_DEPS_H
+#define DBUS_ARCH_DEPS_H
+
+#include <dbus/dbus-macros.h>
+
+DBUS_BEGIN_DECLS
+
+#if 1
+#define DBUS_HAVE_INT64 1
+_DBUS_GNUC_EXTENSION typedef long dbus_int64_t;
+_DBUS_GNUC_EXTENSION typedef unsigned long dbus_uint64_t;
+
+#define DBUS_INT64_CONSTANT(val)  (_DBUS_GNUC_EXTENSION (val##L))
+#define DBUS_UINT64_CONSTANT(val) (_DBUS_GNUC_EXTENSION (val##UL))
+
+#else
+#undef DBUS_HAVE_INT64
+#undef DBUS_INT64_CONSTANT
+#undef DBUS_UINT64_CONSTANT
+#endif
+
+typedef int dbus_int32_t;
+typedef unsigned int dbus_uint32_t;
+
+typedef short dbus_int16_t;
+typedef unsigned short dbus_uint16_t;
+
+/* This is not really arch-dependent, but it's not worth
+ * creating an additional generated header just for this
+ */
+#define DBUS_MAJOR_VERSION 1
+#define DBUS_MINOR_VERSION 2
+#define DBUS_MICRO_VERSION 14
+
+#define DBUS_VERSION_STRING "1.2.14"
+
+#define DBUS_VERSION ((1 << 16) | (2 << 8) | (14)) 
+
+DBUS_END_DECLS
+
+#endif /* DBUS_ARCH_DEPS_H */
diff --git a/src/dbus/dbus-arch-deps.h.in b/src/dbus/dbus-arch-deps.h.in
new file mode 100644 (file)
index 0000000..ca8e286
--- /dev/null
@@ -0,0 +1,67 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-arch-deps.h Header with architecture/compiler specific information, installed to libdir
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.0
+ * 
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_ARCH_DEPS_H
+#define DBUS_ARCH_DEPS_H
+
+#include <dbus/dbus-macros.h>
+
+DBUS_BEGIN_DECLS
+
+#if @DBUS_HAVE_INT64@
+#define DBUS_HAVE_INT64 1
+_DBUS_GNUC_EXTENSION typedef @DBUS_INT64_TYPE@ dbus_int64_t;
+_DBUS_GNUC_EXTENSION typedef unsigned @DBUS_INT64_TYPE@ dbus_uint64_t;
+
+#define DBUS_INT64_CONSTANT(val)  (_DBUS_GNUC_EXTENSION @DBUS_INT64_CONSTANT@)
+#define DBUS_UINT64_CONSTANT(val) (_DBUS_GNUC_EXTENSION @DBUS_UINT64_CONSTANT@)
+
+#else
+#undef DBUS_HAVE_INT64
+#undef DBUS_INT64_CONSTANT
+#undef DBUS_UINT64_CONSTANT
+#endif
+
+typedef @DBUS_INT32_TYPE@ dbus_int32_t;
+typedef unsigned @DBUS_INT32_TYPE@ dbus_uint32_t;
+
+typedef @DBUS_INT16_TYPE@ dbus_int16_t;
+typedef unsigned @DBUS_INT16_TYPE@ dbus_uint16_t;
+
+/* This is not really arch-dependent, but it's not worth
+ * creating an additional generated header just for this
+ */
+#define DBUS_MAJOR_VERSION @DBUS_MAJOR_VERSION@
+#define DBUS_MINOR_VERSION @DBUS_MINOR_VERSION@
+#define DBUS_MICRO_VERSION @DBUS_MICRO_VERSION@
+
+#define DBUS_VERSION_STRING "@DBUS_VERSION@"
+
+#define DBUS_VERSION ((@DBUS_MAJOR_VERSION@ << 16) | (@DBUS_MINOR_VERSION@ << 8) | (@DBUS_MICRO_VERSION@)) 
+
+DBUS_END_DECLS
+
+#endif /* DBUS_ARCH_DEPS_H */
diff --git a/src/dbus/dbus-auth-script.c b/src/dbus/dbus-auth-script.c
new file mode 100644 (file)
index 0000000..93d6f78
--- /dev/null
@@ -0,0 +1,803 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-auth-script.c Test DBusAuth using a special script file (internal to D-Bus implementation)
+ * 
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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 <config.h>
+
+#ifdef DBUS_BUILD_TESTS
+
+#include "dbus-auth-script.h"
+#include "dbus-auth.h"
+#include "dbus-string.h"
+#include "dbus-hash.h"
+#include "dbus-credentials.h"
+#include "dbus-internals.h"
+
+/**
+ * @defgroup DBusAuthScript code for running unit test scripts for DBusAuth
+ * @ingroup  DBusInternals
+ * @brief DBusAuth unit test scripting
+ *
+ * The code in here is used for unit testing, it loads
+ * up a script that tests DBusAuth.
+ *
+ * @{
+ */
+
+/* this is slightly different from the other append_quoted_string
+ * in dbus-message-builder.c
+ */
+static dbus_bool_t
+append_quoted_string (DBusString       *dest,
+                      const DBusString *quoted)
+{
+  dbus_bool_t in_quotes = FALSE;
+  dbus_bool_t in_backslash = FALSE;
+  int i;
+
+  i = 0;
+  while (i < _dbus_string_get_length (quoted))
+    {
+      unsigned char b;
+
+      b = _dbus_string_get_byte (quoted, i);
+
+      if (in_backslash)
+        {
+          unsigned char a;
+          
+          if (b == 'r')
+            a = '\r';
+          else if (b == 'n')
+            a = '\n';
+          else if (b == '\\')
+            a = '\\';
+          else
+            {
+              _dbus_warn ("bad backslashed byte %c\n", b);
+              return FALSE;
+            }
+
+          if (!_dbus_string_append_byte (dest, a))
+            return FALSE;
+          
+          in_backslash = FALSE;
+        }
+      else if (b == '\\')
+        {
+          in_backslash = TRUE;
+        }
+      else if (in_quotes)
+        {
+          if (b == '\'')
+            in_quotes = FALSE;
+          else
+            {
+              if (!_dbus_string_append_byte (dest, b))
+                return FALSE;
+            }
+        }
+      else
+        {
+          if (b == '\'')
+            in_quotes = TRUE;
+          else if (b == ' ' || b == '\n' || b == '\t')
+            break; /* end on whitespace if not quoted */
+          else
+            {
+              if (!_dbus_string_append_byte (dest, b))
+                return FALSE;
+            }
+        }
+      
+      ++i;
+    }
+
+  return TRUE;
+}
+
+static dbus_bool_t
+same_first_word (const DBusString *a,
+                 const DBusString *b)
+{
+  int first_a_blank, first_b_blank;
+
+  _dbus_string_find_blank (a, 0, &first_a_blank);
+  _dbus_string_find_blank (b, 0, &first_b_blank);
+
+  if (first_a_blank != first_b_blank)
+    return FALSE;
+
+  return _dbus_string_equal_len (a, b, first_a_blank);
+}
+
+static DBusAuthState
+auth_state_from_string (const DBusString *str)
+{ 
+  if (_dbus_string_starts_with_c_str (str, "WAITING_FOR_INPUT"))
+    return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
+  else if (_dbus_string_starts_with_c_str (str, "WAITING_FOR_MEMORY"))
+    return DBUS_AUTH_STATE_WAITING_FOR_MEMORY;
+  else if (_dbus_string_starts_with_c_str (str, "HAVE_BYTES_TO_SEND"))
+    return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
+  else if (_dbus_string_starts_with_c_str (str, "NEED_DISCONNECT"))
+    return DBUS_AUTH_STATE_NEED_DISCONNECT;
+  else if (_dbus_string_starts_with_c_str (str, "AUTHENTICATED"))
+    return DBUS_AUTH_STATE_AUTHENTICATED;
+  else
+    return -1;
+}
+
+static const char*
+auth_state_to_string (DBusAuthState state)
+{
+  switch (state)
+    {
+    case DBUS_AUTH_STATE_WAITING_FOR_INPUT:
+      return "WAITING_FOR_INPUT";
+    case DBUS_AUTH_STATE_WAITING_FOR_MEMORY:
+      return "WAITING_FOR_MEMORY";
+    case DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND:
+      return "HAVE_BYTES_TO_SEND";
+    case DBUS_AUTH_STATE_NEED_DISCONNECT:
+      return "NEED_DISCONNECT";
+    case DBUS_AUTH_STATE_AUTHENTICATED:
+      return "AUTHENTICATED";
+    }
+
+  return "unknown";
+}
+
+static char **
+split_string (DBusString *str)
+{
+  int i, j, k, count, end;
+  char **array;
+
+  end = _dbus_string_get_length (str);
+
+  i = 0;
+  _dbus_string_skip_blank (str, i, &i);
+  for (count = 0; i < end; count++)
+    {
+      _dbus_string_find_blank (str, i, &i);
+      _dbus_string_skip_blank (str, i, &i);
+    }
+
+  array = dbus_new0 (char *, count + 1);
+  if (array == NULL)
+    return NULL;
+
+  i = 0;
+  _dbus_string_skip_blank (str, i, &i);
+  for (k = 0; k < count; k++)
+    {
+      _dbus_string_find_blank (str, i, &j);
+
+      array[k] = dbus_malloc (j - i + 1);
+      if (array[k] == NULL)
+        {
+          dbus_free_string_array (array);
+          return NULL;
+        }
+      memcpy (array[k],
+              _dbus_string_get_const_data_len (str, i, j - i), j - i);
+      array[k][j - i] = '\0';
+
+      _dbus_string_skip_blank (str, j, &i);
+    }
+  array[k] = NULL;
+
+  return array;
+}
+
+static void
+auth_set_unix_credentials(DBusAuth  *auth,
+                          dbus_uid_t uid,
+                          dbus_pid_t pid)
+{
+  DBusCredentials *credentials;
+
+  credentials = _dbus_credentials_new ();
+  if (credentials == NULL)
+    _dbus_assert_not_reached ("no memory");
+
+  if (uid != DBUS_UID_UNSET)
+    _dbus_credentials_add_unix_uid (credentials, uid);
+  if (pid != DBUS_PID_UNSET)
+    _dbus_credentials_add_unix_pid (credentials, pid);
+
+  _dbus_auth_set_credentials (auth, credentials);
+
+  _dbus_credentials_unref (credentials);
+}
+
+/**
+ * Runs an "auth script" which is a script for testing the
+ * authentication protocol. Scripts send and receive data, and then
+ * include assertions about the state of both ends of the connection
+ * after processing the data. A script succeeds if these assertions
+ * hold.
+ *
+ * @param filename the file containing the script to run
+ * @returns #TRUE if the script succeeds, #FALSE otherwise
+ */
+dbus_bool_t
+_dbus_auth_script_run (const DBusString *filename)
+{
+  DBusString file;
+  DBusError error = DBUS_ERROR_INIT;
+  DBusString line;
+  dbus_bool_t retval;
+  int line_no;
+  DBusAuth *auth;
+  DBusString from_auth;
+  DBusAuthState state;
+  DBusString context;
+  DBusString guid;
+  
+  retval = FALSE;
+  auth = NULL;
+
+  _dbus_string_init_const (&guid, "5fa01f4202cd837709a3274ca0df9d00");
+  _dbus_string_init_const (&context, "org_freedesktop_test");
+  
+  if (!_dbus_string_init (&file))
+    return FALSE;
+
+  if (!_dbus_string_init (&line))
+    {
+      _dbus_string_free (&file);
+      return FALSE;
+    }
+
+  if (!_dbus_string_init (&from_auth))
+    {
+      _dbus_string_free (&file);
+      _dbus_string_free (&line);
+      return FALSE;
+    }
+
+  if (!_dbus_file_get_contents (&file, filename, &error))    {
+      _dbus_warn ("Getting contents of %s failed: %s\n",
+                  _dbus_string_get_const_data (filename), error.message);
+      dbus_error_free (&error);
+      goto out;
+    }
+
+  state = DBUS_AUTH_STATE_NEED_DISCONNECT;
+  line_no = 0;
+
+ next_iteration:
+  while (_dbus_string_pop_line (&file, &line))
+    {      
+      line_no += 1;
+
+      /* _dbus_warn ("%s\n", _dbus_string_get_const_data (&line)); */
+      
+      _dbus_string_delete_leading_blanks (&line);
+
+      if (auth != NULL)
+        {
+          while ((state = _dbus_auth_do_work (auth)) ==
+                 DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND)
+            {
+              const DBusString *tmp;
+              if (_dbus_auth_get_bytes_to_send (auth, &tmp))
+                {
+                  int count = _dbus_string_get_length (tmp);
+
+                  if (_dbus_string_copy (tmp, 0, &from_auth,
+                                         _dbus_string_get_length (&from_auth)))
+                    _dbus_auth_bytes_sent (auth, count);
+                }
+            }
+        }
+      
+      if (_dbus_string_get_length (&line) == 0)
+        {
+          /* empty line */
+          goto next_iteration;
+        }
+      else if (_dbus_string_starts_with_c_str (&line,
+                                               "#"))
+        {
+          /* Ignore this comment */
+          goto next_iteration;
+        }
+#ifdef DBUS_WIN
+      else if (_dbus_string_starts_with_c_str (&line,
+                                               "WIN_ONLY"))
+        {
+          /* Ignore this line */
+          goto next_iteration;
+        }
+      else if (_dbus_string_starts_with_c_str (&line,
+                                               "UNIX_ONLY"))
+        {
+          /* skip this file */
+          _dbus_warn ("skipping unix only auth script\n");
+          retval = TRUE;
+          goto out;
+        }
+#endif
+#ifdef DBUS_UNIX
+      else if (_dbus_string_starts_with_c_str (&line,
+                                               "UNIX_ONLY"))
+        {
+          /* Ignore this line */
+          goto next_iteration;
+        }
+      else if (_dbus_string_starts_with_c_str (&line,
+                                               "WIN_ONLY"))
+        {
+          /* skip this file */
+          _dbus_warn ("skipping windows only auth script\n");
+          retval = TRUE;
+          goto out;
+        }
+#endif
+      else if (_dbus_string_starts_with_c_str (&line,
+                                               "CLIENT"))
+        {
+          DBusCredentials *creds;
+          
+          if (auth != NULL)
+            {
+              _dbus_warn ("already created a DBusAuth (CLIENT or SERVER given twice)\n");
+              goto out;
+            }
+
+          auth = _dbus_auth_client_new ();
+          if (auth == NULL)
+            {
+              _dbus_warn ("no memory to create DBusAuth\n");
+              goto out;
+            }
+
+          /* test ref/unref */
+          _dbus_auth_ref (auth);
+          _dbus_auth_unref (auth);
+
+          creds = _dbus_credentials_new_from_current_process ();
+          if (creds == NULL)
+            {
+              _dbus_warn ("no memory for credentials\n");
+              _dbus_auth_unref (auth);
+              auth = NULL;
+              goto out;
+            }
+              
+          if (!_dbus_auth_set_credentials (auth, creds))
+            {
+              _dbus_warn ("no memory for setting credentials\n");
+              _dbus_auth_unref (auth);
+              auth = NULL;
+              _dbus_credentials_unref (creds);
+              goto out;
+            }
+          
+          _dbus_credentials_unref (creds);
+        }
+      else if (_dbus_string_starts_with_c_str (&line,
+                                               "SERVER"))
+        {
+          DBusCredentials *creds;
+          
+          if (auth != NULL)
+            {
+              _dbus_warn ("already created a DBusAuth (CLIENT or SERVER given twice)\n");
+              goto out;
+            }
+
+          auth = _dbus_auth_server_new (&guid);
+          if (auth == NULL)
+            {
+              _dbus_warn ("no memory to create DBusAuth\n");
+              goto out;
+            }
+
+          /* test ref/unref */
+          _dbus_auth_ref (auth);
+          _dbus_auth_unref (auth);
+
+          creds = _dbus_credentials_new_from_current_process ();
+          if (creds == NULL)
+            {
+              _dbus_warn ("no memory for credentials\n");
+              _dbus_auth_unref (auth);
+              auth = NULL;
+              goto out;
+            }
+              
+          if (!_dbus_auth_set_credentials (auth, creds))
+            {
+              _dbus_warn ("no memory for setting credentials\n");
+              _dbus_auth_unref (auth);
+              auth = NULL;
+              _dbus_credentials_unref (creds);
+              goto out;
+            }
+          
+          _dbus_credentials_unref (creds);
+
+          _dbus_auth_set_context (auth, &context);
+        }
+      else if (auth == NULL)
+        {
+          _dbus_warn ("must specify CLIENT or SERVER\n");
+          goto out;
+
+        }
+      else if (_dbus_string_starts_with_c_str (&line,
+                                               "NO_CREDENTIALS"))
+        {
+          auth_set_unix_credentials (auth, DBUS_UID_UNSET, DBUS_PID_UNSET);
+        }
+      else if (_dbus_string_starts_with_c_str (&line,
+                                               "ROOT_CREDENTIALS"))
+        {
+          auth_set_unix_credentials (auth, 0, DBUS_PID_UNSET);
+        }
+      else if (_dbus_string_starts_with_c_str (&line,
+                                               "SILLY_CREDENTIALS"))
+        {
+          auth_set_unix_credentials (auth, 4312, DBUS_PID_UNSET);
+        }
+      else if (_dbus_string_starts_with_c_str (&line,
+                                               "ALLOWED_MECHS"))
+        {
+          char **mechs;
+
+          _dbus_string_delete_first_word (&line);
+          mechs = split_string (&line);
+          _dbus_auth_set_mechanisms (auth, (const char **) mechs);
+          dbus_free_string_array (mechs);
+        }
+      else if (_dbus_string_starts_with_c_str (&line,
+                                               "SEND"))
+        {
+          DBusString to_send;
+          
+          _dbus_string_delete_first_word (&line);
+
+          if (!_dbus_string_init (&to_send))
+            {
+              _dbus_warn ("no memory to allocate string\n");
+              goto out;
+            }
+
+          if (!append_quoted_string (&to_send, &line))
+            {
+              _dbus_warn ("failed to append quoted string line %d\n",
+                          line_no);
+              _dbus_string_free (&to_send);
+              goto out;
+            }
+
+          _dbus_verbose ("Sending '%s'\n", _dbus_string_get_const_data (&to_send));
+          
+          if (!_dbus_string_append (&to_send, "\r\n"))
+            {
+              _dbus_warn ("failed to append \r\n from line %d\n",
+                          line_no);
+              _dbus_string_free (&to_send);
+              goto out;
+            }
+
+          /* Replace USERID_HEX with our username in hex */
+          {
+            int where;
+            
+            if (_dbus_string_find (&to_send, 0,
+                                   "USERID_HEX", &where))
+              {
+                DBusString username;
+
+                if (!_dbus_string_init (&username))
+                  {
+                    _dbus_warn ("no memory for userid\n");
+                    _dbus_string_free (&to_send);
+                    goto out;
+                  }
+
+                if (!_dbus_append_user_from_current_process (&username))
+                  {
+                    _dbus_warn ("no memory for userid\n");
+                    _dbus_string_free (&username);
+                    _dbus_string_free (&to_send);
+                    goto out;
+                  }
+
+                _dbus_string_delete (&to_send, where, strlen ("USERID_HEX"));
+                
+                if (!_dbus_string_hex_encode (&username, 0,
+                                             &to_send, where))
+                  {
+                    _dbus_warn ("no memory to subst USERID_HEX\n");
+                    _dbus_string_free (&username);
+                    _dbus_string_free (&to_send);
+                    goto out;
+                  }
+
+                _dbus_string_free (&username);
+              }
+            else if (_dbus_string_find (&to_send, 0,
+                                        "USERNAME_HEX", &where))
+              {
+                DBusString username;
+                
+                if (!_dbus_string_init (&username))
+                  {
+                    _dbus_warn ("no memory for username\n");
+                    _dbus_string_free (&to_send);
+                    goto out;
+                  }
+
+                if (!_dbus_append_user_from_current_process (&username))
+                  {
+                    _dbus_warn ("no memory for username\n");
+                    _dbus_string_free (&username);
+                    _dbus_string_free (&to_send);
+                    goto out;
+                  }
+
+                _dbus_string_delete (&to_send, where, strlen ("USERNAME_HEX"));
+                
+                if (!_dbus_string_hex_encode (&username, 0,
+                                             &to_send, where))
+                  {
+                    _dbus_warn ("no memory to subst USERNAME_HEX\n");
+                    _dbus_string_free (&username);
+                    _dbus_string_free (&to_send);
+                    goto out;
+                  }
+
+                _dbus_string_free (&username);
+              }
+          }
+
+          {
+            DBusString *buffer;
+
+            _dbus_auth_get_buffer (auth, &buffer);
+            if (!_dbus_string_copy (&to_send, 0,
+                                    buffer, _dbus_string_get_length (buffer)))
+              {
+                _dbus_warn ("not enough memory to call bytes_received, or can't add bytes to auth object already in end state\n");
+                _dbus_string_free (&to_send);
+                _dbus_auth_return_buffer (auth, buffer, 0);
+                goto out;
+              }
+
+            _dbus_auth_return_buffer (auth, buffer, _dbus_string_get_length (&to_send));
+          }
+          
+          _dbus_string_free (&to_send);
+        }
+      else if (_dbus_string_starts_with_c_str (&line,
+                                               "EXPECT_STATE"))
+        {
+          DBusAuthState expected;
+          
+          _dbus_string_delete_first_word (&line);
+
+          expected = auth_state_from_string (&line);
+          if (expected < 0)
+            {
+              _dbus_warn ("bad auth state given to EXPECT_STATE\n");
+              goto parse_failed;
+            }
+
+          if (expected != state)
+            {
+              _dbus_warn ("expected auth state %s but got %s on line %d\n",
+                          auth_state_to_string (expected),
+                          auth_state_to_string (state),
+                          line_no);
+              goto out;
+            }
+        }
+      else if (_dbus_string_starts_with_c_str (&line,
+                                               "EXPECT_COMMAND"))
+        {
+          DBusString received;
+          
+          _dbus_string_delete_first_word (&line);
+
+          if (!_dbus_string_init (&received))
+            {
+              _dbus_warn ("no mem to allocate string received\n");
+              goto out;
+            }
+
+          if (!_dbus_string_pop_line (&from_auth, &received))
+            {
+              _dbus_warn ("no line popped from the DBusAuth being tested, expected command %s on line %d\n",
+                          _dbus_string_get_const_data (&line), line_no);
+              _dbus_string_free (&received);
+              goto out;
+            }
+
+          if (!same_first_word (&received, &line))
+            {
+              _dbus_warn ("line %d expected command '%s' and got '%s'\n",
+                          line_no,
+                          _dbus_string_get_const_data (&line),
+                          _dbus_string_get_const_data (&received));
+              _dbus_string_free (&received);
+              goto out;
+            }
+          
+          _dbus_string_free (&received);
+        }
+      else if (_dbus_string_starts_with_c_str (&line,
+                                               "EXPECT_UNUSED"))
+        {
+          DBusString expected;
+          const DBusString *unused;
+          
+          _dbus_string_delete_first_word (&line);
+
+          if (!_dbus_string_init (&expected))
+            {
+              _dbus_warn ("no mem to allocate string expected\n");
+              goto out;
+            }
+
+          if (!append_quoted_string (&expected, &line))
+            {
+              _dbus_warn ("failed to append quoted string line %d\n",
+                          line_no);
+              _dbus_string_free (&expected);
+              goto out;
+            }
+
+          _dbus_auth_get_unused_bytes (auth, &unused);
+          
+          if (_dbus_string_equal (&expected, unused))
+            {
+              _dbus_auth_delete_unused_bytes (auth);
+              _dbus_string_free (&expected);
+            }
+          else
+            {
+              _dbus_warn ("Expected unused bytes '%s' and have '%s'\n",
+                          _dbus_string_get_const_data (&expected),
+                          _dbus_string_get_const_data (unused));
+              _dbus_string_free (&expected);
+              goto out;
+            }
+        }
+      else if (_dbus_string_starts_with_c_str (&line,
+                                               "EXPECT_HAVE_NO_CREDENTIALS"))
+        {
+          DBusCredentials *authorized_identity;
+          
+          authorized_identity = _dbus_auth_get_identity (auth);
+          if (!_dbus_credentials_are_anonymous (authorized_identity))
+            {
+              _dbus_warn ("Expected anonymous login or failed login, but some credentials were authorized\n");
+              goto out;
+            }
+        }
+      else if (_dbus_string_starts_with_c_str (&line,
+                                               "EXPECT_HAVE_SOME_CREDENTIALS"))
+        {
+          DBusCredentials *authorized_identity;
+          
+          authorized_identity = _dbus_auth_get_identity (auth);
+          if (_dbus_credentials_are_anonymous (authorized_identity))
+            {
+              _dbus_warn ("Expected to have some credentials, but we don't\n");
+              goto out;
+            }
+        }
+      else if (_dbus_string_starts_with_c_str (&line,
+                                               "EXPECT"))
+        {
+          DBusString expected;
+          
+          _dbus_string_delete_first_word (&line);
+
+          if (!_dbus_string_init (&expected))
+            {
+              _dbus_warn ("no mem to allocate string expected\n");
+              goto out;
+            }
+
+          if (!append_quoted_string (&expected, &line))
+            {
+              _dbus_warn ("failed to append quoted string line %d\n",
+                          line_no);
+              _dbus_string_free (&expected);
+              goto out;
+            }
+
+          if (_dbus_string_equal_len (&expected, &from_auth,
+                                      _dbus_string_get_length (&expected)))
+            {
+              _dbus_string_delete (&from_auth, 0,
+                                   _dbus_string_get_length (&expected));
+              _dbus_string_free (&expected);
+            }
+          else
+            {
+              _dbus_warn ("Expected exact string '%s' and have '%s'\n",
+                          _dbus_string_get_const_data (&expected),
+                          _dbus_string_get_const_data (&from_auth));
+              _dbus_string_free (&expected);
+              goto out;
+            }
+        }
+      else
+        goto parse_failed;
+
+      goto next_iteration; /* skip parse_failed */
+      
+    parse_failed:
+      {
+        _dbus_warn ("couldn't process line %d \"%s\"\n",
+                    line_no, _dbus_string_get_const_data (&line));
+        goto out;
+      }
+    }
+
+  if (auth == NULL)
+    {
+      _dbus_warn ("Auth script is bogus, did not even have CLIENT or SERVER\n");
+      goto out;
+    }
+  else if (state == DBUS_AUTH_STATE_AUTHENTICATED)
+    {
+      const DBusString *unused;
+
+      _dbus_auth_get_unused_bytes (auth, &unused);
+
+      if (_dbus_string_get_length (unused) > 0)
+        {
+          _dbus_warn ("did not expect unused bytes (scripts must specify explicitly if they are expected)\n");
+          goto out;
+        }
+    }
+
+  if (_dbus_string_get_length (&from_auth) > 0)
+    {
+      _dbus_warn ("script did not have EXPECT_ statements for all the data received from the DBusAuth\n");
+      _dbus_warn ("Leftover data: %s\n", _dbus_string_get_const_data (&from_auth));
+      goto out;
+    }
+  
+  retval = TRUE;
+  
+ out:
+  if (auth)
+    _dbus_auth_unref (auth);
+
+  _dbus_string_free (&file);
+  _dbus_string_free (&line);
+  _dbus_string_free (&from_auth);
+  
+  return retval;
+}
+
+/** @} */
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-auth-script.h b/src/dbus/dbus-auth-script.h
new file mode 100644 (file)
index 0000000..1f4da1b
--- /dev/null
@@ -0,0 +1,39 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-auth-script.h Test DBusAuth using a special script file (internal to D-Bus implementation)
+ * 
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_AUTH_SCRIPT_H
+#define DBUS_AUTH_SCRIPT_H
+
+#include <config.h>
+
+#include <dbus/dbus-memory.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-string.h>
+
+DBUS_BEGIN_DECLS
+
+dbus_bool_t _dbus_auth_script_run (const DBusString *filename);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_AUTH_SCRIPT_H */
diff --git a/src/dbus/dbus-auth-util.c b/src/dbus/dbus-auth-util.c
new file mode 100644 (file)
index 0000000..e501904
--- /dev/null
@@ -0,0 +1,168 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-auth-util.c Would be in dbus-auth.c, but only used for tests/bus
+ *
+ * Copyright (C) 2002, 2003, 2004 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-internals.h"
+#include "dbus-test.h"
+#include "dbus-auth.h"
+
+/**
+ * @addtogroup DBusAuth
+ * @{
+ */
+
+/** @} */
+
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-test.h"
+#include "dbus-auth-script.h"
+#include <stdio.h>
+
+static dbus_bool_t
+process_test_subdir (const DBusString          *test_base_dir,
+                     const char                *subdir)
+{
+  DBusString test_directory;
+  DBusString filename;
+  DBusDirIter *dir;
+  dbus_bool_t retval;
+  DBusError error = DBUS_ERROR_INIT;
+
+  retval = FALSE;
+  dir = NULL;
+  
+  if (!_dbus_string_init (&test_directory))
+    _dbus_assert_not_reached ("didn't allocate test_directory\n");
+
+  _dbus_string_init_const (&filename, subdir);
+  
+  if (!_dbus_string_copy (test_base_dir, 0,
+                          &test_directory, 0))
+    _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
+  
+  if (!_dbus_concat_dir_and_file (&test_directory, &filename))    
+    _dbus_assert_not_reached ("couldn't allocate full path");
+
+  _dbus_string_free (&filename);
+  if (!_dbus_string_init (&filename))
+    _dbus_assert_not_reached ("didn't allocate filename string\n");
+
+  dir = _dbus_directory_open (&test_directory, &error);
+  if (dir == NULL)
+    {
+      _dbus_warn ("Could not open %s: %s\n",
+                  _dbus_string_get_const_data (&test_directory),
+                  error.message);
+      dbus_error_free (&error);
+      goto failed;
+    }
+
+  printf ("Testing %s:\n", subdir);
+  
+ next:
+  while (_dbus_directory_get_next_file (dir, &filename, &error))
+    {
+      DBusString full_path;
+      
+      if (!_dbus_string_init (&full_path))
+        _dbus_assert_not_reached ("couldn't init string");
+
+      if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
+        _dbus_assert_not_reached ("couldn't copy dir to full_path");
+
+      if (!_dbus_concat_dir_and_file (&full_path, &filename))
+        _dbus_assert_not_reached ("couldn't concat file to dir");
+
+      if (!_dbus_string_ends_with_c_str (&filename, ".auth-script"))
+        {
+          _dbus_verbose ("Skipping non-.auth-script file %s\n",
+                         _dbus_string_get_const_data (&filename));
+         _dbus_string_free (&full_path);
+          goto next;
+        }
+
+      printf ("    %s\n", _dbus_string_get_const_data (&filename));
+      
+      if (!_dbus_auth_script_run (&full_path))
+        {
+          _dbus_string_free (&full_path);
+          goto failed;
+        }
+      else
+        _dbus_string_free (&full_path);
+    }
+
+  if (dbus_error_is_set (&error))
+    {
+      _dbus_warn ("Could not get next file in %s: %s\n",
+                  _dbus_string_get_const_data (&test_directory), error.message);
+      dbus_error_free (&error);
+      goto failed;
+    }
+    
+  retval = TRUE;
+  
+ failed:
+
+  if (dir)
+    _dbus_directory_close (dir);
+  _dbus_string_free (&test_directory);
+  _dbus_string_free (&filename);
+
+  return retval;
+}
+
+static dbus_bool_t
+process_test_dirs (const char *test_data_dir)
+{
+  DBusString test_directory;
+  dbus_bool_t retval;
+
+  retval = FALSE;
+  
+  _dbus_string_init_const (&test_directory, test_data_dir);
+
+  if (!process_test_subdir (&test_directory, "auth"))
+    goto failed;
+
+  retval = TRUE;
+  
+ failed:
+
+  _dbus_string_free (&test_directory);
+  
+  return retval;
+}
+
+dbus_bool_t
+_dbus_auth_test (const char *test_data_dir)
+{
+  
+  if (test_data_dir == NULL)
+    return TRUE;
+  
+  if (!process_test_dirs (test_data_dir))
+    return FALSE;
+
+  return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-auth.c b/src/dbus/dbus-auth.c
new file mode 100644 (file)
index 0000000..ec7cf31
--- /dev/null
@@ -0,0 +1,2690 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-auth.c Authentication
+ *
+ * Copyright (C) 2002, 2003, 2004 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-auth.h"
+#include "dbus-string.h"
+#include "dbus-list.h"
+#include "dbus-internals.h"
+#include "dbus-keyring.h"
+#include "dbus-sha.h"
+#include "dbus-protocol.h"
+#include "dbus-credentials.h"
+
+/**
+ * @defgroup DBusAuth Authentication
+ * @ingroup  DBusInternals
+ * @brief DBusAuth object
+ *
+ * DBusAuth manages the authentication negotiation when a connection
+ * is first established, and also manage any encryption used over a
+ * connection.
+ *
+ * @todo some SASL profiles require sending the empty string as a
+ * challenge/response, but we don't currently allow that in our
+ * protocol.
+ *
+ * @todo right now sometimes both ends will block waiting for input
+ * from the other end, e.g. if there's an error during
+ * DBUS_COOKIE_SHA1.
+ *
+ * @todo the cookie keyring needs to be cached globally not just
+ * per-auth (which raises threadsafety issues too)
+ * 
+ * @todo grep FIXME in dbus-auth.c
+ */
+
+/**
+ * @defgroup DBusAuthInternals Authentication implementation details
+ * @ingroup  DBusInternals
+ * @brief DBusAuth implementation details
+ *
+ * Private details of authentication code.
+ *
+ * @{
+ */
+
+/**
+ * This function appends an initial client response to the given string
+ */
+typedef dbus_bool_t (* DBusInitialResponseFunction)  (DBusAuth         *auth,
+                                                      DBusString       *response);
+
+/**
+ * This function processes a block of data received from the peer.
+ * i.e. handles a DATA command.
+ */
+typedef dbus_bool_t (* DBusAuthDataFunction)     (DBusAuth         *auth,
+                                                  const DBusString *data);
+
+/**
+ * This function encodes a block of data from the peer.
+ */
+typedef dbus_bool_t (* DBusAuthEncodeFunction)   (DBusAuth         *auth,
+                                                  const DBusString *data,
+                                                  DBusString       *encoded);
+
+/**
+ * This function decodes a block of data from the peer.
+ */
+typedef dbus_bool_t (* DBusAuthDecodeFunction)   (DBusAuth         *auth,
+                                                  const DBusString *data,
+                                                  DBusString       *decoded);
+
+/**
+ * This function is called when the mechanism is abandoned.
+ */
+typedef void        (* DBusAuthShutdownFunction) (DBusAuth       *auth);
+
+/**
+ * Virtual table representing a particular auth mechanism.
+ */
+typedef struct
+{
+  const char *mechanism; /**< Name of the mechanism */
+  DBusAuthDataFunction server_data_func; /**< Function on server side for DATA */
+  DBusAuthEncodeFunction server_encode_func; /**< Function on server side to encode */
+  DBusAuthDecodeFunction server_decode_func; /**< Function on server side to decode */
+  DBusAuthShutdownFunction server_shutdown_func; /**< Function on server side to shut down */
+  DBusInitialResponseFunction client_initial_response_func; /**< Function on client side to handle initial response */
+  DBusAuthDataFunction client_data_func; /**< Function on client side for DATA */
+  DBusAuthEncodeFunction client_encode_func; /**< Function on client side for encode */
+  DBusAuthDecodeFunction client_decode_func; /**< Function on client side for decode */
+  DBusAuthShutdownFunction client_shutdown_func; /**< Function on client side for shutdown */
+} DBusAuthMechanismHandler;
+
+/**
+ * Enumeration for the known authentication commands.
+ */
+typedef enum {
+  DBUS_AUTH_COMMAND_AUTH,
+  DBUS_AUTH_COMMAND_CANCEL,
+  DBUS_AUTH_COMMAND_DATA,
+  DBUS_AUTH_COMMAND_BEGIN,
+  DBUS_AUTH_COMMAND_REJECTED,
+  DBUS_AUTH_COMMAND_OK,
+  DBUS_AUTH_COMMAND_ERROR,
+  DBUS_AUTH_COMMAND_UNKNOWN
+} DBusAuthCommand;
+
+/**
+ * Auth state function, determines the reaction to incoming events for
+ * a particular state. Returns whether we had enough memory to
+ * complete the operation.
+ */
+typedef dbus_bool_t (* DBusAuthStateFunction) (DBusAuth         *auth,
+                                               DBusAuthCommand   command,
+                                               const DBusString *args);
+
+/**
+ * Information about a auth state.
+ */
+typedef struct
+{
+  const char *name;               /**< Name of the state */
+  DBusAuthStateFunction handler;  /**< State function for this state */
+} DBusAuthStateData;
+
+/**
+ * Internal members of DBusAuth.
+ */
+struct DBusAuth
+{
+  int refcount;           /**< reference count */
+  const char *side;       /**< Client or server */
+
+  DBusString incoming;    /**< Incoming data buffer */
+  DBusString outgoing;    /**< Outgoing data buffer */
+  
+  const DBusAuthStateData *state;         /**< Current protocol state */
+
+  const DBusAuthMechanismHandler *mech;   /**< Current auth mechanism */
+
+  DBusString identity;                   /**< Current identity we're authorizing
+                                          *   as.
+                                          */
+  
+  DBusCredentials *credentials;          /**< Credentials read from socket
+                                          */
+
+  DBusCredentials *authorized_identity; /**< Credentials that are authorized */
+
+  DBusCredentials *desired_identity;    /**< Identity client has requested */
+  
+  DBusString context;               /**< Cookie scope */
+  DBusKeyring *keyring;             /**< Keyring for cookie mechanism. */
+  int cookie_id;                    /**< ID of cookie to use */
+  DBusString challenge;             /**< Challenge sent to client */
+
+  char **allowed_mechs;             /**< Mechanisms we're allowed to use,
+                                     * or #NULL if we can use any
+                                     */
+  
+  unsigned int needed_memory : 1;   /**< We needed memory to continue since last
+                                     * successful getting something done
+                                     */
+  unsigned int already_got_mechanisms : 1;       /**< Client already got mech list */
+  unsigned int already_asked_for_initial_response : 1; /**< Already sent a blank challenge to get an initial response */
+  unsigned int buffer_outstanding : 1; /**< Buffer is "checked out" for reading data into */
+};
+
+/**
+ * "Subclass" of DBusAuth for client side
+ */
+typedef struct
+{
+  DBusAuth base;    /**< Parent class */
+
+  DBusList *mechs_to_try; /**< Mechanisms we got from the server that we're going to try using */
+
+  DBusString guid_from_server; /**< GUID received from server */
+  
+} DBusAuthClient;
+
+/**
+ * "Subclass" of DBusAuth for server side.
+ */
+typedef struct
+{
+  DBusAuth base;    /**< Parent class */
+
+  int failures;     /**< Number of times client has been rejected */
+  int max_failures; /**< Number of times we reject before disconnect */
+
+  DBusString guid;  /**< Our globally unique ID in hex encoding */
+  
+} DBusAuthServer;
+
+static void        goto_state                (DBusAuth                       *auth,
+                                              const DBusAuthStateData        *new_state);
+static dbus_bool_t send_auth                 (DBusAuth *auth,
+                                              const DBusAuthMechanismHandler *mech);
+static dbus_bool_t send_data                 (DBusAuth *auth,
+                                              DBusString *data);
+static dbus_bool_t send_rejected             (DBusAuth *auth);
+static dbus_bool_t send_error                (DBusAuth *auth,
+                                              const char *message);
+static dbus_bool_t send_ok                   (DBusAuth *auth);
+static dbus_bool_t send_begin                (DBusAuth *auth,
+                                              const DBusString *args_from_ok);
+static dbus_bool_t send_cancel               (DBusAuth *auth);
+
+/**
+ * Client states
+ */
+static dbus_bool_t handle_server_state_waiting_for_auth  (DBusAuth         *auth,
+                                                          DBusAuthCommand   command,
+                                                          const DBusString *args);
+static dbus_bool_t handle_server_state_waiting_for_data  (DBusAuth         *auth,
+                                                          DBusAuthCommand   command,
+                                                          const DBusString *args);
+static dbus_bool_t handle_server_state_waiting_for_begin (DBusAuth         *auth,
+                                                          DBusAuthCommand   command,
+                                                          const DBusString *args);
+  
+static const DBusAuthStateData server_state_waiting_for_auth = {
+  "WaitingForAuth", handle_server_state_waiting_for_auth
+};
+static const DBusAuthStateData server_state_waiting_for_data = {
+  "WaitingForData", handle_server_state_waiting_for_data
+};
+static const DBusAuthStateData server_state_waiting_for_begin = {
+  "WaitingForBegin", handle_server_state_waiting_for_begin
+};
+  
+/**
+ * Client states
+ */
+static dbus_bool_t handle_client_state_waiting_for_data   (DBusAuth         *auth,
+                                                           DBusAuthCommand   command,
+                                                           const DBusString *args);
+static dbus_bool_t handle_client_state_waiting_for_ok     (DBusAuth         *auth,
+                                                           DBusAuthCommand   command,
+                                                           const DBusString *args);
+static dbus_bool_t handle_client_state_waiting_for_reject (DBusAuth         *auth,
+                                                           DBusAuthCommand   command,
+                                                           const DBusString *args);
+
+static const DBusAuthStateData client_state_need_send_auth = {
+  "NeedSendAuth", NULL
+};
+static const DBusAuthStateData client_state_waiting_for_data = {
+  "WaitingForData", handle_client_state_waiting_for_data
+};
+static const DBusAuthStateData client_state_waiting_for_ok = {
+  "WaitingForOK", handle_client_state_waiting_for_ok
+};
+static const DBusAuthStateData client_state_waiting_for_reject = {
+  "WaitingForReject", handle_client_state_waiting_for_reject
+};
+  
+/**
+ * Common terminal states.  Terminal states have handler == NULL.
+ */
+
+static const DBusAuthStateData common_state_authenticated = {
+  "Authenticated",  NULL
+};
+
+static const DBusAuthStateData common_state_need_disconnect = {
+  "NeedDisconnect",  NULL
+};
+
+static const char auth_side_client[] = "client";
+static const char auth_side_server[] = "server";
+/**
+ * @param auth the auth conversation
+ * @returns #TRUE if the conversation is the server side
+ */
+#define DBUS_AUTH_IS_SERVER(auth) ((auth)->side == auth_side_server)
+/**
+ * @param auth the auth conversation
+ * @returns #TRUE if the conversation is the client side
+ */
+#define DBUS_AUTH_IS_CLIENT(auth) ((auth)->side == auth_side_client)
+/**
+ * @param auth the auth conversation
+ * @returns auth cast to DBusAuthClient
+ */
+#define DBUS_AUTH_CLIENT(auth)    ((DBusAuthClient*)(auth))
+/**
+ * @param auth the auth conversation
+ * @returns auth cast to DBusAuthServer
+ */
+#define DBUS_AUTH_SERVER(auth)    ((DBusAuthServer*)(auth))
+
+/**
+ * The name of the auth ("client" or "server")
+ * @param auth the auth conversation
+ * @returns a string
+ */
+#define DBUS_AUTH_NAME(auth)      ((auth)->side)
+
+static DBusAuth*
+_dbus_auth_new (int size)
+{
+  DBusAuth *auth;
+  
+  auth = dbus_malloc0 (size);
+  if (auth == NULL)
+    return NULL;
+  
+  auth->refcount = 1;
+  
+  auth->keyring = NULL;
+  auth->cookie_id = -1;
+  
+  /* note that we don't use the max string length feature,
+   * because you can't use that feature if you're going to
+   * try to recover from out-of-memory (it creates
+   * what looks like unrecoverable inability to alloc
+   * more space in the string). But we do handle
+   * overlong buffers in _dbus_auth_do_work().
+   */
+  
+  if (!_dbus_string_init (&auth->incoming))
+    goto enomem_0;
+
+  if (!_dbus_string_init (&auth->outgoing))
+    goto enomem_1;
+    
+  if (!_dbus_string_init (&auth->identity))
+    goto enomem_2;
+
+  if (!_dbus_string_init (&auth->context))
+    goto enomem_3;
+
+  if (!_dbus_string_init (&auth->challenge))
+    goto enomem_4;
+
+  /* default context if none is specified */
+  if (!_dbus_string_append (&auth->context, "org_freedesktop_general"))
+    goto enomem_5;
+
+  auth->credentials = _dbus_credentials_new ();
+  if (auth->credentials == NULL)
+    goto enomem_6;
+  
+  auth->authorized_identity = _dbus_credentials_new ();
+  if (auth->authorized_identity == NULL)
+    goto enomem_7;
+
+  auth->desired_identity = _dbus_credentials_new ();
+  if (auth->desired_identity == NULL)
+    goto enomem_8;
+  
+  return auth;
+
+#if 0
+ enomem_9:
+  _dbus_credentials_unref (auth->desired_identity);
+#endif
+ enomem_8:
+  _dbus_credentials_unref (auth->authorized_identity);
+ enomem_7:
+  _dbus_credentials_unref (auth->credentials);
+ enomem_6:
+ /* last alloc was an append to context, which is freed already below */ ;
+ enomem_5:
+  _dbus_string_free (&auth->challenge);
+ enomem_4:
+  _dbus_string_free (&auth->context);
+ enomem_3:
+  _dbus_string_free (&auth->identity);
+ enomem_2:
+  _dbus_string_free (&auth->outgoing);
+ enomem_1:
+  _dbus_string_free (&auth->incoming);
+ enomem_0:
+  dbus_free (auth);
+  return NULL;
+}
+
+static void
+shutdown_mech (DBusAuth *auth)
+{
+  /* Cancel any auth */
+  auth->already_asked_for_initial_response = FALSE;
+  _dbus_string_set_length (&auth->identity, 0);
+
+  _dbus_credentials_clear (auth->authorized_identity);
+  _dbus_credentials_clear (auth->desired_identity);
+  
+  if (auth->mech != NULL)
+    {
+      _dbus_verbose ("%s: Shutting down mechanism %s\n",
+                     DBUS_AUTH_NAME (auth), auth->mech->mechanism);
+      
+      if (DBUS_AUTH_IS_CLIENT (auth))
+        (* auth->mech->client_shutdown_func) (auth);
+      else
+        (* auth->mech->server_shutdown_func) (auth);
+      
+      auth->mech = NULL;
+    }
+}
+
+/*
+ * DBUS_COOKIE_SHA1 mechanism
+ */
+
+/* Returns TRUE but with an empty string hash if the
+ * cookie_id isn't known. As with all this code
+ * TRUE just means we had enough memory.
+ */
+static dbus_bool_t
+sha1_compute_hash (DBusAuth         *auth,
+                   int               cookie_id,
+                   const DBusString *server_challenge,
+                   const DBusString *client_challenge,
+                   DBusString       *hash)
+{
+  DBusString cookie;
+  DBusString to_hash;
+  dbus_bool_t retval;
+  
+  _dbus_assert (auth->keyring != NULL);
+
+  retval = FALSE;
+  
+  if (!_dbus_string_init (&cookie))
+    return FALSE;
+
+  if (!_dbus_keyring_get_hex_key (auth->keyring, cookie_id,
+                                  &cookie))
+    goto out_0;
+
+  if (_dbus_string_get_length (&cookie) == 0)
+    {
+      retval = TRUE;
+      goto out_0;
+    }
+
+  if (!_dbus_string_init (&to_hash))
+    goto out_0;
+  
+  if (!_dbus_string_copy (server_challenge, 0,
+                          &to_hash, _dbus_string_get_length (&to_hash)))
+    goto out_1;
+
+  if (!_dbus_string_append (&to_hash, ":"))
+    goto out_1;
+  
+  if (!_dbus_string_copy (client_challenge, 0,
+                          &to_hash, _dbus_string_get_length (&to_hash)))
+    goto out_1;
+
+  if (!_dbus_string_append (&to_hash, ":"))
+    goto out_1;
+
+  if (!_dbus_string_copy (&cookie, 0,
+                          &to_hash, _dbus_string_get_length (&to_hash)))
+    goto out_1;
+
+  if (!_dbus_sha_compute (&to_hash, hash))
+    goto out_1;
+  
+  retval = TRUE;
+
+ out_1:
+  _dbus_string_zero (&to_hash);
+  _dbus_string_free (&to_hash);
+ out_0:
+  _dbus_string_zero (&cookie);
+  _dbus_string_free (&cookie);
+  return retval;
+}
+
+/** http://www.ietf.org/rfc/rfc2831.txt suggests at least 64 bits of
+ * entropy, we use 128. This is the number of bytes in the random
+ * challenge.
+ */
+#define N_CHALLENGE_BYTES (128/8)
+
+static dbus_bool_t
+sha1_handle_first_client_response (DBusAuth         *auth,
+                                   const DBusString *data)
+{
+  /* We haven't sent a challenge yet, we're expecting a desired
+   * username from the client.
+   */
+  DBusString tmp;
+  DBusString tmp2;
+  dbus_bool_t retval;
+  DBusError error;
+  
+  retval = FALSE;
+
+  _dbus_string_set_length (&auth->challenge, 0);
+  
+  if (_dbus_string_get_length (data) > 0)
+    {
+      if (_dbus_string_get_length (&auth->identity) > 0)
+        {
+          /* Tried to send two auth identities, wtf */
+          _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n",
+                         DBUS_AUTH_NAME (auth));
+          return send_rejected (auth);
+        }
+      else
+        {
+          /* this is our auth identity */
+          if (!_dbus_string_copy (data, 0, &auth->identity, 0))
+            return FALSE;
+        }
+    }
+      
+  if (!_dbus_credentials_add_from_user (auth->desired_identity, data))
+    {
+      _dbus_verbose ("%s: Did not get a valid username from client\n",
+                     DBUS_AUTH_NAME (auth));
+      return send_rejected (auth);
+    }
+      
+  if (!_dbus_string_init (&tmp))
+    return FALSE;
+
+  if (!_dbus_string_init (&tmp2))
+    {
+      _dbus_string_free (&tmp);
+      return FALSE;
+    }
+
+  /* we cache the keyring for speed, so here we drop it if it's the
+   * wrong one. FIXME caching the keyring here is useless since we use
+   * a different DBusAuth for every connection.
+   */
+  if (auth->keyring &&
+      !_dbus_keyring_is_for_credentials (auth->keyring,
+                                         auth->desired_identity))
+    {
+      _dbus_keyring_unref (auth->keyring);
+      auth->keyring = NULL;
+    }
+  
+  if (auth->keyring == NULL)
+    {
+      dbus_error_init (&error);
+      auth->keyring = _dbus_keyring_new_for_credentials (auth->desired_identity,
+                                                         &auth->context,
+                                                         &error);
+
+      if (auth->keyring == NULL)
+        {
+          if (dbus_error_has_name (&error,
+                                   DBUS_ERROR_NO_MEMORY))
+            {
+              dbus_error_free (&error);
+              goto out;
+            }
+          else
+            {
+              _DBUS_ASSERT_ERROR_IS_SET (&error);
+              _dbus_verbose ("%s: Error loading keyring: %s\n",
+                             DBUS_AUTH_NAME (auth), error.message);
+              if (send_rejected (auth))
+                retval = TRUE; /* retval is only about mem */
+              dbus_error_free (&error);
+              goto out;
+            }
+        }
+      else
+        {
+          _dbus_assert (!dbus_error_is_set (&error));
+        }
+    }
+
+  _dbus_assert (auth->keyring != NULL);
+
+  dbus_error_init (&error);
+  auth->cookie_id = _dbus_keyring_get_best_key (auth->keyring, &error);
+  if (auth->cookie_id < 0)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (&error);
+      _dbus_verbose ("%s: Could not get a cookie ID to send to client: %s\n",
+                     DBUS_AUTH_NAME (auth), error.message);
+      if (send_rejected (auth))
+        retval = TRUE;
+      dbus_error_free (&error);
+      goto out;
+    }
+  else
+    {
+      _dbus_assert (!dbus_error_is_set (&error));
+    }
+
+  if (!_dbus_string_copy (&auth->context, 0,
+                          &tmp2, _dbus_string_get_length (&tmp2)))
+    goto out;
+
+  if (!_dbus_string_append (&tmp2, " "))
+    goto out;
+
+  if (!_dbus_string_append_int (&tmp2, auth->cookie_id))
+    goto out;
+
+  if (!_dbus_string_append (&tmp2, " "))
+    goto out;  
+  
+  if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES))
+    goto out;
+
+  _dbus_string_set_length (&auth->challenge, 0);
+  if (!_dbus_string_hex_encode (&tmp, 0, &auth->challenge, 0))
+    goto out;
+  
+  if (!_dbus_string_hex_encode (&tmp, 0, &tmp2,
+                                _dbus_string_get_length (&tmp2)))
+    goto out;
+
+  if (!send_data (auth, &tmp2))
+    goto out;
+      
+  goto_state (auth, &server_state_waiting_for_data);
+  retval = TRUE;
+  
+ out:
+  _dbus_string_zero (&tmp);
+  _dbus_string_free (&tmp);
+  _dbus_string_zero (&tmp2);
+  _dbus_string_free (&tmp2);
+
+  return retval;
+}
+
+static dbus_bool_t
+sha1_handle_second_client_response (DBusAuth         *auth,
+                                    const DBusString *data)
+{
+  /* We are expecting a response which is the hex-encoded client
+   * challenge, space, then SHA-1 hash of the concatenation of our
+   * challenge, ":", client challenge, ":", secret key, all
+   * hex-encoded.
+   */
+  int i;
+  DBusString client_challenge;
+  DBusString client_hash;
+  dbus_bool_t retval;
+  DBusString correct_hash;
+  
+  retval = FALSE;
+  
+  if (!_dbus_string_find_blank (data, 0, &i))
+    {
+      _dbus_verbose ("%s: no space separator in client response\n",
+                     DBUS_AUTH_NAME (auth));
+      return send_rejected (auth);
+    }
+  
+  if (!_dbus_string_init (&client_challenge))
+    goto out_0;
+
+  if (!_dbus_string_init (&client_hash))
+    goto out_1;  
+
+  if (!_dbus_string_copy_len (data, 0, i, &client_challenge,
+                              0))
+    goto out_2;
+
+  _dbus_string_skip_blank (data, i, &i);
+  
+  if (!_dbus_string_copy_len (data, i,
+                              _dbus_string_get_length (data) - i,
+                              &client_hash,
+                              0))
+    goto out_2;
+
+  if (_dbus_string_get_length (&client_challenge) == 0 ||
+      _dbus_string_get_length (&client_hash) == 0)
+    {
+      _dbus_verbose ("%s: zero-length client challenge or hash\n",
+                     DBUS_AUTH_NAME (auth));
+      if (send_rejected (auth))
+        retval = TRUE;
+      goto out_2;
+    }
+
+  if (!_dbus_string_init (&correct_hash))
+    goto out_2;
+
+  if (!sha1_compute_hash (auth, auth->cookie_id,
+                          &auth->challenge, 
+                          &client_challenge,
+                          &correct_hash))
+    goto out_3;
+
+  /* if cookie_id was invalid, then we get an empty hash */
+  if (_dbus_string_get_length (&correct_hash) == 0)
+    {
+      if (send_rejected (auth))
+        retval = TRUE;
+      goto out_3;
+    }
+  
+  if (!_dbus_string_equal (&client_hash, &correct_hash))
+    {
+      if (send_rejected (auth))
+        retval = TRUE;
+      goto out_3;
+    }
+
+  if (!_dbus_credentials_add_credentials (auth->authorized_identity,
+                                          auth->desired_identity))
+    goto out_3;
+
+  /* Copy process ID from the socket credentials if it's there
+   */
+  if (!_dbus_credentials_add_credential (auth->authorized_identity,
+                                         DBUS_CREDENTIAL_UNIX_PROCESS_ID,
+                                         auth->credentials))
+    goto out_3;
+  
+  if (!send_ok (auth))
+    goto out_3;
+
+  _dbus_verbose ("%s: authenticated client using DBUS_COOKIE_SHA1\n",
+                 DBUS_AUTH_NAME (auth));
+  
+  retval = TRUE;
+  
+ out_3:
+  _dbus_string_zero (&correct_hash);
+  _dbus_string_free (&correct_hash);
+ out_2:
+  _dbus_string_zero (&client_hash);
+  _dbus_string_free (&client_hash);
+ out_1:
+  _dbus_string_free (&client_challenge);
+ out_0:
+  return retval;
+}
+
+static dbus_bool_t
+handle_server_data_cookie_sha1_mech (DBusAuth         *auth,
+                                     const DBusString *data)
+{
+  if (auth->cookie_id < 0)
+    return sha1_handle_first_client_response (auth, data);
+  else
+    return sha1_handle_second_client_response (auth, data);
+}
+
+static void
+handle_server_shutdown_cookie_sha1_mech (DBusAuth *auth)
+{
+  auth->cookie_id = -1;  
+  _dbus_string_set_length (&auth->challenge, 0);
+}
+
+static dbus_bool_t
+handle_client_initial_response_cookie_sha1_mech (DBusAuth   *auth,
+                                                 DBusString *response)
+{
+  DBusString username;
+  dbus_bool_t retval;
+
+  retval = FALSE;
+
+  if (!_dbus_string_init (&username))
+    return FALSE;
+  
+  if (!_dbus_append_user_from_current_process (&username))
+    goto out_0;
+
+  if (!_dbus_string_hex_encode (&username, 0,
+                               response,
+                               _dbus_string_get_length (response)))
+    goto out_0;
+
+  retval = TRUE;
+  
+ out_0:
+  _dbus_string_free (&username);
+  
+  return retval;
+}
+
+static dbus_bool_t
+handle_client_data_cookie_sha1_mech (DBusAuth         *auth,
+                                     const DBusString *data)
+{
+  /* The data we get from the server should be the cookie context
+   * name, the cookie ID, and the server challenge, separated by
+   * spaces. We send back our challenge string and the correct hash.
+   */
+  dbus_bool_t retval;
+  DBusString context;
+  DBusString cookie_id_str;
+  DBusString server_challenge;
+  DBusString client_challenge;
+  DBusString correct_hash;
+  DBusString tmp;
+  int i, j;
+  long val;
+  
+  retval = FALSE;                 
+  
+  if (!_dbus_string_find_blank (data, 0, &i))
+    {
+      if (send_error (auth,
+                      "Server did not send context/ID/challenge properly"))
+        retval = TRUE;
+      goto out_0;
+    }
+
+  if (!_dbus_string_init (&context))
+    goto out_0;
+
+  if (!_dbus_string_copy_len (data, 0, i,
+                              &context, 0))
+    goto out_1;
+  
+  _dbus_string_skip_blank (data, i, &i);
+  if (!_dbus_string_find_blank (data, i, &j))
+    {
+      if (send_error (auth,
+                      "Server did not send context/ID/challenge properly"))
+        retval = TRUE;
+      goto out_1;
+    }
+
+  if (!_dbus_string_init (&cookie_id_str))
+    goto out_1;
+  
+  if (!_dbus_string_copy_len (data, i, j - i,
+                              &cookie_id_str, 0))
+    goto out_2;  
+
+  if (!_dbus_string_init (&server_challenge))
+    goto out_2;
+
+  i = j;
+  _dbus_string_skip_blank (data, i, &i);
+  j = _dbus_string_get_length (data);
+
+  if (!_dbus_string_copy_len (data, i, j - i,
+                              &server_challenge, 0))
+    goto out_3;
+
+  if (!_dbus_keyring_validate_context (&context))
+    {
+      if (send_error (auth, "Server sent invalid cookie context"))
+        retval = TRUE;
+      goto out_3;
+    }
+
+  if (!_dbus_string_parse_int (&cookie_id_str, 0, &val, NULL))
+    {
+      if (send_error (auth, "Could not parse cookie ID as an integer"))
+        retval = TRUE;
+      goto out_3;
+    }
+
+  if (_dbus_string_get_length (&server_challenge) == 0)
+    {
+      if (send_error (auth, "Empty server challenge string"))
+        retval = TRUE;
+      goto out_3;
+    }
+
+  if (auth->keyring == NULL)
+    {
+      DBusError error;
+
+      dbus_error_init (&error);
+      auth->keyring = _dbus_keyring_new_for_credentials (NULL,
+                                                         &context,
+                                                         &error);
+
+      if (auth->keyring == NULL)
+        {
+          if (dbus_error_has_name (&error,
+                                   DBUS_ERROR_NO_MEMORY))
+            {
+              dbus_error_free (&error);
+              goto out_3;
+            }
+          else
+            {
+              _DBUS_ASSERT_ERROR_IS_SET (&error);
+
+              _dbus_verbose ("%s: Error loading keyring: %s\n",
+                             DBUS_AUTH_NAME (auth), error.message);
+              
+              if (send_error (auth, "Could not load cookie file"))
+                retval = TRUE; /* retval is only about mem */
+              
+              dbus_error_free (&error);
+              goto out_3;
+            }
+        }
+      else
+        {
+          _dbus_assert (!dbus_error_is_set (&error));
+        }
+    }
+  
+  _dbus_assert (auth->keyring != NULL);
+  
+  if (!_dbus_string_init (&tmp))
+    goto out_3;
+  
+  if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES))
+    goto out_4;
+
+  if (!_dbus_string_init (&client_challenge))
+    goto out_4;
+
+  if (!_dbus_string_hex_encode (&tmp, 0, &client_challenge, 0))
+    goto out_5;
+
+  if (!_dbus_string_init (&correct_hash))
+    goto out_5;
+  
+  if (!sha1_compute_hash (auth, val,
+                          &server_challenge,
+                          &client_challenge,
+                          &correct_hash))
+    goto out_6;
+
+  if (_dbus_string_get_length (&correct_hash) == 0)
+    {
+      /* couldn't find the cookie ID or something */
+      if (send_error (auth, "Don't have the requested cookie ID"))
+        retval = TRUE;
+      goto out_6;
+    }
+  
+  _dbus_string_set_length (&tmp, 0);
+  
+  if (!_dbus_string_copy (&client_challenge, 0, &tmp,
+                          _dbus_string_get_length (&tmp)))
+    goto out_6;
+
+  if (!_dbus_string_append (&tmp, " "))
+    goto out_6;
+
+  if (!_dbus_string_copy (&correct_hash, 0, &tmp,
+                          _dbus_string_get_length (&tmp)))
+    goto out_6;
+
+  if (!send_data (auth, &tmp))
+    goto out_6;
+
+  retval = TRUE;
+
+ out_6:
+  _dbus_string_zero (&correct_hash);
+  _dbus_string_free (&correct_hash);
+ out_5:
+  _dbus_string_free (&client_challenge);
+ out_4:
+  _dbus_string_zero (&tmp);
+  _dbus_string_free (&tmp);
+ out_3:
+  _dbus_string_free (&server_challenge);
+ out_2:
+  _dbus_string_free (&cookie_id_str);
+ out_1:
+  _dbus_string_free (&context);
+ out_0:
+  return retval;
+}
+
+static void
+handle_client_shutdown_cookie_sha1_mech (DBusAuth *auth)
+{
+  auth->cookie_id = -1;  
+  _dbus_string_set_length (&auth->challenge, 0);
+}
+
+/*
+ * EXTERNAL mechanism
+ */
+
+static dbus_bool_t
+handle_server_data_external_mech (DBusAuth         *auth,
+                                  const DBusString *data)
+{
+  if (_dbus_credentials_are_anonymous (auth->credentials))
+    {
+      _dbus_verbose ("%s: no credentials, mechanism EXTERNAL can't authenticate\n",
+                     DBUS_AUTH_NAME (auth));
+      return send_rejected (auth);
+    }
+  
+  if (_dbus_string_get_length (data) > 0)
+    {
+      if (_dbus_string_get_length (&auth->identity) > 0)
+        {
+          /* Tried to send two auth identities, wtf */
+          _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n",
+                         DBUS_AUTH_NAME (auth));
+          return send_rejected (auth);
+        }
+      else
+        {
+          /* this is our auth identity */
+          if (!_dbus_string_copy (data, 0, &auth->identity, 0))
+            return FALSE;
+        }
+    }
+
+  /* Poke client for an auth identity, if none given */
+  if (_dbus_string_get_length (&auth->identity) == 0 &&
+      !auth->already_asked_for_initial_response)
+    {
+      if (send_data (auth, NULL))
+        {
+          _dbus_verbose ("%s: sending empty challenge asking client for auth identity\n",
+                         DBUS_AUTH_NAME (auth));
+          auth->already_asked_for_initial_response = TRUE;
+          goto_state (auth, &server_state_waiting_for_data);
+          return TRUE;
+        }
+      else
+        return FALSE;
+    }
+
+  _dbus_credentials_clear (auth->desired_identity);
+  
+  /* If auth->identity is still empty here, then client
+   * responded with an empty string after we poked it for
+   * an initial response. This means to try to auth the
+   * identity provided in the credentials.
+   */
+  if (_dbus_string_get_length (&auth->identity) == 0)
+    {
+      if (!_dbus_credentials_add_credentials (auth->desired_identity,
+                                              auth->credentials))
+        {
+          return FALSE; /* OOM */
+        }
+    }
+  else
+    {
+      if (!_dbus_credentials_add_from_user (auth->desired_identity,
+                                            &auth->identity))
+        {
+          _dbus_verbose ("%s: could not get credentials from uid string\n",
+                         DBUS_AUTH_NAME (auth));
+          return send_rejected (auth);
+        }
+    }
+
+  if (_dbus_credentials_are_anonymous (auth->desired_identity))
+    {
+      _dbus_verbose ("%s: desired user %s is no good\n",
+                     DBUS_AUTH_NAME (auth),
+                     _dbus_string_get_const_data (&auth->identity));
+      return send_rejected (auth);
+    }
+  
+  if (_dbus_credentials_are_superset (auth->credentials,
+                                      auth->desired_identity))
+    {
+      /* client has authenticated */
+      if (!_dbus_credentials_add_credentials (auth->authorized_identity,
+                                              auth->desired_identity))
+        return FALSE;
+
+      /* also copy process ID from the socket credentials
+       */
+      if (!_dbus_credentials_add_credential (auth->authorized_identity,
+                                             DBUS_CREDENTIAL_UNIX_PROCESS_ID,
+                                             auth->credentials))
+        return FALSE;
+
+      /* also copy audit data from the socket credentials
+       */
+      if (!_dbus_credentials_add_credential (auth->authorized_identity,
+                                             DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID,
+                                             auth->credentials))
+        return FALSE;
+      
+      if (!send_ok (auth))
+        return FALSE;
+
+      _dbus_verbose ("%s: authenticated client based on socket credentials\n",
+                     DBUS_AUTH_NAME (auth));
+
+      return TRUE;
+    }
+  else
+    {
+      _dbus_verbose ("%s: desired identity not found in socket credentials\n",
+                     DBUS_AUTH_NAME (auth));
+      return send_rejected (auth);
+    }
+}
+
+static void
+handle_server_shutdown_external_mech (DBusAuth *auth)
+{
+
+}
+
+static dbus_bool_t
+handle_client_initial_response_external_mech (DBusAuth         *auth,
+                                              DBusString       *response)
+{
+  /* We always append our UID as an initial response, so the server
+   * doesn't have to send back an empty challenge to check whether we
+   * want to specify an identity. i.e. this avoids a round trip that
+   * the spec for the EXTERNAL mechanism otherwise requires.
+   */
+  DBusString plaintext;
+
+  if (!_dbus_string_init (&plaintext))
+    return FALSE;
+
+  if (!_dbus_append_user_from_current_process (&plaintext))
+    goto failed;
+
+  if (!_dbus_string_hex_encode (&plaintext, 0,
+                               response,
+                               _dbus_string_get_length (response)))
+    goto failed;
+
+  _dbus_string_free (&plaintext);
+  
+  return TRUE;
+
+ failed:
+  _dbus_string_free (&plaintext);
+  return FALSE;  
+}
+
+static dbus_bool_t
+handle_client_data_external_mech (DBusAuth         *auth,
+                                  const DBusString *data)
+{
+  
+  return TRUE;
+}
+
+static void
+handle_client_shutdown_external_mech (DBusAuth *auth)
+{
+
+}
+
+/*
+ * ANONYMOUS mechanism
+ */
+
+static dbus_bool_t
+handle_server_data_anonymous_mech (DBusAuth         *auth,
+                                   const DBusString *data)
+{  
+  if (_dbus_string_get_length (data) > 0)
+    {
+      /* Client is allowed to send "trace" data, the only defined
+       * meaning is that if it contains '@' it is an email address,
+       * and otherwise it is anything else, and it's supposed to be
+       * UTF-8
+       */
+      if (!_dbus_string_validate_utf8 (data, 0, _dbus_string_get_length (data)))
+        {
+          _dbus_verbose ("%s: Received invalid UTF-8 trace data from ANONYMOUS client\n",
+                         DBUS_AUTH_NAME (auth));
+
+          {
+            DBusString plaintext;
+            DBusString encoded;
+            _dbus_string_init_const (&plaintext, "D-Bus " VERSION);
+            _dbus_string_init (&encoded);
+            _dbus_string_hex_encode (&plaintext, 0,
+                                     &encoded,
+                                     0);
+              _dbus_verbose ("%s: try '%s'\n",
+                             DBUS_AUTH_NAME (auth), _dbus_string_get_const_data (&encoded));
+          }
+          return send_rejected (auth);
+        }
+      
+      _dbus_verbose ("%s: ANONYMOUS client sent trace string: '%s'\n",
+                     DBUS_AUTH_NAME (auth),
+                     _dbus_string_get_const_data (data));
+    }
+
+  /* We want to be anonymous (clear in case some other protocol got midway through I guess) */
+  _dbus_credentials_clear (auth->desired_identity);
+
+  /* Copy process ID from the socket credentials
+   */
+  if (!_dbus_credentials_add_credential (auth->authorized_identity,
+                                         DBUS_CREDENTIAL_UNIX_PROCESS_ID,
+                                         auth->credentials))
+    return FALSE;
+  
+  /* Anonymous is always allowed */
+  if (!send_ok (auth))
+    return FALSE;
+
+  _dbus_verbose ("%s: authenticated client as anonymous\n",
+                 DBUS_AUTH_NAME (auth));
+
+  return TRUE;
+}
+
+static void
+handle_server_shutdown_anonymous_mech (DBusAuth *auth)
+{
+  
+}
+
+static dbus_bool_t
+handle_client_initial_response_anonymous_mech (DBusAuth         *auth,
+                                               DBusString       *response)
+{
+  /* Our initial response is a "trace" string which must be valid UTF-8
+   * and must be an email address if it contains '@'.
+   * We just send the dbus implementation info, like a user-agent or
+   * something, because... why not. There's nothing guaranteed here
+   * though, we could change it later.
+   */
+  DBusString plaintext;
+
+  if (!_dbus_string_init (&plaintext))
+    return FALSE;
+
+  if (!_dbus_string_append (&plaintext,
+                            "libdbus " VERSION))
+    goto failed;
+
+  if (!_dbus_string_hex_encode (&plaintext, 0,
+                               response,
+                               _dbus_string_get_length (response)))
+    goto failed;
+
+  _dbus_string_free (&plaintext);
+  
+  return TRUE;
+
+ failed:
+  _dbus_string_free (&plaintext);
+  return FALSE;  
+}
+
+static dbus_bool_t
+handle_client_data_anonymous_mech (DBusAuth         *auth,
+                                  const DBusString *data)
+{
+  
+  return TRUE;
+}
+
+static void
+handle_client_shutdown_anonymous_mech (DBusAuth *auth)
+{
+  
+}
+
+/* Put mechanisms here in order of preference.
+ * Right now we have:
+ *
+ * - EXTERNAL checks socket credentials (or in the future, other info from the OS)
+ * - DBUS_COOKIE_SHA1 uses a cookie in the home directory, like xauth or ICE
+ * - ANONYMOUS checks nothing but doesn't auth the person as a user
+ *
+ * We might ideally add a mechanism to chain to Cyrus SASL so we can
+ * use its mechanisms as well.
+ * 
+ */
+static const DBusAuthMechanismHandler
+all_mechanisms[] = {
+  { "EXTERNAL",
+    handle_server_data_external_mech,
+    NULL, NULL,
+    handle_server_shutdown_external_mech,
+    handle_client_initial_response_external_mech,
+    handle_client_data_external_mech,
+    NULL, NULL,
+    handle_client_shutdown_external_mech },
+  { "DBUS_COOKIE_SHA1",
+    handle_server_data_cookie_sha1_mech,
+    NULL, NULL,
+    handle_server_shutdown_cookie_sha1_mech,
+    handle_client_initial_response_cookie_sha1_mech,
+    handle_client_data_cookie_sha1_mech,
+    NULL, NULL,
+    handle_client_shutdown_cookie_sha1_mech },
+  { "ANONYMOUS",
+    handle_server_data_anonymous_mech,
+    NULL, NULL,
+    handle_server_shutdown_anonymous_mech,
+    handle_client_initial_response_anonymous_mech,
+    handle_client_data_anonymous_mech,
+    NULL, NULL,
+    handle_client_shutdown_anonymous_mech },  
+  { NULL, NULL }
+};
+
+static const DBusAuthMechanismHandler*
+find_mech (const DBusString  *name,
+           char             **allowed_mechs)
+{
+  int i;
+  
+  if (allowed_mechs != NULL &&
+      !_dbus_string_array_contains ((const char**) allowed_mechs,
+                                    _dbus_string_get_const_data (name)))
+    return NULL;
+  
+  i = 0;
+  while (all_mechanisms[i].mechanism != NULL)
+    {      
+      if (_dbus_string_equal_c_str (name,
+                                    all_mechanisms[i].mechanism))
+
+        return &all_mechanisms[i];
+      
+      ++i;
+    }
+  
+  return NULL;
+}
+
+static dbus_bool_t
+send_auth (DBusAuth *auth, const DBusAuthMechanismHandler *mech)
+{
+  DBusString auth_command;
+
+  if (!_dbus_string_init (&auth_command))
+    return FALSE;
+      
+  if (!_dbus_string_append (&auth_command,
+                            "AUTH "))
+    {
+      _dbus_string_free (&auth_command);
+      return FALSE;
+    }  
+  
+  if (!_dbus_string_append (&auth_command,
+                            mech->mechanism))
+    {
+      _dbus_string_free (&auth_command);
+      return FALSE;
+    }
+
+  if (mech->client_initial_response_func != NULL)
+    {
+      if (!_dbus_string_append (&auth_command, " "))
+        {
+          _dbus_string_free (&auth_command);
+          return FALSE;
+        }
+      
+      if (!(* mech->client_initial_response_func) (auth, &auth_command))
+        {
+          _dbus_string_free (&auth_command);
+          return FALSE;
+        }
+    }
+  
+  if (!_dbus_string_append (&auth_command,
+                            "\r\n"))
+    {
+      _dbus_string_free (&auth_command);
+      return FALSE;
+    }
+
+  if (!_dbus_string_copy (&auth_command, 0,
+                          &auth->outgoing,
+                          _dbus_string_get_length (&auth->outgoing)))
+    {
+      _dbus_string_free (&auth_command);
+      return FALSE;
+    }
+
+  _dbus_string_free (&auth_command);
+  shutdown_mech (auth);
+  auth->mech = mech;      
+  goto_state (auth, &client_state_waiting_for_data);
+
+  return TRUE;
+}
+
+static dbus_bool_t
+send_data (DBusAuth *auth, DBusString *data)
+{
+  int old_len;
+
+  if (data == NULL || _dbus_string_get_length (data) == 0)
+    return _dbus_string_append (&auth->outgoing, "DATA\r\n");
+  else
+    {
+      old_len = _dbus_string_get_length (&auth->outgoing);
+      if (!_dbus_string_append (&auth->outgoing, "DATA "))
+        goto out;
+
+      if (!_dbus_string_hex_encode (data, 0, &auth->outgoing,
+                                    _dbus_string_get_length (&auth->outgoing)))
+        goto out;
+
+      if (!_dbus_string_append (&auth->outgoing, "\r\n"))
+        goto out;
+
+      return TRUE;
+
+    out:
+      _dbus_string_set_length (&auth->outgoing, old_len);
+
+      return FALSE;
+    }
+}
+
+static dbus_bool_t
+send_rejected (DBusAuth *auth)
+{
+  DBusString command;
+  DBusAuthServer *server_auth;
+  int i;
+  
+  if (!_dbus_string_init (&command))
+    return FALSE;
+  
+  if (!_dbus_string_append (&command,
+                            "REJECTED"))
+    goto nomem;
+
+  i = 0;
+  while (all_mechanisms[i].mechanism != NULL)
+    {
+      if (!_dbus_string_append (&command,
+                                " "))
+        goto nomem;
+
+      if (!_dbus_string_append (&command,
+                                all_mechanisms[i].mechanism))
+        goto nomem;
+      
+      ++i;
+    }
+  
+  if (!_dbus_string_append (&command, "\r\n"))
+    goto nomem;
+
+  if (!_dbus_string_copy (&command, 0, &auth->outgoing,
+                          _dbus_string_get_length (&auth->outgoing)))
+    goto nomem;
+
+  shutdown_mech (auth);
+  
+  _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
+  server_auth = DBUS_AUTH_SERVER (auth);
+  server_auth->failures += 1;
+
+  if (server_auth->failures >= server_auth->max_failures)
+    goto_state (auth, &common_state_need_disconnect);
+  else
+    goto_state (auth, &server_state_waiting_for_auth);
+
+  _dbus_string_free (&command);
+  
+  return TRUE;
+
+ nomem:
+  _dbus_string_free (&command);
+  return FALSE;
+}
+
+static dbus_bool_t
+send_error (DBusAuth *auth, const char *message)
+{
+  return _dbus_string_append_printf (&auth->outgoing,
+                                     "ERROR \"%s\"\r\n", message);
+}
+
+static dbus_bool_t
+send_ok (DBusAuth *auth)
+{
+  int orig_len;
+
+  orig_len = _dbus_string_get_length (&auth->outgoing);
+  
+  if (_dbus_string_append (&auth->outgoing, "OK ") &&
+      _dbus_string_copy (& DBUS_AUTH_SERVER (auth)->guid,
+                         0,
+                         &auth->outgoing,
+                         _dbus_string_get_length (&auth->outgoing)) &&
+      _dbus_string_append (&auth->outgoing, "\r\n"))
+    {
+      goto_state (auth, &server_state_waiting_for_begin);
+      return TRUE;
+    }
+  else
+    {
+      _dbus_string_set_length (&auth->outgoing, orig_len);
+      return FALSE;
+    }
+}
+
+static dbus_bool_t
+send_begin (DBusAuth         *auth,
+            const DBusString *args_from_ok)
+{
+  int end_of_hex;
+  
+  /* "args_from_ok" should be the GUID, whitespace already pulled off the front */
+  _dbus_assert (_dbus_string_get_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server) == 0);
+
+  /* We decode the hex string to binary, using guid_from_server as scratch... */
+  
+  end_of_hex = 0;
+  if (!_dbus_string_hex_decode (args_from_ok, 0, &end_of_hex,
+                                & DBUS_AUTH_CLIENT (auth)->guid_from_server, 0))
+    return FALSE;
+
+  /* now clear out the scratch */
+  _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);
+  
+  if (end_of_hex != _dbus_string_get_length (args_from_ok) ||
+      end_of_hex == 0)
+    {
+      _dbus_verbose ("Bad GUID from server, parsed %d bytes and had %d bytes from server\n",
+                     end_of_hex, _dbus_string_get_length (args_from_ok));
+      goto_state (auth, &common_state_need_disconnect);
+      return TRUE;
+    }
+
+  if (_dbus_string_copy (args_from_ok, 0, &DBUS_AUTH_CLIENT (auth)->guid_from_server, 0) &&
+      _dbus_string_append (&auth->outgoing, "BEGIN\r\n"))
+    {
+      _dbus_verbose ("Got GUID '%s' from the server\n",
+                     _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server));
+      
+      goto_state (auth, &common_state_authenticated);
+      return TRUE;
+    }
+  else
+    {
+      _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);
+      return FALSE;
+    }
+}
+
+static dbus_bool_t
+send_cancel (DBusAuth *auth)
+{
+  if (_dbus_string_append (&auth->outgoing, "CANCEL\r\n"))
+    {
+      goto_state (auth, &client_state_waiting_for_reject);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+static dbus_bool_t
+process_data (DBusAuth             *auth,
+              const DBusString     *args,
+              DBusAuthDataFunction  data_func)
+{
+  int end;
+  DBusString decoded;
+
+  if (!_dbus_string_init (&decoded))
+    return FALSE;
+
+  if (!_dbus_string_hex_decode (args, 0, &end, &decoded, 0))
+    {
+      _dbus_string_free (&decoded);
+      return FALSE;
+    }
+
+  if (_dbus_string_get_length (args) != end)
+    {
+      _dbus_string_free (&decoded);
+      if (!send_error (auth, "Invalid hex encoding"))
+        return FALSE;
+
+      return TRUE;
+    }
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+  if (_dbus_string_validate_ascii (&decoded, 0,
+                                   _dbus_string_get_length (&decoded)))
+    _dbus_verbose ("%s: data: '%s'\n",
+                   DBUS_AUTH_NAME (auth),
+                   _dbus_string_get_const_data (&decoded));
+#endif
+      
+  if (!(* data_func) (auth, &decoded))
+    {
+      _dbus_string_free (&decoded);
+      return FALSE;
+    }
+
+  _dbus_string_free (&decoded);
+  return TRUE;
+}
+
+static dbus_bool_t
+handle_auth (DBusAuth *auth, const DBusString *args)
+{
+  if (_dbus_string_get_length (args) == 0)
+    {
+      /* No args to the auth, send mechanisms */
+      if (!send_rejected (auth))
+        return FALSE;
+
+      return TRUE;
+    }
+  else
+    {
+      int i;
+      DBusString mech;
+      DBusString hex_response;
+      
+      _dbus_string_find_blank (args, 0, &i);
+
+      if (!_dbus_string_init (&mech))
+        return FALSE;
+
+      if (!_dbus_string_init (&hex_response))
+        {
+          _dbus_string_free (&mech);
+          return FALSE;
+        }
+      
+      if (!_dbus_string_copy_len (args, 0, i, &mech, 0))
+        goto failed;
+
+      _dbus_string_skip_blank (args, i, &i);
+      if (!_dbus_string_copy (args, i, &hex_response, 0))
+        goto failed;
+     
+      auth->mech = find_mech (&mech, auth->allowed_mechs);
+      if (auth->mech != NULL)
+        {
+          _dbus_verbose ("%s: Trying mechanism %s\n",
+                         DBUS_AUTH_NAME (auth),
+                         auth->mech->mechanism);
+          
+          if (!process_data (auth, &hex_response,
+                             auth->mech->server_data_func))
+            goto failed;
+        }
+      else
+        {
+          /* Unsupported mechanism */
+          _dbus_verbose ("%s: Unsupported mechanism %s\n",
+                         DBUS_AUTH_NAME (auth),
+                         _dbus_string_get_const_data (&mech));
+          
+          if (!send_rejected (auth))
+            goto failed;
+        }
+
+      _dbus_string_free (&mech);      
+      _dbus_string_free (&hex_response);
+
+      return TRUE;
+      
+    failed:
+      auth->mech = NULL;
+      _dbus_string_free (&mech);
+      _dbus_string_free (&hex_response);
+      return FALSE;
+    }
+}
+
+static dbus_bool_t
+handle_server_state_waiting_for_auth  (DBusAuth         *auth,
+                                       DBusAuthCommand   command,
+                                       const DBusString *args)
+{
+  switch (command)
+    {
+    case DBUS_AUTH_COMMAND_AUTH:
+      return handle_auth (auth, args);
+
+    case DBUS_AUTH_COMMAND_CANCEL:
+    case DBUS_AUTH_COMMAND_DATA:
+      return send_error (auth, "Not currently in an auth conversation");
+
+    case DBUS_AUTH_COMMAND_BEGIN:
+      goto_state (auth, &common_state_need_disconnect);
+      return TRUE;
+
+    case DBUS_AUTH_COMMAND_ERROR:
+      return send_rejected (auth);
+
+    case DBUS_AUTH_COMMAND_REJECTED:
+    case DBUS_AUTH_COMMAND_OK:
+    case DBUS_AUTH_COMMAND_UNKNOWN:
+    default:
+      return send_error (auth, "Unknown command");
+    }
+}
+
+static dbus_bool_t
+handle_server_state_waiting_for_data  (DBusAuth         *auth,
+                                       DBusAuthCommand   command,
+                                       const DBusString *args)
+{
+  switch (command)
+    {
+    case DBUS_AUTH_COMMAND_AUTH:
+      return send_error (auth, "Sent AUTH while another AUTH in progress");
+
+    case DBUS_AUTH_COMMAND_CANCEL:
+    case DBUS_AUTH_COMMAND_ERROR:
+      return send_rejected (auth);
+
+    case DBUS_AUTH_COMMAND_DATA:
+      return process_data (auth, args, auth->mech->server_data_func);
+
+    case DBUS_AUTH_COMMAND_BEGIN:
+      goto_state (auth, &common_state_need_disconnect);
+      return TRUE;
+
+    case DBUS_AUTH_COMMAND_REJECTED:
+    case DBUS_AUTH_COMMAND_OK:
+    case DBUS_AUTH_COMMAND_UNKNOWN:
+    default:
+      return send_error (auth, "Unknown command");
+    }
+}
+
+static dbus_bool_t
+handle_server_state_waiting_for_begin (DBusAuth         *auth,
+                                       DBusAuthCommand   command,
+                                       const DBusString *args)
+{
+  switch (command)
+    {
+    case DBUS_AUTH_COMMAND_AUTH:
+      return send_error (auth, "Sent AUTH while expecting BEGIN");
+
+    case DBUS_AUTH_COMMAND_DATA:
+      return send_error (auth, "Sent DATA while expecting BEGIN");
+
+    case DBUS_AUTH_COMMAND_BEGIN:
+      goto_state (auth, &common_state_authenticated);
+      return TRUE;
+
+    case DBUS_AUTH_COMMAND_REJECTED:
+    case DBUS_AUTH_COMMAND_OK:
+    case DBUS_AUTH_COMMAND_UNKNOWN:
+    default:
+      return send_error (auth, "Unknown command");
+
+    case DBUS_AUTH_COMMAND_CANCEL:
+    case DBUS_AUTH_COMMAND_ERROR:
+      return send_rejected (auth);
+    }
+}
+
+/* return FALSE if no memory, TRUE if all OK */
+static dbus_bool_t
+get_word (const DBusString *str,
+          int              *start,
+          DBusString       *word)
+{
+  int i;
+
+  _dbus_string_skip_blank (str, *start, start);
+  _dbus_string_find_blank (str, *start, &i);
+  
+  if (i > *start)
+    {
+      if (!_dbus_string_copy_len (str, *start, i - *start, word, 0))
+        return FALSE;
+      
+      *start = i;
+    }
+
+  return TRUE;
+}
+
+static dbus_bool_t
+record_mechanisms (DBusAuth         *auth,
+                   const DBusString *args)
+{
+  int next;
+  int len;
+
+  if (auth->already_got_mechanisms)
+    return TRUE;
+  
+  len = _dbus_string_get_length (args);
+  
+  next = 0;
+  while (next < len)
+    {
+      DBusString m;
+      const DBusAuthMechanismHandler *mech;
+      
+      if (!_dbus_string_init (&m))
+        goto nomem;
+      
+      if (!get_word (args, &next, &m))
+        {
+          _dbus_string_free (&m);
+          goto nomem;
+        }
+
+      mech = find_mech (&m, auth->allowed_mechs);
+
+      if (mech != NULL)
+        {
+          /* FIXME right now we try mechanisms in the order
+           * the server lists them; should we do them in
+           * some more deterministic order?
+           *
+           * Probably in all_mechanisms order, our order of
+           * preference. Of course when the server is us,
+           * it lists things in that order anyhow.
+           */
+
+          if (mech != &all_mechanisms[0])
+            {
+              _dbus_verbose ("%s: Adding mechanism %s to list we will try\n",
+                             DBUS_AUTH_NAME (auth), mech->mechanism);
+          
+              if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
+                                      (void*) mech))
+                {
+                  _dbus_string_free (&m);
+                  goto nomem;
+                }
+            }
+          else
+            {
+              _dbus_verbose ("%s: Already tried mechanism %s; not adding to list we will try\n",
+                             DBUS_AUTH_NAME (auth), mech->mechanism);
+            }
+        }
+      else
+        {
+          _dbus_verbose ("%s: Server offered mechanism \"%s\" that we don't know how to use\n",
+                         DBUS_AUTH_NAME (auth),
+                         _dbus_string_get_const_data (&m));
+        }
+
+      _dbus_string_free (&m);
+    }
+  
+  auth->already_got_mechanisms = TRUE;
+  
+  return TRUE;
+
+ nomem:
+  _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
+  
+  return FALSE;
+}
+
+static dbus_bool_t
+process_rejected (DBusAuth *auth, const DBusString *args)
+{
+  const DBusAuthMechanismHandler *mech;
+  DBusAuthClient *client;
+
+  client = DBUS_AUTH_CLIENT (auth);
+
+  if (!auth->already_got_mechanisms)
+    {
+      if (!record_mechanisms (auth, args))
+        return FALSE;
+    }
+  
+  if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL)
+    {
+      mech = client->mechs_to_try->data;
+
+      if (!send_auth (auth, mech))
+        return FALSE;
+
+      _dbus_list_pop_first (&client->mechs_to_try);
+
+      _dbus_verbose ("%s: Trying mechanism %s\n",
+                     DBUS_AUTH_NAME (auth),
+                     mech->mechanism);
+    }
+  else
+    {
+      /* Give up */
+      _dbus_verbose ("%s: Disconnecting because we are out of mechanisms to try using\n",
+                     DBUS_AUTH_NAME (auth));
+      goto_state (auth, &common_state_need_disconnect);
+    }
+  
+  return TRUE;
+}
+
+
+static dbus_bool_t
+handle_client_state_waiting_for_data (DBusAuth         *auth,
+                                      DBusAuthCommand   command,
+                                      const DBusString *args)
+{
+  _dbus_assert (auth->mech != NULL);
+  switch (command)
+    {
+    case DBUS_AUTH_COMMAND_DATA:
+      return process_data (auth, args, auth->mech->client_data_func);
+
+    case DBUS_AUTH_COMMAND_REJECTED:
+      return process_rejected (auth, args);
+
+    case DBUS_AUTH_COMMAND_OK:
+      return send_begin (auth, args);
+
+    case DBUS_AUTH_COMMAND_ERROR:
+      return send_cancel (auth);
+
+    case DBUS_AUTH_COMMAND_AUTH:
+    case DBUS_AUTH_COMMAND_CANCEL:
+    case DBUS_AUTH_COMMAND_BEGIN:
+    case DBUS_AUTH_COMMAND_UNKNOWN:
+    default:
+      return send_error (auth, "Unknown command");
+    }
+}
+
+static dbus_bool_t
+handle_client_state_waiting_for_ok (DBusAuth         *auth,
+                                    DBusAuthCommand   command,
+                                    const DBusString *args)
+{
+  switch (command)
+    {
+    case DBUS_AUTH_COMMAND_REJECTED:
+      return process_rejected (auth, args);
+
+    case DBUS_AUTH_COMMAND_OK:
+      return send_begin (auth, args);
+
+    case DBUS_AUTH_COMMAND_DATA:
+    case DBUS_AUTH_COMMAND_ERROR:
+      return send_cancel (auth);
+
+    case DBUS_AUTH_COMMAND_AUTH:
+    case DBUS_AUTH_COMMAND_CANCEL:
+    case DBUS_AUTH_COMMAND_BEGIN:
+    case DBUS_AUTH_COMMAND_UNKNOWN:
+    default:
+      return send_error (auth, "Unknown command");
+    }
+}
+
+static dbus_bool_t
+handle_client_state_waiting_for_reject (DBusAuth         *auth,
+                                        DBusAuthCommand   command,
+                                        const DBusString *args)
+{
+  switch (command)
+    {
+    case DBUS_AUTH_COMMAND_REJECTED:
+      return process_rejected (auth, args);
+      
+    case DBUS_AUTH_COMMAND_AUTH:
+    case DBUS_AUTH_COMMAND_CANCEL:
+    case DBUS_AUTH_COMMAND_DATA:
+    case DBUS_AUTH_COMMAND_BEGIN:
+    case DBUS_AUTH_COMMAND_OK:
+    case DBUS_AUTH_COMMAND_ERROR:
+    case DBUS_AUTH_COMMAND_UNKNOWN:
+    default:
+      goto_state (auth, &common_state_need_disconnect);
+      return TRUE;
+    }
+}
+
+/**
+ * Mapping from command name to enum
+ */
+typedef struct {
+  const char *name;        /**< Name of the command */
+  DBusAuthCommand command; /**< Corresponding enum */
+} DBusAuthCommandName;
+
+static const DBusAuthCommandName auth_command_names[] = {
+  { "AUTH",     DBUS_AUTH_COMMAND_AUTH },
+  { "CANCEL",   DBUS_AUTH_COMMAND_CANCEL },
+  { "DATA",     DBUS_AUTH_COMMAND_DATA },
+  { "BEGIN",    DBUS_AUTH_COMMAND_BEGIN },
+  { "REJECTED", DBUS_AUTH_COMMAND_REJECTED },
+  { "OK",       DBUS_AUTH_COMMAND_OK },
+  { "ERROR",    DBUS_AUTH_COMMAND_ERROR }
+};
+
+static DBusAuthCommand
+lookup_command_from_name (DBusString *command)
+{
+  int i;
+
+  for (i = 0; i < _DBUS_N_ELEMENTS (auth_command_names); i++)
+    {
+      if (_dbus_string_equal_c_str (command,
+                                    auth_command_names[i].name))
+        return auth_command_names[i].command;
+    }
+
+  return DBUS_AUTH_COMMAND_UNKNOWN;
+}
+
+static void
+goto_state (DBusAuth *auth,
+            const DBusAuthStateData *state)
+{
+  _dbus_verbose ("%s: going from state %s to state %s\n",
+                 DBUS_AUTH_NAME (auth),
+                 auth->state->name,
+                 state->name);
+
+  auth->state = state;
+}
+
+/* returns whether to call it again right away */
+static dbus_bool_t
+process_command (DBusAuth *auth)
+{
+  DBusAuthCommand command;
+  DBusString line;
+  DBusString args;
+  int eol;
+  int i, j;
+  dbus_bool_t retval;
+
+  /* _dbus_verbose ("%s:   trying process_command()\n"); */
+  
+  retval = FALSE;
+  
+  eol = 0;
+  if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol))
+    return FALSE;
+  
+  if (!_dbus_string_init (&line))
+    {
+      auth->needed_memory = TRUE;
+      return FALSE;
+    }
+
+  if (!_dbus_string_init (&args))
+    {
+      _dbus_string_free (&line);
+      auth->needed_memory = TRUE;
+      return FALSE;
+    }
+  
+  if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &line, 0))
+    goto out;
+
+  if (!_dbus_string_validate_ascii (&line, 0,
+                                    _dbus_string_get_length (&line)))
+    {
+      _dbus_verbose ("%s: Command contained non-ASCII chars or embedded nul\n",
+                     DBUS_AUTH_NAME (auth));
+      if (!send_error (auth, "Command contained non-ASCII"))
+        goto out;
+      else
+        goto next_command;
+    }
+  
+  _dbus_verbose ("%s: got command \"%s\"\n",
+                 DBUS_AUTH_NAME (auth),
+                 _dbus_string_get_const_data (&line));
+  
+  _dbus_string_find_blank (&line, 0, &i);
+  _dbus_string_skip_blank (&line, i, &j);
+
+  if (j > i)
+    _dbus_string_delete (&line, i, j - i);
+  
+  if (!_dbus_string_move (&line, i, &args, 0))
+    goto out;
+
+  /* FIXME 1.0 we should probably validate that only the allowed
+   * chars are in the command name
+   */
+  
+  command = lookup_command_from_name (&line);
+  if (!(* auth->state->handler) (auth, command, &args))
+    goto out;
+
+ next_command:
+  
+  /* We've succeeded in processing the whole command so drop it out
+   * of the incoming buffer and return TRUE to try another command.
+   */
+
+  _dbus_string_delete (&auth->incoming, 0, eol);
+  
+  /* kill the \r\n */
+  _dbus_string_delete (&auth->incoming, 0, 2);
+
+  retval = TRUE;
+  
+ out:
+  _dbus_string_free (&args);
+  _dbus_string_free (&line);
+
+  if (!retval)
+    auth->needed_memory = TRUE;
+  else
+    auth->needed_memory = FALSE;
+  
+  return retval;
+}
+
+
+/** @} */
+
+/**
+ * @addtogroup DBusAuth
+ * @{
+ */
+
+/**
+ * Creates a new auth conversation object for the server side.
+ * See doc/dbus-sasl-profile.txt for full details on what
+ * this object does.
+ *
+ * @returns the new object or #NULL if no memory
+ */
+DBusAuth*
+_dbus_auth_server_new (const DBusString *guid)
+{
+  DBusAuth *auth;
+  DBusAuthServer *server_auth;
+  DBusString guid_copy;
+
+  if (!_dbus_string_init (&guid_copy))
+    return NULL;
+
+  if (!_dbus_string_copy (guid, 0, &guid_copy, 0))
+    {
+      _dbus_string_free (&guid_copy);
+      return NULL;
+    }
+
+  auth = _dbus_auth_new (sizeof (DBusAuthServer));
+  if (auth == NULL)
+    {
+      _dbus_string_free (&guid_copy);
+      return NULL;
+    }
+  
+  auth->side = auth_side_server;
+  auth->state = &server_state_waiting_for_auth;
+
+  server_auth = DBUS_AUTH_SERVER (auth);
+
+  server_auth->guid = guid_copy;
+  
+  /* perhaps this should be per-mechanism with a lower
+   * max
+   */
+  server_auth->failures = 0;
+  server_auth->max_failures = 6;
+  
+  return auth;
+}
+
+/**
+ * Creates a new auth conversation object for the client side.
+ * See doc/dbus-sasl-profile.txt for full details on what
+ * this object does.
+ *
+ * @returns the new object or #NULL if no memory
+ */
+DBusAuth*
+_dbus_auth_client_new (void)
+{
+  DBusAuth *auth;
+  DBusString guid_str;
+
+  if (!_dbus_string_init (&guid_str))
+    return NULL;
+
+  auth = _dbus_auth_new (sizeof (DBusAuthClient));
+  if (auth == NULL)
+    {
+      _dbus_string_free (&guid_str);
+      return NULL;
+    }
+
+  DBUS_AUTH_CLIENT (auth)->guid_from_server = guid_str;
+
+  auth->side = auth_side_client;
+  auth->state = &client_state_need_send_auth;
+
+  /* Start the auth conversation by sending AUTH for our default
+   * mechanism */
+  if (!send_auth (auth, &all_mechanisms[0]))
+    {
+      _dbus_auth_unref (auth);
+      return NULL;
+    }
+  
+  return auth;
+}
+
+/**
+ * Increments the refcount of an auth object.
+ *
+ * @param auth the auth conversation
+ * @returns the auth conversation
+ */
+DBusAuth *
+_dbus_auth_ref (DBusAuth *auth)
+{
+  _dbus_assert (auth != NULL);
+  
+  auth->refcount += 1;
+  
+  return auth;
+}
+
+/**
+ * Decrements the refcount of an auth object.
+ *
+ * @param auth the auth conversation
+ */
+void
+_dbus_auth_unref (DBusAuth *auth)
+{
+  _dbus_assert (auth != NULL);
+  _dbus_assert (auth->refcount > 0);
+
+  auth->refcount -= 1;
+  if (auth->refcount == 0)
+    {
+      shutdown_mech (auth);
+
+      if (DBUS_AUTH_IS_CLIENT (auth))
+        {
+          _dbus_string_free (& DBUS_AUTH_CLIENT (auth)->guid_from_server);
+          _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
+        }
+      else
+        {
+          _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
+
+          _dbus_string_free (& DBUS_AUTH_SERVER (auth)->guid);
+        }
+
+      if (auth->keyring)
+        _dbus_keyring_unref (auth->keyring);
+
+      _dbus_string_free (&auth->context);
+      _dbus_string_free (&auth->challenge);
+      _dbus_string_free (&auth->identity);
+      _dbus_string_free (&auth->incoming);
+      _dbus_string_free (&auth->outgoing);
+
+      dbus_free_string_array (auth->allowed_mechs);
+
+      _dbus_credentials_unref (auth->credentials);
+      _dbus_credentials_unref (auth->authorized_identity);
+      _dbus_credentials_unref (auth->desired_identity);
+      
+      dbus_free (auth);
+    }
+}
+
+/**
+ * Sets an array of authentication mechanism names
+ * that we are willing to use.
+ *
+ * @param auth the auth conversation
+ * @param mechanisms #NULL-terminated array of mechanism names
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_auth_set_mechanisms (DBusAuth    *auth,
+                           const char **mechanisms)
+{
+  char **copy;
+
+  if (mechanisms != NULL)
+    {
+      copy = _dbus_dup_string_array (mechanisms);
+      if (copy == NULL)
+        return FALSE;
+    }
+  else
+    copy = NULL;
+  
+  dbus_free_string_array (auth->allowed_mechs);
+
+  auth->allowed_mechs = copy;
+
+  return TRUE;
+}
+
+/**
+ * @param auth the auth conversation object
+ * @returns #TRUE if we're in a final state
+ */
+#define DBUS_AUTH_IN_END_STATE(auth) ((auth)->state->handler == NULL)
+
+/**
+ * Analyzes buffered input and moves the auth conversation forward,
+ * returning the new state of the auth conversation.
+ *
+ * @param auth the auth conversation
+ * @returns the new state
+ */
+DBusAuthState
+_dbus_auth_do_work (DBusAuth *auth)
+{
+  auth->needed_memory = FALSE;
+
+  /* Max amount we'll buffer up before deciding someone's on crack */
+#define MAX_BUFFER (16 * _DBUS_ONE_KILOBYTE)
+
+  do
+    {
+      if (DBUS_AUTH_IN_END_STATE (auth))
+        break;
+      
+      if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER ||
+          _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER)
+        {
+          goto_state (auth, &common_state_need_disconnect);
+          _dbus_verbose ("%s: Disconnecting due to excessive data buffered in auth phase\n",
+                         DBUS_AUTH_NAME (auth));
+          break;
+        }
+    }
+  while (process_command (auth));
+
+  if (auth->needed_memory)
+    return DBUS_AUTH_STATE_WAITING_FOR_MEMORY;
+  else if (_dbus_string_get_length (&auth->outgoing) > 0)
+    return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
+  else if (auth->state == &common_state_need_disconnect)
+    return DBUS_AUTH_STATE_NEED_DISCONNECT;
+  else if (auth->state == &common_state_authenticated)
+    return DBUS_AUTH_STATE_AUTHENTICATED;
+  else return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
+}
+
+/**
+ * Gets bytes that need to be sent to the peer we're conversing with.
+ * After writing some bytes, _dbus_auth_bytes_sent() must be called
+ * to notify the auth object that they were written.
+ *
+ * @param auth the auth conversation
+ * @param str return location for a ref to the buffer to send
+ * @returns #FALSE if nothing to send
+ */
+dbus_bool_t
+_dbus_auth_get_bytes_to_send (DBusAuth          *auth,
+                              const DBusString **str)
+{
+  _dbus_assert (auth != NULL);
+  _dbus_assert (str != NULL);
+
+  *str = NULL;
+  
+  if (_dbus_string_get_length (&auth->outgoing) == 0)
+    return FALSE;
+
+  *str = &auth->outgoing;
+
+  return TRUE;
+}
+
+/**
+ * Notifies the auth conversation object that
+ * the given number of bytes of the outgoing buffer
+ * have been written out.
+ *
+ * @param auth the auth conversation
+ * @param bytes_sent number of bytes written out
+ */
+void
+_dbus_auth_bytes_sent (DBusAuth *auth,
+                       int       bytes_sent)
+{
+  _dbus_verbose ("%s: Sent %d bytes of: %s\n",
+                 DBUS_AUTH_NAME (auth),
+                 bytes_sent,
+                 _dbus_string_get_const_data (&auth->outgoing));
+  
+  _dbus_string_delete (&auth->outgoing,
+                       0, bytes_sent);
+}
+
+/**
+ * Get a buffer to be used for reading bytes from the peer we're conversing
+ * with. Bytes should be appended to this buffer.
+ *
+ * @param auth the auth conversation
+ * @param buffer return location for buffer to append bytes to
+ */
+void
+_dbus_auth_get_buffer (DBusAuth     *auth,
+                       DBusString **buffer)
+{
+  _dbus_assert (auth != NULL);
+  _dbus_assert (!auth->buffer_outstanding);
+  
+  *buffer = &auth->incoming;
+
+  auth->buffer_outstanding = TRUE;
+}
+
+/**
+ * Returns a buffer with new data read into it.
+ *
+ * @param auth the auth conversation
+ * @param buffer the buffer being returned
+ * @param bytes_read number of new bytes added
+ */
+void
+_dbus_auth_return_buffer (DBusAuth               *auth,
+                          DBusString             *buffer,
+                          int                     bytes_read)
+{
+  _dbus_assert (buffer == &auth->incoming);
+  _dbus_assert (auth->buffer_outstanding);
+
+  auth->buffer_outstanding = FALSE;
+}
+
+/**
+ * Returns leftover bytes that were not used as part of the auth
+ * conversation.  These bytes will be part of the message stream
+ * instead. This function may not be called until authentication has
+ * succeeded.
+ *
+ * @param auth the auth conversation
+ * @param str return location for pointer to string of unused bytes
+ */
+void
+_dbus_auth_get_unused_bytes (DBusAuth           *auth,
+                             const DBusString **str)
+{
+  if (!DBUS_AUTH_IN_END_STATE (auth))
+    return;
+
+  *str = &auth->incoming;
+}
+
+
+/**
+ * Gets rid of unused bytes returned by _dbus_auth_get_unused_bytes()
+ * after we've gotten them and successfully moved them elsewhere.
+ *
+ * @param auth the auth conversation
+ */
+void
+_dbus_auth_delete_unused_bytes (DBusAuth *auth)
+{
+  if (!DBUS_AUTH_IN_END_STATE (auth))
+    return;
+
+  _dbus_string_set_length (&auth->incoming, 0);
+}
+
+/**
+ * Called post-authentication, indicates whether we need to encode
+ * the message stream with _dbus_auth_encode_data() prior to
+ * sending it to the peer.
+ *
+ * @param auth the auth conversation
+ * @returns #TRUE if we need to encode the stream
+ */
+dbus_bool_t
+_dbus_auth_needs_encoding (DBusAuth *auth)
+{
+  if (auth->state != &common_state_authenticated)
+    return FALSE;
+  
+  if (auth->mech != NULL)
+    {
+      if (DBUS_AUTH_IS_CLIENT (auth))
+        return auth->mech->client_encode_func != NULL;
+      else
+        return auth->mech->server_encode_func != NULL;
+    }
+  else
+    return FALSE;
+}
+
+/**
+ * Called post-authentication, encodes a block of bytes for sending to
+ * the peer. If no encoding was negotiated, just copies the bytes
+ * (you can avoid this by checking _dbus_auth_needs_encoding()).
+ *
+ * @param auth the auth conversation
+ * @param plaintext the plain text data
+ * @param encoded initialized string to where encoded data is appended
+ * @returns #TRUE if we had enough memory and successfully encoded
+ */
+dbus_bool_t
+_dbus_auth_encode_data (DBusAuth         *auth,
+                        const DBusString *plaintext,
+                        DBusString       *encoded)
+{
+  _dbus_assert (plaintext != encoded);
+  
+  if (auth->state != &common_state_authenticated)
+    return FALSE;
+  
+  if (_dbus_auth_needs_encoding (auth))
+    {
+      if (DBUS_AUTH_IS_CLIENT (auth))
+        return (* auth->mech->client_encode_func) (auth, plaintext, encoded);
+      else
+        return (* auth->mech->server_encode_func) (auth, plaintext, encoded);
+    }
+  else
+    {
+      return _dbus_string_copy (plaintext, 0, encoded,
+                                _dbus_string_get_length (encoded));
+    }
+}
+
+/**
+ * Called post-authentication, indicates whether we need to decode
+ * the message stream with _dbus_auth_decode_data() after
+ * receiving it from the peer.
+ *
+ * @param auth the auth conversation
+ * @returns #TRUE if we need to encode the stream
+ */
+dbus_bool_t
+_dbus_auth_needs_decoding (DBusAuth *auth)
+{
+  if (auth->state != &common_state_authenticated)
+    return FALSE;
+    
+  if (auth->mech != NULL)
+    {
+      if (DBUS_AUTH_IS_CLIENT (auth))
+        return auth->mech->client_decode_func != NULL;
+      else
+        return auth->mech->server_decode_func != NULL;
+    }
+  else
+    return FALSE;
+}
+
+
+/**
+ * Called post-authentication, decodes a block of bytes received from
+ * the peer. If no encoding was negotiated, just copies the bytes (you
+ * can avoid this by checking _dbus_auth_needs_decoding()).
+ *
+ * @todo 1.0? We need to be able to distinguish "out of memory" error
+ * from "the data is hosed" error.
+ *
+ * @param auth the auth conversation
+ * @param encoded the encoded data
+ * @param plaintext initialized string where decoded data is appended
+ * @returns #TRUE if we had enough memory and successfully decoded
+ */
+dbus_bool_t
+_dbus_auth_decode_data (DBusAuth         *auth,
+                        const DBusString *encoded,
+                        DBusString       *plaintext)
+{
+  _dbus_assert (plaintext != encoded);
+  
+  if (auth->state != &common_state_authenticated)
+    return FALSE;
+  
+  if (_dbus_auth_needs_decoding (auth))
+    {
+      if (DBUS_AUTH_IS_CLIENT (auth))
+        return (* auth->mech->client_decode_func) (auth, encoded, plaintext);
+      else
+        return (* auth->mech->server_decode_func) (auth, encoded, plaintext);
+    }
+  else
+    {
+      return _dbus_string_copy (encoded, 0, plaintext,
+                                _dbus_string_get_length (plaintext));
+    }
+}
+
+/**
+ * Sets credentials received via reliable means from the operating
+ * system.
+ *
+ * @param auth the auth conversation
+ * @param credentials the credentials received
+ * @returns #FALSE on OOM
+ */
+dbus_bool_t
+_dbus_auth_set_credentials (DBusAuth               *auth,
+                            DBusCredentials        *credentials)
+{
+  _dbus_credentials_clear (auth->credentials);
+  return _dbus_credentials_add_credentials (auth->credentials,
+                                            credentials);
+}
+
+/**
+ * Gets the identity we authorized the client as.  Apps may have
+ * different policies as to what identities they allow.
+ *
+ * Returned credentials are not a copy and should not be modified
+ *
+ * @param auth the auth conversation
+ * @returns the credentials we've authorized BY REFERENCE do not modify
+ */
+DBusCredentials*
+_dbus_auth_get_identity (DBusAuth               *auth)
+{
+  if (auth->state == &common_state_authenticated)
+    {
+      return auth->authorized_identity;
+    }
+  else
+    {
+      /* FIXME instead of this, keep an empty credential around that
+       * doesn't require allocation or something
+       */
+      /* return empty credentials */
+      _dbus_assert (_dbus_credentials_are_empty (auth->authorized_identity));
+      return auth->authorized_identity;
+    }
+}
+
+/**
+ * Gets the GUID from the server if we've authenticated; gets
+ * #NULL otherwise.
+ * @param auth the auth object
+ * @returns the GUID in ASCII hex format
+ */
+const char*
+_dbus_auth_get_guid_from_server (DBusAuth *auth)
+{
+  _dbus_assert (DBUS_AUTH_IS_CLIENT (auth));
+  
+  if (auth->state == &common_state_authenticated)
+    return _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server);
+  else
+    return NULL;
+}
+
+/**
+ * Sets the "authentication context" which scopes cookies
+ * with the DBUS_COOKIE_SHA1 auth mechanism for example.
+ *
+ * @param auth the auth conversation
+ * @param context the context
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_auth_set_context (DBusAuth               *auth,
+                        const DBusString       *context)
+{
+  return _dbus_string_replace_len (context, 0, _dbus_string_get_length (context),
+                                   &auth->context, 0, _dbus_string_get_length (context));
+}
+
+/** @} */
+
+/* tests in dbus-auth-util.c */
diff --git a/src/dbus/dbus-auth.h b/src/dbus/dbus-auth.h
new file mode 100644 (file)
index 0000000..14f8320
--- /dev/null
@@ -0,0 +1,81 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-auth.h Authentication
+ *
+ * Copyright (C) 2002  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_AUTH_H
+#define DBUS_AUTH_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-sysdeps.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusAuth DBusAuth;
+
+typedef enum
+{
+  DBUS_AUTH_STATE_WAITING_FOR_INPUT,
+  DBUS_AUTH_STATE_WAITING_FOR_MEMORY,
+  DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND,
+  DBUS_AUTH_STATE_NEED_DISCONNECT,
+  DBUS_AUTH_STATE_AUTHENTICATED
+} DBusAuthState;
+
+DBusAuth*     _dbus_auth_server_new          (const DBusString       *guid);
+DBusAuth*     _dbus_auth_client_new          (void);
+DBusAuth*     _dbus_auth_ref                 (DBusAuth               *auth);
+void          _dbus_auth_unref               (DBusAuth               *auth);
+dbus_bool_t   _dbus_auth_set_mechanisms      (DBusAuth               *auth,
+                                              const char            **mechanisms);
+DBusAuthState _dbus_auth_do_work             (DBusAuth               *auth);
+dbus_bool_t   _dbus_auth_get_bytes_to_send   (DBusAuth               *auth,
+                                              const DBusString      **str);
+void          _dbus_auth_bytes_sent          (DBusAuth               *auth,
+                                              int                     bytes_sent);
+void          _dbus_auth_get_buffer          (DBusAuth               *auth,
+                                              DBusString            **buffer);
+void          _dbus_auth_return_buffer       (DBusAuth               *auth,
+                                              DBusString             *buffer,
+                                              int                     bytes_read);
+void          _dbus_auth_get_unused_bytes    (DBusAuth               *auth,
+                                              const DBusString      **str);
+void          _dbus_auth_delete_unused_bytes (DBusAuth               *auth);
+dbus_bool_t   _dbus_auth_needs_encoding      (DBusAuth               *auth);
+dbus_bool_t   _dbus_auth_encode_data         (DBusAuth               *auth,
+                                              const DBusString       *plaintext,
+                                              DBusString             *encoded);
+dbus_bool_t   _dbus_auth_needs_decoding      (DBusAuth               *auth);
+dbus_bool_t   _dbus_auth_decode_data         (DBusAuth               *auth,
+                                              const DBusString       *encoded,
+                                              DBusString             *plaintext);
+dbus_bool_t   _dbus_auth_set_credentials     (DBusAuth               *auth,
+                                              DBusCredentials        *credentials);
+DBusCredentials* _dbus_auth_get_identity     (DBusAuth               *auth);
+dbus_bool_t   _dbus_auth_set_context         (DBusAuth               *auth,
+                                              const DBusString       *context);
+const char*   _dbus_auth_get_guid_from_server(DBusAuth               *auth);
+
+
+DBUS_END_DECLS
+
+#endif /* DBUS_AUTH_H */
diff --git a/src/dbus/dbus-bus.c b/src/dbus/dbus-bus.c
new file mode 100644 (file)
index 0000000..f97cce6
--- /dev/null
@@ -0,0 +1,1537 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-bus.c  Convenience functions for communicating with the bus.
+ *
+ * Copyright (C) 2003  CodeFactory AB
+ * Copyright (C) 2003  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-bus.h"
+#include "dbus-protocol.h"
+#include "dbus-internals.h"
+#include "dbus-message.h"
+#include "dbus-marshal-validate.h"
+#include "dbus-threads-internal.h"
+#include "dbus-connection-internal.h"
+#include <string.h>
+
+/**
+ * @defgroup DBusBus Message bus APIs
+ * @ingroup DBus
+ * @brief Functions for communicating with the message bus
+ *
+ * dbus_bus_get() allows all modules and libraries in a given
+ * process to share the same connection to the bus daemon by storing
+ * the connection globally.
+ *
+ * All other functions in this module are just convenience functions;
+ * most of them invoke methods on the bus daemon, by sending method
+ * call messages to #DBUS_SERVICE_DBUS. These convenience functions
+ * often make blocking method calls. If you don't want to block,
+ * you can send the method call messages manually in the same way
+ * you would any other method call message.
+ *
+ * This module is the only one in libdbus that's specific to
+ * communicating with the message bus daemon. The rest of the API can
+ * also be used for connecting to another application directly.
+ * 
+ * @todo right now the default address of the system bus is hardcoded,
+ * so if you change it in the global config file suddenly you have to
+ * set DBUS_SYSTEM_BUS_ADDRESS env variable.  Might be nice if the
+ * client lib somehow read the config file, or if the bus on startup
+ * somehow wrote out its address to a well-known spot, but might also
+ * not be worth it.
+ */
+
+/**
+ * @defgroup DBusBusInternals Message bus APIs internals
+ * @ingroup DBusInternals
+ * @brief Internals of functions for communicating with the message bus
+ *
+ * @{
+ */
+
+/**
+ * Block of message-bus-related data we attach to each
+ * #DBusConnection used with these convenience functions.
+ *
+ */
+typedef struct
+{
+  DBusConnection *connection; /**< Connection we're associated with */
+  char *unique_name; /**< Unique name of this connection */
+
+  unsigned int is_well_known : 1; /**< Is one of the well-known connections in our global array */
+} BusData;
+
+/** The slot we have reserved to store BusData.
+ */
+static dbus_int32_t bus_data_slot = -1;
+
+/** Number of bus types */
+#define N_BUS_TYPES 3
+
+static DBusConnection *bus_connections[N_BUS_TYPES];
+static char *bus_connection_addresses[N_BUS_TYPES] = { NULL, NULL, NULL };
+
+static DBusBusType activation_bus_type = DBUS_BUS_STARTER;
+
+static dbus_bool_t initialized = FALSE;
+
+/**
+ * Lock for globals in this file
+ */
+_DBUS_DEFINE_GLOBAL_LOCK (bus);
+
+/**
+ * Global lock covering all BusData on any connection. The bet is
+ * that some lock contention is better than more memory
+ * for a per-connection lock, but it's tough to imagine it mattering
+ * either way.
+ */
+_DBUS_DEFINE_GLOBAL_LOCK (bus_datas);
+
+static void
+addresses_shutdown_func (void *data)
+{
+  int i;
+
+  i = 0;
+  while (i < N_BUS_TYPES)
+    {
+      if (bus_connections[i] != NULL)
+        _dbus_warn_check_failed ("dbus_shutdown() called but connections were still live. This probably means the application did not drop all its references to bus connections.\n");
+      
+      dbus_free (bus_connection_addresses[i]);
+      bus_connection_addresses[i] = NULL;
+      ++i;
+    }
+
+  activation_bus_type = DBUS_BUS_STARTER;
+
+  initialized = FALSE;
+}
+
+static dbus_bool_t
+get_from_env (char           **connection_p,
+              const char      *env_var)
+{
+  const char *s;
+  
+  _dbus_assert (*connection_p == NULL);
+  
+  s = _dbus_getenv (env_var);
+  if (s == NULL || *s == '\0')
+    return TRUE; /* successfully didn't use the env var */
+  else
+    {
+      *connection_p = _dbus_strdup (s);
+      return *connection_p != NULL;
+    }
+}
+
+static dbus_bool_t
+init_connections_unlocked (void)
+{
+  if (!initialized)
+    {
+      const char *s;
+      int i;
+
+      i = 0;
+      while (i < N_BUS_TYPES)
+        {
+          bus_connections[i] = NULL;
+          ++i;
+        }
+
+      /* Don't init these twice, we may run this code twice if
+       * init_connections_unlocked() fails midway through.
+       * In practice, each block below should contain only one
+       * "return FALSE" or running through twice may not
+       * work right.
+       */
+      
+       if (bus_connection_addresses[DBUS_BUS_SYSTEM] == NULL)
+         {
+           _dbus_verbose ("Filling in system bus address...\n");
+           
+           if (!get_from_env (&bus_connection_addresses[DBUS_BUS_SYSTEM],
+                              "DBUS_SYSTEM_BUS_ADDRESS"))
+             return FALSE;
+         }
+
+                  
+       if (bus_connection_addresses[DBUS_BUS_SYSTEM] == NULL)
+         {
+           /* Use default system bus address if none set in environment */
+           bus_connection_addresses[DBUS_BUS_SYSTEM] =
+             _dbus_strdup (DBUS_SYSTEM_BUS_DEFAULT_ADDRESS);
+
+           if (bus_connection_addresses[DBUS_BUS_SYSTEM] == NULL)
+             return FALSE;
+           
+           _dbus_verbose ("  used default system bus \"%s\"\n",
+                          bus_connection_addresses[DBUS_BUS_SYSTEM]);
+         }
+       else
+         _dbus_verbose ("  used env var system bus \"%s\"\n",
+                        bus_connection_addresses[DBUS_BUS_SYSTEM]);
+          
+      if (bus_connection_addresses[DBUS_BUS_SESSION] == NULL)
+        {
+          _dbus_verbose ("Filling in session bus address...\n");
+          
+          if (!get_from_env (&bus_connection_addresses[DBUS_BUS_SESSION],
+                             "DBUS_SESSION_BUS_ADDRESS"))
+            return FALSE;
+
+         if (bus_connection_addresses[DBUS_BUS_SESSION] == NULL)
+           bus_connection_addresses[DBUS_BUS_SESSION] =
+             _dbus_strdup (DBUS_SESSION_BUS_DEFAULT_ADDRESS);
+          
+          if (bus_connection_addresses[DBUS_BUS_SESSION] == NULL)
+             return FALSE;
+
+          _dbus_verbose ("  \"%s\"\n", bus_connection_addresses[DBUS_BUS_SESSION] ?
+                         bus_connection_addresses[DBUS_BUS_SESSION] : "none set");
+        }
+
+      if (bus_connection_addresses[DBUS_BUS_STARTER] == NULL)
+        {
+          _dbus_verbose ("Filling in activation bus address...\n");
+          
+          if (!get_from_env (&bus_connection_addresses[DBUS_BUS_STARTER],
+                             "DBUS_STARTER_ADDRESS"))
+            return FALSE;
+          
+          _dbus_verbose ("  \"%s\"\n", bus_connection_addresses[DBUS_BUS_STARTER] ?
+                         bus_connection_addresses[DBUS_BUS_STARTER] : "none set");
+        }
+
+
+      if (bus_connection_addresses[DBUS_BUS_STARTER] != NULL)
+        {
+          s = _dbus_getenv ("DBUS_STARTER_BUS_TYPE");
+              
+          if (s != NULL)
+            {
+              _dbus_verbose ("Bus activation type was set to \"%s\"\n", s);
+                  
+              if (strcmp (s, "system") == 0)
+                activation_bus_type = DBUS_BUS_SYSTEM;
+              else if (strcmp (s, "session") == 0)
+                activation_bus_type = DBUS_BUS_SESSION;
+            }
+        }
+      else
+        {
+          /* Default to the session bus instead if available */
+          if (bus_connection_addresses[DBUS_BUS_SESSION] != NULL)
+            {
+              bus_connection_addresses[DBUS_BUS_STARTER] =
+                _dbus_strdup (bus_connection_addresses[DBUS_BUS_SESSION]);
+              if (bus_connection_addresses[DBUS_BUS_STARTER] == NULL)
+                return FALSE;
+            }
+        }
+      
+      /* If we return FALSE we have to be sure that restarting
+       * the above code will work right
+       */
+      
+      if (!_dbus_setenv ("DBUS_ACTIVATION_ADDRESS", NULL))
+        return FALSE;
+
+      if (!_dbus_setenv ("DBUS_ACTIVATION_BUS_TYPE", NULL))
+        return FALSE;
+      
+      if (!_dbus_register_shutdown_func (addresses_shutdown_func,
+                                         NULL))
+        return FALSE;
+      
+      initialized = TRUE;
+    }
+
+  return initialized;
+}
+
+static void
+bus_data_free (void *data)
+{
+  BusData *bd = data;
+  
+  if (bd->is_well_known)
+    {
+      int i;
+      _DBUS_LOCK (bus);
+      /* We may be stored in more than one slot */
+      /* This should now be impossible - these slots are supposed to
+       * be cleared on disconnect, so should not need to be cleared on
+       * finalize
+       */
+      i = 0;
+      while (i < N_BUS_TYPES)
+        {
+          if (bus_connections[i] == bd->connection)
+            bus_connections[i] = NULL;
+          
+          ++i;
+        }
+      _DBUS_UNLOCK (bus);
+    }
+  
+  dbus_free (bd->unique_name);
+  dbus_free (bd);
+
+  dbus_connection_free_data_slot (&bus_data_slot);
+}
+
+static BusData*
+ensure_bus_data (DBusConnection *connection)
+{
+  BusData *bd;
+
+  if (!dbus_connection_allocate_data_slot (&bus_data_slot))
+    return NULL;
+
+  bd = dbus_connection_get_data (connection, bus_data_slot);
+  if (bd == NULL)
+    {      
+      bd = dbus_new0 (BusData, 1);
+      if (bd == NULL)
+        {
+          dbus_connection_free_data_slot (&bus_data_slot);
+          return NULL;
+        }
+
+      bd->connection = connection;
+      
+      if (!dbus_connection_set_data (connection, bus_data_slot, bd,
+                                     bus_data_free))
+        {
+          dbus_free (bd);
+          dbus_connection_free_data_slot (&bus_data_slot);
+          return NULL;
+        }
+
+      /* Data slot refcount now held by the BusData */
+    }
+  else
+    {
+      dbus_connection_free_data_slot (&bus_data_slot);
+    }
+
+  return bd;
+}
+
+/**
+ * Internal function that checks to see if this
+ * is a shared connection owned by the bus and if it is unref it.
+ *
+ * @param connection a connection that has been disconnected.
+ */
+void
+_dbus_bus_notify_shared_connection_disconnected_unlocked (DBusConnection *connection)
+{
+  int i;
+  
+  _DBUS_LOCK (bus);
+
+  /* We are expecting to have the connection saved in only one of these
+   * slots, but someone could in a pathological case set system and session
+   * bus to the same bus or something. Or set one of them to the starter
+   * bus without setting the starter bus type in the env variable.
+   * So we don't break the loop as soon as we find a match.
+   */
+  for (i = 0; i < N_BUS_TYPES; ++i)
+    {
+      if (bus_connections[i] == connection)
+        {
+          bus_connections[i] = NULL;
+        }
+    }
+
+  _DBUS_UNLOCK (bus);
+}
+
+static DBusConnection *
+internal_bus_get (DBusBusType  type,
+                  dbus_bool_t  private,
+                  DBusError   *error)
+{
+  const char *address;
+  DBusConnection *connection;
+  BusData *bd;
+  DBusBusType address_type;
+
+  _dbus_return_val_if_fail (type >= 0 && type < N_BUS_TYPES, NULL);
+  _dbus_return_val_if_error_is_set (error, NULL);
+
+  _DBUS_LOCK (bus);
+
+  if (!init_connections_unlocked ())
+    {
+      _DBUS_UNLOCK (bus);
+      _DBUS_SET_OOM (error);
+      return NULL;
+    }
+
+  /* We want to use the activation address even if the
+   * activating bus is the session or system bus,
+   * per the spec.
+   */
+  address_type = type;
+  
+  /* Use the real type of the activation bus for getting its
+   * connection, but only if the real type's address is available. (If
+   * the activating bus isn't a well-known bus then
+   * activation_bus_type == DBUS_BUS_STARTER)
+   */
+  if (type == DBUS_BUS_STARTER &&
+      bus_connection_addresses[activation_bus_type] != NULL)
+    type = activation_bus_type;
+  
+  if (!private && bus_connections[type] != NULL)
+    {
+      connection = bus_connections[type];
+      dbus_connection_ref (connection);
+      
+      _DBUS_UNLOCK (bus);
+      return connection;
+    }
+
+  address = bus_connection_addresses[address_type];
+  if (address == NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                      "Unable to determine the address of the message bus (try 'man dbus-launch' and 'man dbus-daemon' for help)");
+      _DBUS_UNLOCK (bus);
+      return NULL;
+    }
+
+  if (private)
+    connection = dbus_connection_open_private (address, error);
+  else
+    connection = dbus_connection_open (address, error);
+  
+  if (!connection)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      _DBUS_UNLOCK (bus);
+      return NULL;
+    }
+
+  if (!dbus_bus_register (connection, error))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      _dbus_connection_close_possibly_shared (connection);
+      dbus_connection_unref (connection);
+
+      _DBUS_UNLOCK (bus);
+      return NULL;
+    }
+
+  if (!private)
+    {
+      /* store a weak ref to the connection (dbus-connection.c is
+       * supposed to have a strong ref that it drops on disconnect,
+       * since this is a shared connection)
+       */
+      bus_connections[type] = connection;
+    }
+
+  /* By default we're bound to the lifecycle of
+   * the message bus.
+   */
+  dbus_connection_set_exit_on_disconnect (connection,
+                                          TRUE);
+  _DBUS_LOCK (bus_datas);
+  bd = ensure_bus_data (connection);
+  _dbus_assert (bd != NULL); /* it should have been created on
+                                register, so OOM not possible */
+  bd->is_well_known = TRUE;
+  _DBUS_UNLOCK (bus_datas);
+
+  
+  _DBUS_UNLOCK (bus);
+
+  /* Return a reference to the caller */
+  return connection;
+}
+
+
+/** @} */ /* end of implementation details docs */
+
+/**
+ * @addtogroup DBusBus
+ * @{
+ */
+
+/**
+ * Connects to a bus daemon and registers the client with it.  If a
+ * connection to the bus already exists, then that connection is
+ * returned.  The caller of this function owns a reference to the bus.
+ *
+ * The caller may NOT call dbus_connection_close() on this connection;
+ * see dbus_connection_open() and dbus_connection_close() for details
+ * on that.
+ *
+ * If this function obtains a new connection object never before
+ * returned from dbus_bus_get(), it will call
+ * dbus_connection_set_exit_on_disconnect(), so the application
+ * will exit if the connection closes. You can undo this
+ * by calling dbus_connection_set_exit_on_disconnect() yourself
+ * after you get the connection.
+ *
+ * dbus_bus_get() calls dbus_bus_register() for you.
+ * 
+ * If returning a newly-created connection, this function will block
+ * until authentication and bus registration are complete.
+ * 
+ * @param type bus type
+ * @param error address where an error can be returned.
+ * @returns a #DBusConnection with new ref
+ */
+DBusConnection *
+dbus_bus_get (DBusBusType  type,
+             DBusError   *error)
+{
+  return internal_bus_get (type, FALSE, error);
+}
+
+/**
+ * Connects to a bus daemon and registers the client with it as with
+ * dbus_bus_register().  Unlike dbus_bus_get(), always creates a new
+ * connection. This connection will not be saved or recycled by
+ * libdbus. Caller owns a reference to the bus and must either close
+ * it or know it to be closed prior to releasing this reference.
+ *
+ * See dbus_connection_open_private() for more details on when to
+ * close and unref this connection.
+ *
+ * This function calls
+ * dbus_connection_set_exit_on_disconnect() on the new connection, so the application
+ * will exit if the connection closes. You can undo this
+ * by calling dbus_connection_set_exit_on_disconnect() yourself
+ * after you get the connection.
+ *
+ * dbus_bus_get_private() calls dbus_bus_register() for you.
+ *
+ * This function will block until authentication and bus registration
+ * are complete.
+ *
+ * @param type bus type
+ * @param error address where an error can be returned.
+ * @returns a DBusConnection with new ref
+ */
+DBusConnection *
+dbus_bus_get_private (DBusBusType  type,
+                      DBusError   *error)
+{
+  return internal_bus_get (type, TRUE, error);
+}
+
+/**
+ * Registers a connection with the bus. This must be the first
+ * thing an application does when connecting to the message bus.
+ * If registration succeeds, the unique name will be set,
+ * and can be obtained using dbus_bus_get_unique_name().
+ *
+ * This function will block until registration is complete.
+ *
+ * If the connection has already registered with the bus
+ * (determined by checking whether dbus_bus_get_unique_name()
+ * returns a non-#NULL value), then this function does nothing.
+ *
+ * If you use dbus_bus_get() or dbus_bus_get_private() this
+ * function will be called for you.
+ * 
+ * @note Just use dbus_bus_get() or dbus_bus_get_private() instead of
+ * dbus_bus_register() and save yourself some pain. Using
+ * dbus_bus_register() manually is only useful if you have your
+ * own custom message bus not found in #DBusBusType.
+ *
+ * If you open a bus connection with dbus_connection_open() or
+ * dbus_connection_open_private() you will have to dbus_bus_register()
+ * yourself, or make the appropriate registration method calls
+ * yourself. If you send the method calls yourself, call
+ * dbus_bus_set_unique_name() with the unique bus name you get from
+ * the bus.
+ *
+ * For shared connections (created with dbus_connection_open()) in a
+ * multithreaded application, you can't really make the registration
+ * calls yourself, because you don't know whether some other thread is
+ * also registering, and the bus will kick you off if you send two
+ * registration messages.
+ *
+ * If you use dbus_bus_register() however, there is a lock that
+ * keeps both apps from registering at the same time.
+ *
+ * The rule in a multithreaded app, then, is that dbus_bus_register()
+ * must be used to register, or you need to have your own locks that
+ * all threads in the app will respect.
+ *
+ * In a single-threaded application you can register by hand instead
+ * of using dbus_bus_register(), as long as you check
+ * dbus_bus_get_unique_name() to see if a unique name has already been
+ * stored by another thread before you send the registration messages.
+ * 
+ * @param connection the connection
+ * @param error place to store errors
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+dbus_bus_register (DBusConnection *connection,
+                   DBusError      *error)
+{
+  DBusMessage *message, *reply;
+  char *name;
+  BusData *bd;
+  dbus_bool_t retval;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_error_is_set (error, FALSE);
+
+  retval = FALSE;
+
+  _DBUS_LOCK (bus_datas);
+
+  bd = ensure_bus_data (connection);
+  if (bd == NULL)
+    {
+      _DBUS_SET_OOM (error);
+      _DBUS_UNLOCK (bus_datas);
+      return FALSE;
+    }
+
+  if (bd->unique_name != NULL)
+    {
+      _dbus_verbose ("Ignoring attempt to register the same DBusConnection %s with the message bus a second time.\n",
+                     bd->unique_name);
+      _DBUS_UNLOCK (bus_datas);
+
+      /* Success! */
+      return TRUE;
+    }
+  
+  message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+                                          DBUS_PATH_DBUS,
+                                          DBUS_INTERFACE_DBUS,
+                                          "Hello"); 
+
+  if (!message)
+    {
+      _DBUS_SET_OOM (error);
+
+      _DBUS_UNLOCK (bus_datas);
+      return FALSE;
+    }
+  
+  reply = dbus_connection_send_with_reply_and_block (connection, message, -1, error);
+
+  dbus_message_unref (message);
+  
+  if (reply == NULL)
+    goto out;
+  else if (dbus_set_error_from_message (error, reply))
+    goto out;
+  else if (!dbus_message_get_args (reply, error,
+                                   DBUS_TYPE_STRING, &name,
+                                   DBUS_TYPE_INVALID))
+    goto out;
+  
+  bd->unique_name = _dbus_strdup (name);
+  if (bd->unique_name == NULL)
+    {
+      _DBUS_SET_OOM (error);
+      goto out;
+    }
+  
+  retval = TRUE;
+  
+ out:
+  if (reply)
+    dbus_message_unref (reply);
+
+  if (!retval)
+    _DBUS_ASSERT_ERROR_IS_SET (error);
+
+  _DBUS_UNLOCK (bus_datas);
+  
+  return retval;
+}
+
+
+/**
+ * Sets the unique name of the connection, as assigned by the message
+ * bus.  Can only be used if you registered with the bus manually
+ * (i.e. if you did not call dbus_bus_register()). Can only be called
+ * once per connection.  After the unique name is set, you can get it
+ * with dbus_bus_get_unique_name().
+ *
+ * The only reason to use this function is to re-implement the
+ * equivalent of dbus_bus_register() yourself. One (probably unusual)
+ * reason to do that might be to do the bus registration call
+ * asynchronously instead of synchronously.
+ *
+ * @note Just use dbus_bus_get() or dbus_bus_get_private(), or worst
+ * case dbus_bus_register(), instead of messing with this
+ * function. There's really no point creating pain for yourself by
+ * doing things manually.
+ *
+ * It's hard to use this function safely on shared connections
+ * (created by dbus_connection_open()) in a multithreaded application,
+ * because only one registration attempt can be sent to the bus. If
+ * two threads are both sending the registration message, there is no
+ * mechanism in libdbus itself to avoid sending it twice.
+ *
+ * Thus, you need a way to coordinate which thread sends the
+ * registration attempt; which also means you know which thread
+ * will call dbus_bus_set_unique_name(). If you don't know
+ * about all threads in the app (for example, if some libraries
+ * you're using might start libdbus-using threads), then you
+ * need to avoid using this function on shared connections.
+ *
+ * @param connection the connection
+ * @param unique_name the unique name
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_bus_set_unique_name (DBusConnection *connection,
+                          const char     *unique_name)
+{
+  BusData *bd;
+  dbus_bool_t success;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (unique_name != NULL, FALSE);
+
+  _DBUS_LOCK (bus_datas);
+  
+  bd = ensure_bus_data (connection);
+  if (bd == NULL)
+    return FALSE;
+
+  _dbus_assert (bd->unique_name == NULL);
+  
+  bd->unique_name = _dbus_strdup (unique_name);
+  success = bd->unique_name != NULL;
+  
+  _DBUS_UNLOCK (bus_datas);
+  
+  return success;
+}
+
+/**
+ * Gets the unique name of the connection as assigned by the message
+ * bus. Only possible after the connection has been registered with
+ * the message bus. All connections returned by dbus_bus_get() or
+ * dbus_bus_get_private() have been successfully registered.
+ *
+ * The name remains valid until the connection is freed, and
+ * should not be freed by the caller.
+ *
+ * Other than dbus_bus_get(), there are two ways to set the unique
+ * name; one is dbus_bus_register(), the other is
+ * dbus_bus_set_unique_name().  You are responsible for calling
+ * dbus_bus_set_unique_name() if you register by hand instead of using
+ * dbus_bus_register().
+ * 
+ * @param connection the connection
+ * @returns the unique name or #NULL on error
+ */
+const char*
+dbus_bus_get_unique_name (DBusConnection *connection)
+{
+  BusData *bd;
+  const char *unique_name;
+
+  _dbus_return_val_if_fail (connection != NULL, NULL);
+
+  _DBUS_LOCK (bus_datas);
+  
+  bd = ensure_bus_data (connection);
+  if (bd == NULL)
+    return NULL;
+
+  unique_name = bd->unique_name;
+
+  _DBUS_UNLOCK (bus_datas);
+  
+  return unique_name;
+}
+
+/**
+ * Asks the bus to return the UID the named connection authenticated
+ * as, if any.  Only works on UNIX; only works for connections on the
+ * same machine as the bus. If you are not on the same machine as the
+ * bus, then calling this is probably a bad idea, since the UID will
+ * mean little to your application.
+ *
+ * For the system message bus you're guaranteed to be on the same
+ * machine since it only listens on a UNIX domain socket (at least,
+ * as shipped by default).
+ *
+ * This function only works for connections that authenticated as
+ * a UNIX user, right now that includes all bus connections, but
+ * it's very possible to have connections with no associated UID.
+ * So check for errors and do something sensible if they happen.
+ * 
+ * This function will always return an error on Windows.
+ * 
+ * @param connection the connection
+ * @param name a name owned by the connection
+ * @param error location to store the error
+ * @returns the unix user id, or ((unsigned)-1) if error is set
+ */ 
+unsigned long
+dbus_bus_get_unix_user (DBusConnection *connection,
+                        const char     *name,
+                        DBusError      *error)
+{
+  DBusMessage *message, *reply;
+  dbus_uint32_t uid;
+
+  _dbus_return_val_if_fail (connection != NULL, DBUS_UID_UNSET);
+  _dbus_return_val_if_fail (name != NULL, DBUS_UID_UNSET);
+  _dbus_return_val_if_fail (_dbus_check_is_valid_bus_name (name), DBUS_UID_UNSET);
+  _dbus_return_val_if_error_is_set (error, DBUS_UID_UNSET);
+  
+  message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+                                          DBUS_PATH_DBUS,
+                                          DBUS_INTERFACE_DBUS,
+                                          "GetConnectionUnixUser");
+
+  if (message == NULL)
+    {
+      _DBUS_SET_OOM (error);
+      return DBUS_UID_UNSET;
+    }
+  if (!dbus_message_append_args (message,
+                                DBUS_TYPE_STRING, &name,
+                                DBUS_TYPE_INVALID))
+    {
+      dbus_message_unref (message);
+      _DBUS_SET_OOM (error);
+      return DBUS_UID_UNSET;
+    }
+  
+  reply = dbus_connection_send_with_reply_and_block (connection, message, -1,
+                                                     error);
+  
+  dbus_message_unref (message);
+  
+  if (reply == NULL)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      return DBUS_UID_UNSET;
+    }  
+
+  if (dbus_set_error_from_message (error, reply))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      dbus_message_unref (reply);
+      return DBUS_UID_UNSET;
+    }
+  
+  if (!dbus_message_get_args (reply, error,
+                              DBUS_TYPE_UINT32, &uid,
+                              DBUS_TYPE_INVALID))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      dbus_message_unref (reply);
+      return DBUS_UID_UNSET;
+    }
+
+  dbus_message_unref (reply);
+  
+  return (unsigned long) uid;
+}
+
+/**
+ * Asks the bus to return its globally unique ID, as described in the
+ * D-Bus specification. For the session bus, this is useful as a way
+ * to uniquely identify each user session. For the system bus,
+ * probably the bus ID is not useful; instead, use the machine ID
+ * since it's accessible without necessarily connecting to the bus and
+ * may be persistent beyond a single bus instance (across reboots for
+ * example). See dbus_get_local_machine_id().
+ *
+ * In addition to an ID for each bus and an ID for each machine, there is
+ * an ID for each address that the bus is listening on; that can
+ * be retrieved with dbus_connection_get_server_id(), though it is
+ * probably not very useful.
+ * 
+ * @param connection the connection
+ * @param error location to store the error
+ * @returns the bus ID or #NULL if error is set
+ */ 
+char*
+dbus_bus_get_id (DBusConnection *connection,
+                 DBusError      *error)
+{
+  DBusMessage *message, *reply;
+  char *id;
+  const char *v_STRING;
+
+  _dbus_return_val_if_fail (connection != NULL, NULL);
+  _dbus_return_val_if_error_is_set (error, NULL);
+  
+  message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+                                          DBUS_PATH_DBUS,
+                                          DBUS_INTERFACE_DBUS,
+                                          "GetId");
+  
+  if (message == NULL)
+    {
+      _DBUS_SET_OOM (error);
+      return NULL;
+    }
+  
+  reply = dbus_connection_send_with_reply_and_block (connection, message, -1,
+                                                     error);
+  
+  dbus_message_unref (message);
+  
+  if (reply == NULL)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      return NULL;
+    }  
+
+  if (dbus_set_error_from_message (error, reply))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      dbus_message_unref (reply);
+      return NULL;
+    }
+
+  v_STRING = NULL;
+  if (!dbus_message_get_args (reply, error,
+                              DBUS_TYPE_STRING, &v_STRING,
+                              DBUS_TYPE_INVALID))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      dbus_message_unref (reply);
+      return NULL;
+    }
+
+  id = _dbus_strdup (v_STRING); /* may be NULL */
+  
+  dbus_message_unref (reply);
+
+  if (id == NULL)
+    _DBUS_SET_OOM (error);
+
+  /* FIXME it might be nice to cache the ID locally */
+  
+  return id;
+}
+
+/**
+ * Asks the bus to assign the given name to this connection by invoking
+ * the RequestName method on the bus. This method is fully documented
+ * in the D-Bus specification. For quick reference, the flags and
+ * result codes are discussed here, but the specification is the
+ * canonical version of this information.
+ *
+ * First you should know that for each bus name, the bus stores
+ * a queue of connections that would like to own it. Only
+ * one owns it at a time - called the primary owner. If the primary
+ * owner releases the name or disconnects, then the next owner in the
+ * queue atomically takes over.
+ *
+ * So for example if you have an application org.freedesktop.TextEditor
+ * and multiple instances of it can be run, you can have all of them
+ * sitting in the queue. The first one to start up will receive messages
+ * sent to org.freedesktop.TextEditor, but if that one exits another
+ * will become the primary owner and receive messages.
+ *
+ * The queue means you don't need to manually watch for the current owner to
+ * disappear and then request the name again.
+ *
+ * When requesting a name, you can specify several flags.
+ * 
+ * #DBUS_NAME_FLAG_ALLOW_REPLACEMENT and #DBUS_NAME_FLAG_DO_NOT_QUEUE
+ * are properties stored by the bus for this connection with respect to
+ * each requested bus name. These properties are stored even if the
+ * connection is queued and does not become the primary owner.
+ * You can update these flags by calling RequestName again (even if
+ * you already own the name).
+ *
+ * #DBUS_NAME_FLAG_ALLOW_REPLACEMENT means that another requestor of the
+ * name can take it away from you by specifying #DBUS_NAME_FLAG_REPLACE_EXISTING.
+ *
+ * #DBUS_NAME_FLAG_DO_NOT_QUEUE means that if you aren't the primary owner,
+ * you don't want to be queued up - you only care about being the
+ * primary owner.
+ *
+ * Unlike the other two flags, #DBUS_NAME_FLAG_REPLACE_EXISTING is a property
+ * of the individual RequestName call, i.e. the bus does not persistently
+ * associate it with the connection-name pair. If a RequestName call includes
+ * the #DBUS_NAME_FLAG_REPLACE_EXISTING flag, and the current primary
+ * owner has #DBUS_NAME_FLAG_ALLOW_REPLACEMENT set, then the current primary
+ * owner will be kicked off.
+ *
+ * If no flags are given, an application will receive the requested
+ * name only if the name is currently unowned; and it will NOT give
+ * up the name if another application asks to take it over using
+ * #DBUS_NAME_FLAG_REPLACE_EXISTING.
+ *
+ * This function returns a result code. The possible result codes
+ * are as follows.
+ * 
+ * #DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER means that the name had no
+ * existing owner, and the caller is now the primary owner; or that
+ * the name had an owner, and the caller specified
+ * #DBUS_NAME_FLAG_REPLACE_EXISTING, and the current owner
+ * specified #DBUS_NAME_FLAG_ALLOW_REPLACEMENT.
+ *
+ * #DBUS_REQUEST_NAME_REPLY_IN_QUEUE happens only if the caller does NOT
+ * specify #DBUS_NAME_FLAG_DO_NOT_QUEUE and either the current owner
+ * did NOT specify #DBUS_NAME_FLAG_ALLOW_REPLACEMENT or the caller did NOT
+ * specify #DBUS_NAME_FLAG_REPLACE_EXISTING. In this case the caller ends up 
+ * in a queue to own the name after the current owner gives it up.
+ *
+ * #DBUS_REQUEST_NAME_REPLY_EXISTS happens if the name has an owner
+ * already and the caller specifies #DBUS_NAME_FLAG_DO_NOT_QUEUE
+ * and either the current owner has NOT specified 
+ * #DBUS_NAME_FLAG_ALLOW_REPLACEMENT or the caller did NOT specify 
+ * #DBUS_NAME_FLAG_REPLACE_EXISTING.
+ *
+ * #DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER happens if an application
+ * requests a name it already owns. (Re-requesting a name is useful if
+ * you want to change the #DBUS_NAME_FLAG_ALLOW_REPLACEMENT or
+ * #DBUS_NAME_FLAG_DO_NOT_QUEUE settings.)
+ *
+ * When a service represents an application, say "text editor," then
+ * it should specify #DBUS_NAME_FLAG_ALLOW_REPLACEMENT if it wants
+ * the last editor started to be the user's editor vs. the first one
+ * started.  Then any editor that can be the user's editor should
+ * specify #DBUS_NAME_FLAG_REPLACE_EXISTING to either take over
+ * (last-started-wins) or be queued up (first-started-wins) according
+ * to whether #DBUS_NAME_FLAG_ALLOW_REPLACEMENT was given.
+ *
+ * Conventionally, single-instance applications often offer a command
+ * line option called --replace which means to replace the current
+ * instance.  To implement this, always set
+ * #DBUS_NAME_FLAG_ALLOW_REPLACEMENT when you request your
+ * application's bus name.  When you lose ownership of your bus name,
+ * you need to exit.  Look for the signal "NameLost" from
+ * #DBUS_SERVICE_DBUS and #DBUS_INTERFACE_DBUS (the signal's first
+ * argument is the bus name that was lost).  If starting up without
+ * --replace, do not specify #DBUS_NAME_FLAG_REPLACE_EXISTING, and
+ * exit if you fail to become the bus name owner. If --replace is
+ * given, ask to replace the old owner.
+ *
+ * @param connection the connection
+ * @param name the name to request
+ * @param flags flags
+ * @param error location to store the error
+ * @returns a result code, -1 if error is set
+ */ 
+int
+dbus_bus_request_name (DBusConnection *connection,
+                       const char     *name,
+                       unsigned int    flags,
+                       DBusError      *error)
+{
+  DBusMessage *message, *reply;
+  dbus_uint32_t result;
+
+  _dbus_return_val_if_fail (connection != NULL, 0);
+  _dbus_return_val_if_fail (name != NULL, 0);
+  _dbus_return_val_if_fail (_dbus_check_is_valid_bus_name (name), 0);
+  _dbus_return_val_if_error_is_set (error, 0);
+  
+  message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+                                          DBUS_PATH_DBUS,
+                                          DBUS_INTERFACE_DBUS,
+                                          "RequestName");
+
+  if (message == NULL)
+    {
+      _DBUS_SET_OOM (error);
+      return -1;
+    }
+  if (!dbus_message_append_args (message,
+                                DBUS_TYPE_STRING, &name,
+                                DBUS_TYPE_UINT32, &flags,
+                                DBUS_TYPE_INVALID))
+    {
+      dbus_message_unref (message);
+      _DBUS_SET_OOM (error);
+      return -1;
+    }
+  
+  reply = dbus_connection_send_with_reply_and_block (connection, message, -1,
+                                                     error);
+  
+  dbus_message_unref (message);
+  
+  if (reply == NULL)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      return -1;
+    }  
+
+  if (dbus_set_error_from_message (error, reply))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      dbus_message_unref (reply);
+      return -1;
+    }
+  
+  if (!dbus_message_get_args (reply, error,
+                              DBUS_TYPE_UINT32, &result,
+                              DBUS_TYPE_INVALID))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      dbus_message_unref (reply);
+      return -1;
+    }
+
+  dbus_message_unref (reply);
+  
+  return result;
+}
+
+
+/**
+ * Asks the bus to unassign the given name from this connection by
+ * invoking the ReleaseName method on the bus. The "ReleaseName"
+ * method is canonically documented in the D-Bus specification.
+ *
+ * Possible results are: #DBUS_RELEASE_NAME_REPLY_RELEASED
+ * which means you owned the name or were in the queue to own it,
+ * and and now you don't own it and aren't in the queue.
+ * #DBUS_RELEASE_NAME_REPLY_NOT_OWNER which means someone else
+ * owns the name so you can't release it.
+ * #DBUS_RELEASE_NAME_REPLY_NON_EXISTENT
+ * which means nobody owned the name.
+ * 
+ * @param connection the connection
+ * @param name the name to remove 
+ * @param error location to store the error
+ * @returns a result code, -1 if error is set
+ */ 
+int
+dbus_bus_release_name (DBusConnection *connection,
+                       const char     *name,
+                       DBusError      *error)
+{
+  DBusMessage *message, *reply;
+  dbus_uint32_t result;
+
+  _dbus_return_val_if_fail (connection != NULL, 0);
+  _dbus_return_val_if_fail (name != NULL, 0);
+  _dbus_return_val_if_fail (_dbus_check_is_valid_bus_name (name), 0);
+  _dbus_return_val_if_error_is_set (error, 0);
+
+  message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+                                          DBUS_PATH_DBUS,
+                                          DBUS_INTERFACE_DBUS,
+                                          "ReleaseName");
+
+  if (message == NULL)
+    {
+      _DBUS_SET_OOM (error);
+      return -1;
+    }
+
+  if (!dbus_message_append_args (message,
+                                 DBUS_TYPE_STRING, &name,
+                                 DBUS_TYPE_INVALID))
+    {
+      dbus_message_unref (message);
+      _DBUS_SET_OOM (error);
+      return -1;
+    }
+
+  reply = dbus_connection_send_with_reply_and_block (connection, message, -1,
+                                                     error);
+
+  dbus_message_unref (message);
+
+  if (reply == NULL)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      return -1;
+    }
+
+  if (dbus_set_error_from_message (error, reply))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      dbus_message_unref (reply);
+      return -1;
+    }
+
+  if (!dbus_message_get_args (reply, error,
+                              DBUS_TYPE_UINT32, &result,
+                              DBUS_TYPE_INVALID))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      dbus_message_unref (reply);
+      return -1;
+    }
+
+  dbus_message_unref (reply);
+
+  return result;
+}
+
+/**
+ * Asks the bus whether a certain name has an owner.
+ *
+ * Using this can easily result in a race condition,
+ * since an owner can appear or disappear after you
+ * call this.
+ *
+ * If you want to request a name, just request it;
+ * if you want to avoid replacing a current owner,
+ * don't specify #DBUS_NAME_FLAG_REPLACE_EXISTING and
+ * you will get an error if there's already an owner.
+ * 
+ * @param connection the connection
+ * @param name the name
+ * @param error location to store any errors
+ * @returns #TRUE if the name exists, #FALSE if not or on error
+ */
+dbus_bool_t
+dbus_bus_name_has_owner (DBusConnection *connection,
+                        const char     *name,
+                         DBusError      *error)
+{
+  DBusMessage *message, *reply;
+  dbus_bool_t exists;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (name != NULL, FALSE);
+  _dbus_return_val_if_fail (_dbus_check_is_valid_bus_name (name), FALSE);
+  _dbus_return_val_if_error_is_set (error, FALSE);
+  
+  message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+                                          DBUS_PATH_DBUS,
+                                          DBUS_INTERFACE_DBUS,
+                                          "NameHasOwner");
+  if (message == NULL)
+    {
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+  
+  if (!dbus_message_append_args (message,
+                                DBUS_TYPE_STRING, &name,
+                                DBUS_TYPE_INVALID))
+    {
+      dbus_message_unref (message);
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+  
+  reply = dbus_connection_send_with_reply_and_block (connection, message, -1, error);
+  dbus_message_unref (message);
+
+  if (reply == NULL)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      return FALSE;
+    }
+
+  if (!dbus_message_get_args (reply, error,
+                              DBUS_TYPE_BOOLEAN, &exists,
+                              DBUS_TYPE_INVALID))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      dbus_message_unref (reply);
+      return FALSE;
+    }
+  
+  dbus_message_unref (reply);
+  return exists;
+}
+
+/**
+ * Starts a service that will request ownership of the given name.
+ * The returned result will be one of be one of
+ * #DBUS_START_REPLY_SUCCESS or #DBUS_START_REPLY_ALREADY_RUNNING if
+ * successful.  Pass #NULL if you don't care about the result.
+ * 
+ * The flags parameter is for future expansion, currently you should
+ * specify 0.
+ *
+ * It's often easier to avoid explicitly starting services, and
+ * just send a method call to the service's bus name instead.
+ * Method calls start a service to handle them by default
+ * unless you call dbus_message_set_auto_start() to disable this
+ * behavior.
+ * 
+ * @param connection the connection
+ * @param name the name we want the new service to request
+ * @param flags the flags (should always be 0 for now)
+ * @param result a place to store the result or #NULL
+ * @param error location to store any errors
+ * @returns #TRUE if the activation succeeded, #FALSE if not
+ */
+dbus_bool_t
+dbus_bus_start_service_by_name (DBusConnection *connection,
+                                const char     *name,
+                                dbus_uint32_t   flags,
+                                dbus_uint32_t  *result,
+                                DBusError      *error)
+{
+  DBusMessage *msg;
+  DBusMessage *reply;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (_dbus_check_is_valid_bus_name (name), FALSE);
+  
+  msg = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+                                      DBUS_PATH_DBUS,
+                                      DBUS_INTERFACE_DBUS,
+                                      "StartServiceByName");
+
+  if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, &name,
+                                DBUS_TYPE_UINT32, &flags, DBUS_TYPE_INVALID))
+    {
+      dbus_message_unref (msg);
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+
+  reply = dbus_connection_send_with_reply_and_block (connection, msg,
+                                                     -1, error);
+  dbus_message_unref (msg);
+
+  if (reply == NULL)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      return FALSE;
+    }
+
+  if (dbus_set_error_from_message (error, reply))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      dbus_message_unref (reply);
+      return FALSE;
+    }
+
+  if (result != NULL &&
+      !dbus_message_get_args (reply, error, DBUS_TYPE_UINT32,
+                             result, DBUS_TYPE_INVALID))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      dbus_message_unref (reply);
+      return FALSE;
+    }
+  
+  dbus_message_unref (reply);
+  return TRUE;
+}
+
+static void
+send_no_return_values (DBusConnection *connection,
+                       DBusMessage    *msg,
+                       DBusError      *error)
+{
+  if (error)
+    {
+      /* Block to check success codepath */
+      DBusMessage *reply;
+      
+      reply = dbus_connection_send_with_reply_and_block (connection, msg,
+                                                         -1, error);
+      
+      if (reply == NULL)
+        _DBUS_ASSERT_ERROR_IS_SET (error);
+      else
+        dbus_message_unref (reply);
+    }
+  else
+    {
+      /* Silently-fail nonblocking codepath */
+      dbus_message_set_no_reply (msg, TRUE);
+      dbus_connection_send (connection, msg, NULL);
+    }
+}
+
+/**
+ * Adds a match rule to match messages going through the message bus.
+ * The "rule" argument is the string form of a match rule.
+ *
+ * If you pass #NULL for the error, this function will not
+ * block; the match thus won't be added until you flush the
+ * connection, and if there's an error adding the match
+ * (only possible error is lack of resources in the bus),
+ * you won't find out about it.
+ *
+ * If you pass non-#NULL for the error this function will
+ * block until it gets a reply.
+ *
+ * Normal API conventions would have the function return
+ * a boolean value indicating whether the error was set,
+ * but that would require blocking always to determine
+ * the return value.
+ *
+ * The AddMatch method is fully documented in the D-Bus 
+ * specification. For quick reference, the format of the 
+ * match rules is discussed here, but the specification 
+ * is the canonical version of this information.
+ *
+ * Rules are specified as a string of comma separated 
+ * key/value pairs. An example is 
+ * "type='signal',sender='org.freedesktop.DBus',
+ * interface='org.freedesktop.DBus',member='Foo',
+ * path='/bar/foo',destination=':452345.34'"
+ *
+ * Possible keys you can match on are type, sender, 
+ * interface, member, path, destination and numbered
+ * keys to match message args (keys are 'arg0', 'arg1', etc.).
+ * Omitting a key from the rule indicates 
+ * a wildcard match.  For instance omitting
+ * the member from a match rule but adding a sender would
+ * let all messages from that sender through regardless of
+ * the member.
+ *
+ * Matches are inclusive not exclusive so as long as one 
+ * rule matches the message will get through.  It is important
+ * to note this because every time a message is received the 
+ * application will be paged into memory to process it.  This
+ * can cause performance problems such as draining batteries
+ * on embedded platforms.
+ *
+ * If you match message args ('arg0', 'arg1', and so forth)
+ * only string arguments will match. That is, arg0='5' means
+ * match the string "5" not the integer 5.
+ *
+ * Currently there is no way to match against non-string arguments.
+ *
+ * A specialised form of wildcard matching on arguments is
+ * supported for path-like namespaces.  If your argument match has
+ * a 'path' suffix (eg: "arg0path='/some/path/'") then it is
+ * considered a match if the argument exactly matches the given
+ * string or if one of them ends in a '/' and is a prefix of the
+ * other.
+ *
+ * Matching on interface is tricky because method call
+ * messages only optionally specify the interface.
+ * If a message omits the interface, then it will NOT match
+ * if the rule specifies an interface name. This means match
+ * rules on method calls should not usually give an interface.
+ *
+ * However, signal messages are required to include the interface
+ * so when matching signals usually you should specify the interface
+ * in the match rule.
+ * 
+ * For security reasons, you can match arguments only up to
+ * #DBUS_MAXIMUM_MATCH_RULE_ARG_NUMBER.
+ *
+ * Match rules have a maximum length of #DBUS_MAXIMUM_MATCH_RULE_LENGTH
+ * bytes.
+ *
+ * Both of these maximums are much higher than you're likely to need,
+ * they only exist because the D-Bus bus daemon has fixed limits on
+ * all resource usage.
+ *
+ * @param connection connection to the message bus
+ * @param rule textual form of match rule
+ * @param error location to store any errors
+ */
+void
+dbus_bus_add_match (DBusConnection *connection,
+                    const char     *rule,
+                    DBusError      *error)
+{
+  DBusMessage *msg;
+
+  _dbus_return_if_fail (rule != NULL);
+
+  msg = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+                                      DBUS_PATH_DBUS,
+                                      DBUS_INTERFACE_DBUS,
+                                      "AddMatch");
+
+  if (msg == NULL)
+    {
+      _DBUS_SET_OOM (error);
+      return;
+    }
+
+  if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, &rule,
+                                 DBUS_TYPE_INVALID))
+    {
+      dbus_message_unref (msg);
+      _DBUS_SET_OOM (error);
+      return;
+    }
+
+  send_no_return_values (connection, msg, error);
+
+  dbus_message_unref (msg);
+}
+
+/**
+ * Removes a previously-added match rule "by value" (the most
+ * recently-added identical rule gets removed).  The "rule" argument
+ * is the string form of a match rule.
+ *
+ * The bus compares match rules semantically, not textually, so
+ * whitespace and ordering don't have to be identical to
+ * the rule you passed to dbus_bus_add_match().
+ * 
+ * If you pass #NULL for the error, this function will not
+ * block; otherwise it will. See detailed explanation in
+ * docs for dbus_bus_add_match().
+ * 
+ * @param connection connection to the message bus
+ * @param rule textual form of match rule
+ * @param error location to store any errors
+ */
+void
+dbus_bus_remove_match (DBusConnection *connection,
+                       const char     *rule,
+                       DBusError      *error)
+{
+  DBusMessage *msg;
+
+  _dbus_return_if_fail (rule != NULL);
+  
+  msg = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+                                      DBUS_PATH_DBUS,
+                                      DBUS_INTERFACE_DBUS,
+                                      "RemoveMatch");
+
+  if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, &rule,
+                                 DBUS_TYPE_INVALID))
+    {
+      dbus_message_unref (msg);
+      _DBUS_SET_OOM (error);
+      return;
+    }
+
+  send_no_return_values (connection, msg, error);
+
+  dbus_message_unref (msg);
+}
+
+/** @} */
diff --git a/src/dbus/dbus-bus.h b/src/dbus/dbus-bus.h
new file mode 100644 (file)
index 0000000..7d4f133
--- /dev/null
@@ -0,0 +1,82 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-bus.h  Convenience functions for communicating with the bus.
+ *
+ * Copyright (C) 2003  CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_BUS_H
+#define DBUS_BUS_H
+
+#include <dbus/dbus-connection.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusBus
+ * @{
+ */
+
+DBusConnection *dbus_bus_get              (DBusBusType     type,
+                                          DBusError      *error);
+DBusConnection *dbus_bus_get_private      (DBusBusType     type,
+                                          DBusError      *error);
+
+dbus_bool_t     dbus_bus_register         (DBusConnection *connection,
+                                          DBusError      *error);
+dbus_bool_t     dbus_bus_set_unique_name  (DBusConnection *connection,
+                                          const char     *unique_name);
+const char*     dbus_bus_get_unique_name  (DBusConnection *connection);
+unsigned long   dbus_bus_get_unix_user    (DBusConnection *connection,
+                                          const char     *name,
+                                           DBusError      *error);
+char*           dbus_bus_get_id           (DBusConnection *connection,
+                                           DBusError      *error);
+int             dbus_bus_request_name     (DBusConnection *connection,
+                                          const char     *name,
+                                          unsigned int    flags,
+                                          DBusError      *error);
+int             dbus_bus_release_name     (DBusConnection *connection,
+                                          const char     *name,
+                                          DBusError      *error);
+dbus_bool_t     dbus_bus_name_has_owner   (DBusConnection *connection,
+                                          const char     *name,
+                                          DBusError      *error);
+
+dbus_bool_t     dbus_bus_start_service_by_name (DBusConnection *connection,
+                                                const char     *name,
+                                                dbus_uint32_t   flags,
+                                                dbus_uint32_t  *reply,
+                                                DBusError      *error);
+
+void            dbus_bus_add_match        (DBusConnection *connection,
+                                           const char     *rule,
+                                           DBusError      *error);
+void            dbus_bus_remove_match     (DBusConnection *connection,
+                                           const char     *rule,
+                                           DBusError      *error);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_BUS_H */
diff --git a/src/dbus/dbus-connection-internal.h b/src/dbus/dbus-connection-internal.h
new file mode 100644 (file)
index 0000000..df54412
--- /dev/null
@@ -0,0 +1,120 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-connection-internal.h DBusConnection internal interfaces
+ *
+ * Copyright (C) 2002  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_CONNECTION_INTERNAL_H
+#define DBUS_CONNECTION_INTERNAL_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-connection.h>
+#include <dbus/dbus-message.h>
+#include <dbus/dbus-transport.h>
+#include <dbus/dbus-resources.h>
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-timeout.h>
+#include <dbus/dbus-dataslot.h>
+
+DBUS_BEGIN_DECLS
+
+typedef enum
+{
+  DBUS_ITERATION_DO_WRITING = 1 << 0, /**< Write messages out. */
+  DBUS_ITERATION_DO_READING = 1 << 1, /**< Read messages in. */
+  DBUS_ITERATION_BLOCK      = 1 << 2  /**< Block if nothing to do. */
+} DBusIterationFlags;
+
+/** default timeout value when waiting for a message reply, 25 seconds */
+#define _DBUS_DEFAULT_TIMEOUT_VALUE (25 * 1000)
+
+void              _dbus_connection_lock                        (DBusConnection     *connection);
+void              _dbus_connection_unlock                      (DBusConnection     *connection);
+DBusConnection *  _dbus_connection_ref_unlocked                (DBusConnection     *connection);
+void              _dbus_connection_unref_unlocked              (DBusConnection     *connection);
+dbus_bool_t       _dbus_connection_queue_received_message      (DBusConnection     *connection,
+                                                                DBusMessage        *message);
+void              _dbus_connection_queue_received_message_link (DBusConnection     *connection,
+                                                                DBusList           *link);
+dbus_bool_t       _dbus_connection_has_messages_to_send_unlocked (DBusConnection     *connection);
+DBusMessage*      _dbus_connection_get_message_to_send         (DBusConnection     *connection);
+void              _dbus_connection_message_sent                (DBusConnection     *connection,
+                                                                DBusMessage        *message);
+dbus_bool_t       _dbus_connection_add_watch_unlocked          (DBusConnection     *connection,
+                                                                DBusWatch          *watch);
+void              _dbus_connection_remove_watch_unlocked       (DBusConnection     *connection,
+                                                                DBusWatch          *watch);
+void              _dbus_connection_toggle_watch_unlocked       (DBusConnection     *connection,
+                                                                DBusWatch          *watch,
+                                                                dbus_bool_t         enabled);
+dbus_bool_t       _dbus_connection_handle_watch                (DBusWatch          *watch,
+                                                                unsigned int        condition,
+                                                                void               *data);
+dbus_bool_t       _dbus_connection_add_timeout_unlocked        (DBusConnection     *connection,
+                                                                DBusTimeout        *timeout);
+void              _dbus_connection_remove_timeout_unlocked     (DBusConnection     *connection,
+                                                                DBusTimeout        *timeout);
+void              _dbus_connection_toggle_timeout_unlocked     (DBusConnection     *connection,
+                                                                DBusTimeout        *timeout,
+                                                                dbus_bool_t         enabled);
+DBusConnection*   _dbus_connection_new_for_transport           (DBusTransport      *transport);
+void              _dbus_connection_do_iteration_unlocked       (DBusConnection     *connection,
+                                                                unsigned int        flags,
+                                                                int                 timeout_milliseconds);
+void              _dbus_connection_close_possibly_shared       (DBusConnection     *connection);
+void              _dbus_connection_close_if_only_one_ref       (DBusConnection     *connection);
+
+DBusPendingCall*  _dbus_pending_call_new                       (DBusConnection     *connection,
+                                                                int                 timeout_milliseconds,
+                                                                DBusTimeoutHandler  timeout_handler);
+void              _dbus_pending_call_notify                    (DBusPendingCall    *pending);
+void              _dbus_connection_remove_pending_call         (DBusConnection     *connection,
+                                                                DBusPendingCall    *pending);
+void              _dbus_connection_block_pending_call          (DBusPendingCall    *pending);
+void              _dbus_pending_call_complete_and_unlock       (DBusPendingCall    *pending,
+                                                                DBusMessage        *message);
+dbus_bool_t       _dbus_connection_send_and_unlock             (DBusConnection     *connection,
+                                                                DBusMessage        *message,
+                                                                dbus_uint32_t      *client_serial);
+
+void              _dbus_connection_queue_synthesized_message_link (DBusConnection *connection,
+                                                                  DBusList *link);
+void              _dbus_connection_test_get_locks                 (DBusConnection *conn,
+                                                                   DBusMutex **mutex_loc,
+                                                                   DBusMutex **dispatch_mutex_loc,
+                                                                   DBusMutex **io_path_mutex_loc,
+                                                                   DBusCondVar **dispatch_cond_loc,
+                                                                   DBusCondVar **io_path_cond_loc);
+
+/* This _dbus_bus_* stuff doesn't really belong here, but dbus-bus-internal.h seems
+ * silly for one function
+ */
+/**
+ * @addtogroup DBusBusInternals
+ * @{
+ */
+
+void           _dbus_bus_notify_shared_connection_disconnected_unlocked (DBusConnection *connection);
+
+/** @} */
+
+
+DBUS_END_DECLS
+
+#endif /* DBUS_CONNECTION_INTERNAL_H */
diff --git a/src/dbus/dbus-connection.c b/src/dbus/dbus-connection.c
new file mode 100644 (file)
index 0000000..947c0af
--- /dev/null
@@ -0,0 +1,5901 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-connection.c DBusConnection object
+ *
+ * Copyright (C) 2002-2006  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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 <config.h>
+#include "dbus-shared.h"
+#include "dbus-connection.h"
+#include "dbus-list.h"
+#include "dbus-timeout.h"
+#include "dbus-transport.h"
+#include "dbus-watch.h"
+#include "dbus-connection-internal.h"
+#include "dbus-pending-call-internal.h"
+#include "dbus-list.h"
+#include "dbus-hash.h"
+#include "dbus-message-internal.h"
+#include "dbus-threads.h"
+#include "dbus-protocol.h"
+#include "dbus-dataslot.h"
+#include "dbus-string.h"
+#include "dbus-pending-call.h"
+#include "dbus-object-tree.h"
+#include "dbus-threads-internal.h"
+#include "dbus-bus.h"
+
+#ifdef DBUS_DISABLE_CHECKS
+#define TOOK_LOCK_CHECK(connection)
+#define RELEASING_LOCK_CHECK(connection)
+#define HAVE_LOCK_CHECK(connection)
+#else
+#define TOOK_LOCK_CHECK(connection) do {                \
+    _dbus_assert (!(connection)->have_connection_lock); \
+    (connection)->have_connection_lock = TRUE;          \
+  } while (0)
+#define RELEASING_LOCK_CHECK(connection) do {            \
+    _dbus_assert ((connection)->have_connection_lock);   \
+    (connection)->have_connection_lock = FALSE;          \
+  } while (0)
+#define HAVE_LOCK_CHECK(connection)        _dbus_assert ((connection)->have_connection_lock)
+/* A "DO_NOT_HAVE_LOCK_CHECK" is impossible since we need the lock to check the flag */
+#endif
+
+#define TRACE_LOCKS 1
+
+#define CONNECTION_LOCK(connection)   do {                                      \
+    if (TRACE_LOCKS) { _dbus_verbose ("  LOCK: %s\n", _DBUS_FUNCTION_NAME); }   \
+    _dbus_mutex_lock ((connection)->mutex);                                      \
+    TOOK_LOCK_CHECK (connection);                                               \
+  } while (0)
+
+#define CONNECTION_UNLOCK(connection) do {                                              \
+    if (TRACE_LOCKS) { _dbus_verbose ("  UNLOCK: %s\n", _DBUS_FUNCTION_NAME);  }        \
+    RELEASING_LOCK_CHECK (connection);                                                  \
+    _dbus_mutex_unlock ((connection)->mutex);                                            \
+  } while (0)
+
+#define DISPATCH_STATUS_NAME(s)                                            \
+                     ((s) == DBUS_DISPATCH_COMPLETE ? "complete" :         \
+                      (s) == DBUS_DISPATCH_DATA_REMAINS ? "data remains" : \
+                      (s) == DBUS_DISPATCH_NEED_MEMORY ? "need memory" :   \
+                      "???")
+
+/**
+ * @defgroup DBusConnection DBusConnection
+ * @ingroup  DBus
+ * @brief Connection to another application
+ *
+ * A DBusConnection represents a connection to another
+ * application. Messages can be sent and received via this connection.
+ * The other application may be a message bus; for convenience, the
+ * function dbus_bus_get() is provided to automatically open a
+ * connection to the well-known message buses.
+ * 
+ * In brief a DBusConnection is a message queue associated with some
+ * message transport mechanism such as a socket.  The connection
+ * maintains a queue of incoming messages and a queue of outgoing
+ * messages.
+ *
+ * Several functions use the following terms:
+ * <ul>
+ * <li><b>read</b> means to fill the incoming message queue by reading from the socket</li>
+ * <li><b>write</b> means to drain the outgoing queue by writing to the socket</li>
+ * <li><b>dispatch</b> means to drain the incoming queue by invoking application-provided message handlers</li>
+ * </ul>
+ *
+ * The function dbus_connection_read_write_dispatch() for example does all
+ * three of these things, offering a simple alternative to a main loop.
+ *
+ * In an application with a main loop, the read/write/dispatch
+ * operations are usually separate.
+ *
+ * The connection provides #DBusWatch and #DBusTimeout objects to
+ * the main loop. These are used to know when reading, writing, or
+ * dispatching should be performed.
+ * 
+ * Incoming messages are processed
+ * by calling dbus_connection_dispatch(). dbus_connection_dispatch()
+ * runs any handlers registered for the topmost message in the message
+ * queue, then discards the message, then returns.
+ * 
+ * dbus_connection_get_dispatch_status() indicates whether
+ * messages are currently in the queue that need dispatching.
+ * dbus_connection_set_dispatch_status_function() allows
+ * you to set a function to be used to monitor the dispatch status.
+ * 
+ * If you're using GLib or Qt add-on libraries for D-Bus, there are
+ * special convenience APIs in those libraries that hide
+ * all the details of dispatch and watch/timeout monitoring.
+ * For example, dbus_connection_setup_with_g_main().
+ *
+ * If you aren't using these add-on libraries, but want to process
+ * messages asynchronously, you must manually call
+ * dbus_connection_set_dispatch_status_function(),
+ * dbus_connection_set_watch_functions(),
+ * dbus_connection_set_timeout_functions() providing appropriate
+ * functions to integrate the connection with your application's main
+ * loop. This can be tricky to get right; main loops are not simple.
+ *
+ * If you don't need to be asynchronous, you can ignore #DBusWatch,
+ * #DBusTimeout, and dbus_connection_dispatch().  Instead,
+ * dbus_connection_read_write_dispatch() can be used.
+ *
+ * Or, in <em>very</em> simple applications,
+ * dbus_connection_pop_message() may be all you need, allowing you to
+ * avoid setting up any handler functions (see
+ * dbus_connection_add_filter(),
+ * dbus_connection_register_object_path() for more on handlers).
+ * 
+ * When you use dbus_connection_send() or one of its variants to send
+ * a message, the message is added to the outgoing queue.  It's
+ * actually written to the network later; either in
+ * dbus_watch_handle() invoked by your main loop, or in
+ * dbus_connection_flush() which blocks until it can write out the
+ * entire outgoing queue. The GLib/Qt add-on libraries again
+ * handle the details here for you by setting up watch functions.
+ *
+ * When a connection is disconnected, you are guaranteed to get a
+ * signal "Disconnected" from the interface
+ * #DBUS_INTERFACE_LOCAL, path
+ * #DBUS_PATH_LOCAL.
+ *
+ * You may not drop the last reference to a #DBusConnection
+ * until that connection has been disconnected.
+ *
+ * You may dispatch the unprocessed incoming message queue even if the
+ * connection is disconnected. However, "Disconnected" will always be
+ * the last message in the queue (obviously no messages are received
+ * after disconnection).
+ *
+ * After calling dbus_threads_init(), #DBusConnection has thread
+ * locks and drops them when invoking user callbacks, so in general is
+ * transparently threadsafe. However, #DBusMessage does NOT have
+ * thread locks; you must not send the same message to multiple
+ * #DBusConnection if those connections will be used from different threads,
+ * for example.
+ *
+ * Also, if you dispatch or pop messages from multiple threads, it
+ * may work in the sense that it won't crash, but it's tough to imagine
+ * sane results; it will be completely unpredictable which messages
+ * go to which threads.
+ *
+ * It's recommended to dispatch from a single thread.
+ *
+ * The most useful function to call from multiple threads at once
+ * is dbus_connection_send_with_reply_and_block(). That is,
+ * multiple threads can make method calls at the same time.
+ *
+ * If you aren't using threads, you can use a main loop and
+ * dbus_pending_call_set_notify() to achieve a similar result.
+ */
+
+/**
+ * @defgroup DBusConnectionInternals DBusConnection implementation details
+ * @ingroup  DBusInternals
+ * @brief Implementation details of DBusConnection
+ *
+ * @{
+ */
+
+/**
+ * Internal struct representing a message filter function 
+ */
+typedef struct DBusMessageFilter DBusMessageFilter;
+
+/**
+ * Internal struct representing a message filter function 
+ */
+struct DBusMessageFilter
+{
+  DBusAtomic refcount; /**< Reference count */
+  DBusHandleMessageFunction function; /**< Function to call to filter */
+  void *user_data; /**< User data for the function */
+  DBusFreeFunction free_user_data_function; /**< Function to free the user data */
+};
+
+
+/**
+ * Internals of DBusPreallocatedSend
+ */
+struct DBusPreallocatedSend
+{
+  DBusConnection *connection; /**< Connection we'd send the message to */
+  DBusList *queue_link;       /**< Preallocated link in the queue */
+  DBusList *counter_link;     /**< Preallocated link in the resource counter */
+};
+
+static dbus_bool_t _dbus_modify_sigpipe = TRUE;
+
+/**
+ * Implementation details of DBusConnection. All fields are private.
+ */
+struct DBusConnection
+{
+  DBusAtomic refcount; /**< Reference count. */
+
+  DBusMutex *mutex; /**< Lock on the entire DBusConnection */
+
+  DBusMutex *dispatch_mutex;     /**< Protects dispatch_acquired */
+  DBusCondVar *dispatch_cond;    /**< Notify when dispatch_acquired is available */
+  DBusMutex *io_path_mutex;      /**< Protects io_path_acquired */
+  DBusCondVar *io_path_cond;     /**< Notify when io_path_acquired is available */
+  
+  DBusList *outgoing_messages; /**< Queue of messages we need to send, send the end of the list first. */
+  DBusList *incoming_messages; /**< Queue of messages we have received, end of the list received most recently. */
+
+  DBusMessage *message_borrowed; /**< Filled in if the first incoming message has been borrowed;
+                                  *   dispatch_acquired will be set by the borrower
+                                  */
+  
+  int n_outgoing;              /**< Length of outgoing queue. */
+  int n_incoming;              /**< Length of incoming queue. */
+
+  DBusCounter *outgoing_counter; /**< Counts size of outgoing messages. */
+  
+  DBusTransport *transport;    /**< Object that sends/receives messages over network. */
+  DBusWatchList *watches;      /**< Stores active watches. */
+  DBusTimeoutList *timeouts;   /**< Stores active timeouts. */
+  
+  DBusList *filter_list;        /**< List of filters. */
+
+  DBusDataSlotList slot_list;   /**< Data stored by allocated integer ID */
+
+  DBusHashTable *pending_replies;  /**< Hash of message serials to #DBusPendingCall. */  
+  
+  dbus_uint32_t client_serial;       /**< Client serial. Increments each time a message is sent  */
+  DBusList *disconnect_message_link; /**< Preallocated list node for queueing the disconnection message */
+
+  DBusWakeupMainFunction wakeup_main_function; /**< Function to wake up the mainloop  */
+  void *wakeup_main_data; /**< Application data for wakeup_main_function */
+  DBusFreeFunction free_wakeup_main_data; /**< free wakeup_main_data */
+
+  DBusDispatchStatusFunction dispatch_status_function; /**< Function on dispatch status changes  */
+  void *dispatch_status_data; /**< Application data for dispatch_status_function */
+  DBusFreeFunction free_dispatch_status_data; /**< free dispatch_status_data */
+
+  DBusDispatchStatus last_dispatch_status; /**< The last dispatch status we reported to the application. */
+
+  DBusList *link_cache; /**< A cache of linked list links to prevent contention
+                         *   for the global linked list mempool lock
+                         */
+  DBusObjectTree *objects; /**< Object path handlers registered with this connection */
+
+  char *server_guid; /**< GUID of server if we are in shared_connections, #NULL if server GUID is unknown or connection is private */
+
+  /* These two MUST be bools and not bitfields, because they are protected by a separate lock
+   * from connection->mutex and all bitfields in a word have to be read/written together.
+   * So you can't have a different lock for different bitfields in the same word.
+   */
+  dbus_bool_t dispatch_acquired; /**< Someone has dispatch path (can drain incoming queue) */
+  dbus_bool_t io_path_acquired;  /**< Someone has transport io path (can use the transport to read/write messages) */
+  
+  unsigned int shareable : 1; /**< #TRUE if libdbus owns a reference to the connection and can return it from dbus_connection_open() more than once */
+  
+  unsigned int exit_on_disconnect : 1; /**< If #TRUE, exit after handling disconnect signal */
+
+  unsigned int route_peer_messages : 1; /**< If #TRUE, if org.freedesktop.DBus.Peer messages have a bus name, don't handle them automatically */
+
+  unsigned int disconnected_message_arrived : 1;   /**< We popped or are dispatching the disconnected message.
+                                                    * if the disconnect_message_link is NULL then we queued it, but
+                                                    * this flag is whether it got to the head of the queue.
+                                                    */
+  unsigned int disconnected_message_processed : 1; /**< We did our default handling of the disconnected message,
+                                                    * such as closing the connection.
+                                                    */
+  
+#ifndef DBUS_DISABLE_CHECKS
+  unsigned int have_connection_lock : 1; /**< Used to check locking */
+#endif
+  
+#ifndef DBUS_DISABLE_CHECKS
+  int generation; /**< _dbus_current_generation that should correspond to this connection */
+#endif 
+};
+
+static DBusDispatchStatus _dbus_connection_get_dispatch_status_unlocked      (DBusConnection     *connection);
+static void               _dbus_connection_update_dispatch_status_and_unlock (DBusConnection     *connection,
+                                                                              DBusDispatchStatus  new_status);
+static void               _dbus_connection_last_unref                        (DBusConnection     *connection);
+static void               _dbus_connection_acquire_dispatch                  (DBusConnection     *connection);
+static void               _dbus_connection_release_dispatch                  (DBusConnection     *connection);
+static DBusDispatchStatus _dbus_connection_flush_unlocked                    (DBusConnection     *connection);
+static void               _dbus_connection_close_possibly_shared_and_unlock  (DBusConnection     *connection);
+static dbus_bool_t        _dbus_connection_get_is_connected_unlocked         (DBusConnection     *connection);
+
+static DBusMessageFilter *
+_dbus_message_filter_ref (DBusMessageFilter *filter)
+{
+  _dbus_assert (filter->refcount.value > 0);
+  _dbus_atomic_inc (&filter->refcount);
+
+  return filter;
+}
+
+static void
+_dbus_message_filter_unref (DBusMessageFilter *filter)
+{
+  _dbus_assert (filter->refcount.value > 0);
+
+  if (_dbus_atomic_dec (&filter->refcount) == 1)
+    {
+      if (filter->free_user_data_function)
+        (* filter->free_user_data_function) (filter->user_data);
+      
+      dbus_free (filter);
+    }
+}
+
+/**
+ * Acquires the connection lock.
+ *
+ * @param connection the connection.
+ */
+void
+_dbus_connection_lock (DBusConnection *connection)
+{
+  CONNECTION_LOCK (connection);
+}
+
+/**
+ * Releases the connection lock.
+ *
+ * @param connection the connection.
+ */
+void
+_dbus_connection_unlock (DBusConnection *connection)
+{
+  CONNECTION_UNLOCK (connection);
+}
+
+/**
+ * Wakes up the main loop if it is sleeping
+ * Needed if we're e.g. queueing outgoing messages
+ * on a thread while the mainloop sleeps.
+ *
+ * @param connection the connection.
+ */
+static void
+_dbus_connection_wakeup_mainloop (DBusConnection *connection)
+{
+  if (connection->wakeup_main_function)
+    (*connection->wakeup_main_function) (connection->wakeup_main_data);
+}
+
+#ifdef DBUS_BUILD_TESTS
+/* For now this function isn't used */
+/**
+ * Adds a message to the incoming message queue, returning #FALSE
+ * if there's insufficient memory to queue the message.
+ * Does not take over refcount of the message.
+ *
+ * @param connection the connection.
+ * @param message the message to queue.
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_connection_queue_received_message (DBusConnection *connection,
+                                         DBusMessage    *message)
+{
+  DBusList *link;
+
+  link = _dbus_list_alloc_link (message);
+  if (link == NULL)
+    return FALSE;
+
+  dbus_message_ref (message);
+  _dbus_connection_queue_received_message_link (connection, link);
+
+  return TRUE;
+}
+
+/**
+ * Gets the locks so we can examine them
+ *
+ * @param connection the connection.
+ * @param mutex_loc return for the location of the main mutex pointer
+ * @param dispatch_mutex_loc return location of the dispatch mutex pointer
+ * @param io_path_mutex_loc return location of the io_path mutex pointer
+ * @param dispatch_cond_loc return location of the dispatch conditional 
+ *        variable pointer
+ * @param io_path_cond_loc return location of the io_path conditional 
+ *        variable pointer
+ */ 
+void 
+_dbus_connection_test_get_locks (DBusConnection *connection,
+                                 DBusMutex     **mutex_loc,
+                                 DBusMutex     **dispatch_mutex_loc,
+                                 DBusMutex     **io_path_mutex_loc,
+                                 DBusCondVar   **dispatch_cond_loc,
+                                 DBusCondVar   **io_path_cond_loc)
+{
+  *mutex_loc = connection->mutex;
+  *dispatch_mutex_loc = connection->dispatch_mutex;
+  *io_path_mutex_loc = connection->io_path_mutex; 
+  *dispatch_cond_loc = connection->dispatch_cond;
+  *io_path_cond_loc = connection->io_path_cond;
+}
+#endif
+
+/**
+ * Adds a message-containing list link to the incoming message queue,
+ * taking ownership of the link and the message's current refcount.
+ * Cannot fail due to lack of memory.
+ *
+ * @param connection the connection.
+ * @param link the message link to queue.
+ */
+void
+_dbus_connection_queue_received_message_link (DBusConnection  *connection,
+                                              DBusList        *link)
+{
+  DBusPendingCall *pending;
+  dbus_uint32_t reply_serial;
+  DBusMessage *message;
+  
+  _dbus_assert (_dbus_transport_get_is_authenticated (connection->transport));
+  
+  _dbus_list_append_link (&connection->incoming_messages,
+                          link);
+  message = link->data;
+
+  /* If this is a reply we're waiting on, remove timeout for it */
+  reply_serial = dbus_message_get_reply_serial (message);
+  if (reply_serial != 0)
+    {
+      pending = _dbus_hash_table_lookup_int (connection->pending_replies,
+                                             reply_serial);
+      if (pending != NULL)
+       {
+         if (_dbus_pending_call_is_timeout_added_unlocked (pending))
+            _dbus_connection_remove_timeout_unlocked (connection,
+                                                      _dbus_pending_call_get_timeout_unlocked (pending));
+
+         _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE);
+       }
+    }
+  
+  
+
+  connection->n_incoming += 1;
+
+  _dbus_connection_wakeup_mainloop (connection);
+  
+  _dbus_verbose ("Message %p (%d %s %s %s '%s' reply to %u) added to incoming queue %p, %d incoming\n",
+                 message,
+                 dbus_message_get_type (message),
+                 dbus_message_get_path (message) ?
+                 dbus_message_get_path (message) :
+                 "no path",
+                 dbus_message_get_interface (message) ?
+                 dbus_message_get_interface (message) :
+                 "no interface",
+                 dbus_message_get_member (message) ?
+                 dbus_message_get_member (message) :
+                 "no member",
+                 dbus_message_get_signature (message),
+                 dbus_message_get_reply_serial (message),
+                 connection,
+                 connection->n_incoming);}
+
+/**
+ * Adds a link + message to the incoming message queue.
+ * Can't fail. Takes ownership of both link and message.
+ *
+ * @param connection the connection.
+ * @param link the list node and message to queue.
+ *
+ */
+void
+_dbus_connection_queue_synthesized_message_link (DBusConnection *connection,
+                                                DBusList *link)
+{
+  HAVE_LOCK_CHECK (connection);
+  
+  _dbus_list_append_link (&connection->incoming_messages, link);
+
+  connection->n_incoming += 1;
+
+  _dbus_connection_wakeup_mainloop (connection);
+  
+  _dbus_verbose ("Synthesized message %p added to incoming queue %p, %d incoming\n",
+                 link->data, connection, connection->n_incoming);
+}
+
+
+/**
+ * Checks whether there are messages in the outgoing message queue.
+ * Called with connection lock held.
+ *
+ * @param connection the connection.
+ * @returns #TRUE if the outgoing queue is non-empty.
+ */
+dbus_bool_t
+_dbus_connection_has_messages_to_send_unlocked (DBusConnection *connection)
+{
+  HAVE_LOCK_CHECK (connection);
+  return connection->outgoing_messages != NULL;
+}
+
+/**
+ * Checks whether there are messages in the outgoing message queue.
+ * Use dbus_connection_flush() to block until all outgoing
+ * messages have been written to the underlying transport
+ * (such as a socket).
+ * 
+ * @param connection the connection.
+ * @returns #TRUE if the outgoing queue is non-empty.
+ */
+dbus_bool_t
+dbus_connection_has_messages_to_send (DBusConnection *connection)
+{
+  dbus_bool_t v;
+  
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+
+  CONNECTION_LOCK (connection);
+  v = _dbus_connection_has_messages_to_send_unlocked (connection);
+  CONNECTION_UNLOCK (connection);
+
+  return v;
+}
+
+/**
+ * Gets the next outgoing message. The message remains in the
+ * queue, and the caller does not own a reference to it.
+ *
+ * @param connection the connection.
+ * @returns the message to be sent.
+ */ 
+DBusMessage*
+_dbus_connection_get_message_to_send (DBusConnection *connection)
+{
+  HAVE_LOCK_CHECK (connection);
+  
+  return _dbus_list_get_last (&connection->outgoing_messages);
+}
+
+/**
+ * Notifies the connection that a message has been sent, so the
+ * message can be removed from the outgoing queue.
+ * Called with the connection lock held.
+ *
+ * @param connection the connection.
+ * @param message the message that was sent.
+ */
+void
+_dbus_connection_message_sent (DBusConnection *connection,
+                               DBusMessage    *message)
+{
+  DBusList *link;
+
+  HAVE_LOCK_CHECK (connection);
+  
+  /* This can be called before we even complete authentication, since
+   * it's called on disconnect to clean up the outgoing queue.
+   * It's also called as we successfully send each message.
+   */
+  
+  link = _dbus_list_get_last_link (&connection->outgoing_messages);
+  _dbus_assert (link != NULL);
+  _dbus_assert (link->data == message);
+
+  /* Save this link in the link cache */
+  _dbus_list_unlink (&connection->outgoing_messages,
+                     link);
+  _dbus_list_prepend_link (&connection->link_cache, link);
+  
+  connection->n_outgoing -= 1;
+
+  _dbus_verbose ("Message %p (%d %s %s %s '%s') removed from outgoing queue %p, %d left to send\n",
+                 message,
+                 dbus_message_get_type (message),
+                 dbus_message_get_path (message) ?
+                 dbus_message_get_path (message) :
+                 "no path",
+                 dbus_message_get_interface (message) ?
+                 dbus_message_get_interface (message) :
+                 "no interface",
+                 dbus_message_get_member (message) ?
+                 dbus_message_get_member (message) :
+                 "no member",
+                 dbus_message_get_signature (message),
+                 connection, connection->n_outgoing);
+
+  /* Save this link in the link cache also */
+  _dbus_message_remove_size_counter (message, connection->outgoing_counter,
+                                     &link);
+  _dbus_list_prepend_link (&connection->link_cache, link);
+  
+  dbus_message_unref (message);
+}
+
+/** Function to be called in protected_change_watch() with refcount held */
+typedef dbus_bool_t (* DBusWatchAddFunction)     (DBusWatchList *list,
+                                                  DBusWatch     *watch);
+/** Function to be called in protected_change_watch() with refcount held */
+typedef void        (* DBusWatchRemoveFunction)  (DBusWatchList *list,
+                                                  DBusWatch     *watch);
+/** Function to be called in protected_change_watch() with refcount held */
+typedef void        (* DBusWatchToggleFunction)  (DBusWatchList *list,
+                                                  DBusWatch     *watch,
+                                                  dbus_bool_t    enabled);
+
+static dbus_bool_t
+protected_change_watch (DBusConnection         *connection,
+                        DBusWatch              *watch,
+                        DBusWatchAddFunction    add_function,
+                        DBusWatchRemoveFunction remove_function,
+                        DBusWatchToggleFunction toggle_function,
+                        dbus_bool_t             enabled)
+{
+  DBusWatchList *watches;
+  dbus_bool_t retval;
+  
+  HAVE_LOCK_CHECK (connection);
+
+  /* This isn't really safe or reasonable; a better pattern is the "do everything, then
+   * drop lock and call out" one; but it has to be propagated up through all callers
+   */
+  
+  watches = connection->watches;
+  if (watches)
+    {
+      connection->watches = NULL;
+      _dbus_connection_ref_unlocked (connection);
+      CONNECTION_UNLOCK (connection);
+
+      if (add_function)
+        retval = (* add_function) (watches, watch);
+      else if (remove_function)
+        {
+          retval = TRUE;
+          (* remove_function) (watches, watch);
+        }
+      else
+        {
+          retval = TRUE;
+          (* toggle_function) (watches, watch, enabled);
+        }
+      
+      CONNECTION_LOCK (connection);
+      connection->watches = watches;
+      _dbus_connection_unref_unlocked (connection);
+
+      return retval;
+    }
+  else
+    return FALSE;
+}
+     
+
+/**
+ * Adds a watch using the connection's DBusAddWatchFunction if
+ * available. Otherwise records the watch to be added when said
+ * function is available. Also re-adds the watch if the
+ * DBusAddWatchFunction changes. May fail due to lack of memory.
+ * Connection lock should be held when calling this.
+ *
+ * @param connection the connection.
+ * @param watch the watch to add.
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_connection_add_watch_unlocked (DBusConnection *connection,
+                                     DBusWatch      *watch)
+{
+  return protected_change_watch (connection, watch,
+                                 _dbus_watch_list_add_watch,
+                                 NULL, NULL, FALSE);
+}
+
+/**
+ * Removes a watch using the connection's DBusRemoveWatchFunction
+ * if available. It's an error to call this function on a watch
+ * that was not previously added.
+ * Connection lock should be held when calling this.
+ *
+ * @param connection the connection.
+ * @param watch the watch to remove.
+ */
+void
+_dbus_connection_remove_watch_unlocked (DBusConnection *connection,
+                                        DBusWatch      *watch)
+{
+  protected_change_watch (connection, watch,
+                          NULL,
+                          _dbus_watch_list_remove_watch,
+                          NULL, FALSE);
+}
+
+/**
+ * Toggles a watch and notifies app via connection's
+ * DBusWatchToggledFunction if available. It's an error to call this
+ * function on a watch that was not previously added.
+ * Connection lock should be held when calling this.
+ *
+ * @param connection the connection.
+ * @param watch the watch to toggle.
+ * @param enabled whether to enable or disable
+ */
+void
+_dbus_connection_toggle_watch_unlocked (DBusConnection *connection,
+                                        DBusWatch      *watch,
+                                        dbus_bool_t     enabled)
+{
+  _dbus_assert (watch != NULL);
+
+  protected_change_watch (connection, watch,
+                          NULL, NULL,
+                          _dbus_watch_list_toggle_watch,
+                          enabled);
+}
+
+/** Function to be called in protected_change_timeout() with refcount held */
+typedef dbus_bool_t (* DBusTimeoutAddFunction)    (DBusTimeoutList *list,
+                                                   DBusTimeout     *timeout);
+/** Function to be called in protected_change_timeout() with refcount held */
+typedef void        (* DBusTimeoutRemoveFunction) (DBusTimeoutList *list,
+                                                   DBusTimeout     *timeout);
+/** Function to be called in protected_change_timeout() with refcount held */
+typedef void        (* DBusTimeoutToggleFunction) (DBusTimeoutList *list,
+                                                   DBusTimeout     *timeout,
+                                                   dbus_bool_t      enabled);
+
+static dbus_bool_t
+protected_change_timeout (DBusConnection           *connection,
+                          DBusTimeout              *timeout,
+                          DBusTimeoutAddFunction    add_function,
+                          DBusTimeoutRemoveFunction remove_function,
+                          DBusTimeoutToggleFunction toggle_function,
+                          dbus_bool_t               enabled)
+{
+  DBusTimeoutList *timeouts;
+  dbus_bool_t retval;
+  
+  HAVE_LOCK_CHECK (connection);
+
+  /* This isn't really safe or reasonable; a better pattern is the "do everything, then
+   * drop lock and call out" one; but it has to be propagated up through all callers
+   */
+  
+  timeouts = connection->timeouts;
+  if (timeouts)
+    {
+      connection->timeouts = NULL;
+      _dbus_connection_ref_unlocked (connection);
+      CONNECTION_UNLOCK (connection);
+
+      if (add_function)
+        retval = (* add_function) (timeouts, timeout);
+      else if (remove_function)
+        {
+          retval = TRUE;
+          (* remove_function) (timeouts, timeout);
+        }
+      else
+        {
+          retval = TRUE;
+          (* toggle_function) (timeouts, timeout, enabled);
+        }
+      
+      CONNECTION_LOCK (connection);
+      connection->timeouts = timeouts;
+      _dbus_connection_unref_unlocked (connection);
+
+      return retval;
+    }
+  else
+    return FALSE;
+}
+
+/**
+ * Adds a timeout using the connection's DBusAddTimeoutFunction if
+ * available. Otherwise records the timeout to be added when said
+ * function is available. Also re-adds the timeout if the
+ * DBusAddTimeoutFunction changes. May fail due to lack of memory.
+ * The timeout will fire repeatedly until removed.
+ * Connection lock should be held when calling this.
+ *
+ * @param connection the connection.
+ * @param timeout the timeout to add.
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_connection_add_timeout_unlocked (DBusConnection *connection,
+                                       DBusTimeout    *timeout)
+{
+  return protected_change_timeout (connection, timeout,
+                                   _dbus_timeout_list_add_timeout,
+                                   NULL, NULL, FALSE);
+}
+
+/**
+ * Removes a timeout using the connection's DBusRemoveTimeoutFunction
+ * if available. It's an error to call this function on a timeout
+ * that was not previously added.
+ * Connection lock should be held when calling this.
+ *
+ * @param connection the connection.
+ * @param timeout the timeout to remove.
+ */
+void
+_dbus_connection_remove_timeout_unlocked (DBusConnection *connection,
+                                          DBusTimeout    *timeout)
+{
+  protected_change_timeout (connection, timeout,
+                            NULL,
+                            _dbus_timeout_list_remove_timeout,
+                            NULL, FALSE);
+}
+
+/**
+ * Toggles a timeout and notifies app via connection's
+ * DBusTimeoutToggledFunction if available. It's an error to call this
+ * function on a timeout that was not previously added.
+ * Connection lock should be held when calling this.
+ *
+ * @param connection the connection.
+ * @param timeout the timeout to toggle.
+ * @param enabled whether to enable or disable
+ */
+void
+_dbus_connection_toggle_timeout_unlocked (DBusConnection   *connection,
+                                          DBusTimeout      *timeout,
+                                          dbus_bool_t       enabled)
+{
+  protected_change_timeout (connection, timeout,
+                            NULL, NULL,
+                            _dbus_timeout_list_toggle_timeout,
+                            enabled);
+}
+
+static dbus_bool_t
+_dbus_connection_attach_pending_call_unlocked (DBusConnection  *connection,
+                                               DBusPendingCall *pending)
+{
+  dbus_uint32_t reply_serial;
+  DBusTimeout *timeout;
+
+  HAVE_LOCK_CHECK (connection);
+
+  reply_serial = _dbus_pending_call_get_reply_serial_unlocked (pending);
+
+  _dbus_assert (reply_serial != 0);
+
+  timeout = _dbus_pending_call_get_timeout_unlocked (pending);
+
+  if (!_dbus_connection_add_timeout_unlocked (connection, timeout))
+    return FALSE;
+  
+  if (!_dbus_hash_table_insert_int (connection->pending_replies,
+                                    reply_serial,
+                                    pending))
+    {
+      _dbus_connection_remove_timeout_unlocked (connection, timeout);
+
+      _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE);
+      HAVE_LOCK_CHECK (connection);
+      return FALSE;
+    }
+  
+  _dbus_pending_call_set_timeout_added_unlocked (pending, TRUE);
+
+  _dbus_pending_call_ref_unlocked (pending);
+
+  HAVE_LOCK_CHECK (connection);
+  
+  return TRUE;
+}
+
+static void
+free_pending_call_on_hash_removal (void *data)
+{
+  DBusPendingCall *pending;
+  DBusConnection  *connection;
+  
+  if (data == NULL)
+    return;
+
+  pending = data;
+
+  connection = _dbus_pending_call_get_connection_unlocked (pending);
+
+  HAVE_LOCK_CHECK (connection);
+  
+  if (_dbus_pending_call_is_timeout_added_unlocked (pending))
+    {
+      _dbus_connection_remove_timeout_unlocked (connection,
+                                                _dbus_pending_call_get_timeout_unlocked (pending));
+      
+      _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE);
+    }
+
+  /* FIXME 1.0? this is sort of dangerous and undesirable to drop the lock 
+   * here, but the pending call finalizer could in principle call out to 
+   * application code so we pretty much have to... some larger code reorg 
+   * might be needed.
+   */
+  _dbus_connection_ref_unlocked (connection);
+  _dbus_pending_call_unref_and_unlock (pending);
+  CONNECTION_LOCK (connection);
+  _dbus_connection_unref_unlocked (connection);
+}
+
+static void
+_dbus_connection_detach_pending_call_unlocked (DBusConnection  *connection,
+                                               DBusPendingCall *pending)
+{
+  /* This ends up unlocking to call the pending call finalizer, which is unexpected to
+   * say the least.
+   */
+  _dbus_hash_table_remove_int (connection->pending_replies,
+                               _dbus_pending_call_get_reply_serial_unlocked (pending));
+}
+
+static void
+_dbus_connection_detach_pending_call_and_unlock (DBusConnection  *connection,
+                                                 DBusPendingCall *pending)
+{
+  /* The idea here is to avoid finalizing the pending call
+   * with the lock held, since there's a destroy notifier
+   * in pending call that goes out to application code.
+   *
+   * There's an extra unlock inside the hash table
+   * "free pending call" function FIXME...
+   */
+  _dbus_pending_call_ref_unlocked (pending);
+  _dbus_hash_table_remove_int (connection->pending_replies,
+                               _dbus_pending_call_get_reply_serial_unlocked (pending));
+
+  if (_dbus_pending_call_is_timeout_added_unlocked (pending))
+      _dbus_connection_remove_timeout_unlocked (connection,
+              _dbus_pending_call_get_timeout_unlocked (pending));
+
+  _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE);
+
+  _dbus_pending_call_unref_and_unlock (pending);
+}
+
+/**
+ * Removes a pending call from the connection, such that
+ * the pending reply will be ignored. May drop the last
+ * reference to the pending call.
+ *
+ * @param connection the connection
+ * @param pending the pending call
+ */
+void
+_dbus_connection_remove_pending_call (DBusConnection  *connection,
+                                      DBusPendingCall *pending)
+{
+  CONNECTION_LOCK (connection);
+  _dbus_connection_detach_pending_call_and_unlock (connection, pending);
+}
+
+/**
+ * Acquire the transporter I/O path. This must be done before
+ * doing any I/O in the transporter. May sleep and drop the
+ * IO path mutex while waiting for the I/O path.
+ *
+ * @param connection the connection.
+ * @param timeout_milliseconds maximum blocking time, or -1 for no limit.
+ * @returns TRUE if the I/O path was acquired.
+ */
+static dbus_bool_t
+_dbus_connection_acquire_io_path (DBusConnection *connection,
+                                 int             timeout_milliseconds)
+{
+  dbus_bool_t we_acquired;
+  
+  HAVE_LOCK_CHECK (connection);
+
+  /* We don't want the connection to vanish */
+  _dbus_connection_ref_unlocked (connection);
+
+  /* We will only touch io_path_acquired which is protected by our mutex */
+  CONNECTION_UNLOCK (connection);
+  
+  _dbus_verbose ("%s locking io_path_mutex\n", _DBUS_FUNCTION_NAME);
+  _dbus_mutex_lock (connection->io_path_mutex);
+
+  _dbus_verbose ("%s start connection->io_path_acquired = %d timeout = %d\n",
+                 _DBUS_FUNCTION_NAME, connection->io_path_acquired, timeout_milliseconds);
+
+  we_acquired = FALSE;
+  
+  if (connection->io_path_acquired)
+    {
+      if (timeout_milliseconds != -1)
+        {
+          _dbus_verbose ("%s waiting %d for IO path to be acquirable\n",
+                         _DBUS_FUNCTION_NAME, timeout_milliseconds);
+
+          if (!_dbus_condvar_wait_timeout (connection->io_path_cond,
+                                           connection->io_path_mutex,
+                                           timeout_milliseconds))
+            {
+              /* We timed out before anyone signaled. */
+              /* (writing the loop to handle the !timedout case by
+               * waiting longer if needed is a pain since dbus
+               * wraps pthread_cond_timedwait to take a relative
+               * time instead of absolute, something kind of stupid
+               * on our part. for now it doesn't matter, we will just
+               * end up back here eventually.)
+               */
+            }
+        }
+      else
+        {
+          while (connection->io_path_acquired)
+            {
+              _dbus_verbose ("%s waiting for IO path to be acquirable\n", _DBUS_FUNCTION_NAME);
+              _dbus_condvar_wait (connection->io_path_cond, 
+                                  connection->io_path_mutex);
+            }
+        }
+    }
+  
+  if (!connection->io_path_acquired)
+    {
+      we_acquired = TRUE;
+      connection->io_path_acquired = TRUE;
+    }
+  
+  _dbus_verbose ("%s end connection->io_path_acquired = %d we_acquired = %d\n",
+                 _DBUS_FUNCTION_NAME, connection->io_path_acquired, we_acquired);
+
+  _dbus_verbose ("%s unlocking io_path_mutex\n", _DBUS_FUNCTION_NAME);
+  _dbus_mutex_unlock (connection->io_path_mutex);
+
+  CONNECTION_LOCK (connection);
+  
+  HAVE_LOCK_CHECK (connection);
+
+  _dbus_connection_unref_unlocked (connection);
+  
+  return we_acquired;
+}
+
+/**
+ * Release the I/O path when you're done with it. Only call
+ * after you've acquired the I/O. Wakes up at most one thread
+ * currently waiting to acquire the I/O path.
+ *
+ * @param connection the connection.
+ */
+static void
+_dbus_connection_release_io_path (DBusConnection *connection)
+{
+  HAVE_LOCK_CHECK (connection);
+  
+  _dbus_verbose ("%s locking io_path_mutex\n", _DBUS_FUNCTION_NAME);
+  _dbus_mutex_lock (connection->io_path_mutex);
+  
+  _dbus_assert (connection->io_path_acquired);
+
+  _dbus_verbose ("%s start connection->io_path_acquired = %d\n",
+                 _DBUS_FUNCTION_NAME, connection->io_path_acquired);
+  
+  connection->io_path_acquired = FALSE;
+  _dbus_condvar_wake_one (connection->io_path_cond);
+
+  _dbus_verbose ("%s unlocking io_path_mutex\n", _DBUS_FUNCTION_NAME);
+  _dbus_mutex_unlock (connection->io_path_mutex);
+}
+
+/**
+ * Queues incoming messages and sends outgoing messages for this
+ * connection, optionally blocking in the process. Each call to
+ * _dbus_connection_do_iteration_unlocked() will call select() or poll() one
+ * time and then read or write data if possible.
+ *
+ * The purpose of this function is to be able to flush outgoing
+ * messages or queue up incoming messages without returning
+ * control to the application and causing reentrancy weirdness.
+ *
+ * The flags parameter allows you to specify whether to
+ * read incoming messages, write outgoing messages, or both,
+ * and whether to block if no immediate action is possible.
+ *
+ * The timeout_milliseconds parameter does nothing unless the
+ * iteration is blocking.
+ *
+ * If there are no outgoing messages and DBUS_ITERATION_DO_READING
+ * wasn't specified, then it's impossible to block, even if
+ * you specify DBUS_ITERATION_BLOCK; in that case the function
+ * returns immediately.
+ *
+ * Called with connection lock held.
+ * 
+ * @param connection the connection.
+ * @param flags iteration flags.
+ * @param timeout_milliseconds maximum blocking time, or -1 for no limit.
+ */
+void
+_dbus_connection_do_iteration_unlocked (DBusConnection *connection,
+                                        unsigned int    flags,
+                                        int             timeout_milliseconds)
+{
+  _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME);
+  
+  HAVE_LOCK_CHECK (connection);
+  
+  if (connection->n_outgoing == 0)
+    flags &= ~DBUS_ITERATION_DO_WRITING;
+
+  if (_dbus_connection_acquire_io_path (connection,
+                                       (flags & DBUS_ITERATION_BLOCK) ? timeout_milliseconds : 0))
+    {
+      HAVE_LOCK_CHECK (connection);
+      
+      _dbus_transport_do_iteration (connection->transport,
+                                   flags, timeout_milliseconds);
+      _dbus_connection_release_io_path (connection);
+    }
+
+  HAVE_LOCK_CHECK (connection);
+
+  _dbus_verbose ("%s end\n", _DBUS_FUNCTION_NAME);
+}
+
+/**
+ * Creates a new connection for the given transport.  A transport
+ * represents a message stream that uses some concrete mechanism, such
+ * as UNIX domain sockets. May return #NULL if insufficient
+ * memory exists to create the connection.
+ *
+ * @param transport the transport.
+ * @returns the new connection, or #NULL on failure.
+ */
+DBusConnection*
+_dbus_connection_new_for_transport (DBusTransport *transport)
+{
+  DBusConnection *connection;
+  DBusWatchList *watch_list;
+  DBusTimeoutList *timeout_list;
+  DBusHashTable *pending_replies;
+  DBusList *disconnect_link;
+  DBusMessage *disconnect_message;
+  DBusCounter *outgoing_counter;
+  DBusObjectTree *objects;
+  
+  watch_list = NULL;
+  connection = NULL;
+  pending_replies = NULL;
+  timeout_list = NULL;
+  disconnect_link = NULL;
+  disconnect_message = NULL;
+  outgoing_counter = NULL;
+  objects = NULL;
+  
+  watch_list = _dbus_watch_list_new ();
+  if (watch_list == NULL)
+    goto error;
+
+  timeout_list = _dbus_timeout_list_new ();
+  if (timeout_list == NULL)
+    goto error;  
+
+  pending_replies =
+    _dbus_hash_table_new (DBUS_HASH_INT,
+                         NULL,
+                          (DBusFreeFunction)free_pending_call_on_hash_removal);
+  if (pending_replies == NULL)
+    goto error;
+  
+  connection = dbus_new0 (DBusConnection, 1);
+  if (connection == NULL)
+    goto error;
+
+  _dbus_mutex_new_at_location (&connection->mutex);
+  if (connection->mutex == NULL)
+    goto error;
+
+  _dbus_mutex_new_at_location (&connection->io_path_mutex);
+  if (connection->io_path_mutex == NULL)
+    goto error;
+
+  _dbus_mutex_new_at_location (&connection->dispatch_mutex);
+  if (connection->dispatch_mutex == NULL)
+    goto error;
+  
+  _dbus_condvar_new_at_location (&connection->dispatch_cond);
+  if (connection->dispatch_cond == NULL)
+    goto error;
+  
+  _dbus_condvar_new_at_location (&connection->io_path_cond);
+  if (connection->io_path_cond == NULL)
+    goto error;
+
+  disconnect_message = dbus_message_new_signal (DBUS_PATH_LOCAL,
+                                                DBUS_INTERFACE_LOCAL,
+                                                "Disconnected");
+  
+  if (disconnect_message == NULL)
+    goto error;
+
+  disconnect_link = _dbus_list_alloc_link (disconnect_message);
+  if (disconnect_link == NULL)
+    goto error;
+
+  outgoing_counter = _dbus_counter_new ();
+  if (outgoing_counter == NULL)
+    goto error;
+
+  objects = _dbus_object_tree_new (connection);
+  if (objects == NULL)
+    goto error;
+  
+  if (_dbus_modify_sigpipe)
+    _dbus_disable_sigpipe ();
+  
+  connection->refcount.value = 1;
+  connection->transport = transport;
+  connection->watches = watch_list;
+  connection->timeouts = timeout_list;
+  connection->pending_replies = pending_replies;
+  connection->outgoing_counter = outgoing_counter;
+  connection->filter_list = NULL;
+  connection->last_dispatch_status = DBUS_DISPATCH_COMPLETE; /* so we're notified first time there's data */
+  connection->objects = objects;
+  connection->exit_on_disconnect = FALSE;
+  connection->shareable = FALSE;
+  connection->route_peer_messages = FALSE;
+  connection->disconnected_message_arrived = FALSE;
+  connection->disconnected_message_processed = FALSE;
+  
+#ifndef DBUS_DISABLE_CHECKS
+  connection->generation = _dbus_current_generation;
+#endif
+  
+  _dbus_data_slot_list_init (&connection->slot_list);
+
+  connection->client_serial = 1;
+
+  connection->disconnect_message_link = disconnect_link;
+
+  CONNECTION_LOCK (connection);
+  
+  if (!_dbus_transport_set_connection (transport, connection))
+    {
+      CONNECTION_UNLOCK (connection);
+
+      goto error;
+    }
+
+  _dbus_transport_ref (transport);
+
+  CONNECTION_UNLOCK (connection);
+  
+  return connection;
+  
+ error:
+  if (disconnect_message != NULL)
+    dbus_message_unref (disconnect_message);
+  
+  if (disconnect_link != NULL)
+    _dbus_list_free_link (disconnect_link);
+  
+  if (connection != NULL)
+    {
+      _dbus_condvar_free_at_location (&connection->io_path_cond);
+      _dbus_condvar_free_at_location (&connection->dispatch_cond);
+      _dbus_mutex_free_at_location (&connection->mutex);
+      _dbus_mutex_free_at_location (&connection->io_path_mutex);
+      _dbus_mutex_free_at_location (&connection->dispatch_mutex);
+      dbus_free (connection);
+    }
+  if (pending_replies)
+    _dbus_hash_table_unref (pending_replies);
+  
+  if (watch_list)
+    _dbus_watch_list_free (watch_list);
+
+  if (timeout_list)
+    _dbus_timeout_list_free (timeout_list);
+
+  if (outgoing_counter)
+    _dbus_counter_unref (outgoing_counter);
+
+  if (objects)
+    _dbus_object_tree_unref (objects);
+  
+  return NULL;
+}
+
+/**
+ * Increments the reference count of a DBusConnection.
+ * Requires that the caller already holds the connection lock.
+ *
+ * @param connection the connection.
+ * @returns the connection.
+ */
+DBusConnection *
+_dbus_connection_ref_unlocked (DBusConnection *connection)
+{  
+  _dbus_assert (connection != NULL);
+  _dbus_assert (connection->generation == _dbus_current_generation);
+
+  HAVE_LOCK_CHECK (connection);
+  
+#ifdef DBUS_HAVE_ATOMIC_INT
+  _dbus_atomic_inc (&connection->refcount);
+#else
+  _dbus_assert (connection->refcount.value > 0);
+  connection->refcount.value += 1;
+#endif
+
+  return connection;
+}
+
+/**
+ * Decrements the reference count of a DBusConnection.
+ * Requires that the caller already holds the connection lock.
+ *
+ * @param connection the connection.
+ */
+void
+_dbus_connection_unref_unlocked (DBusConnection *connection)
+{
+  dbus_bool_t last_unref;
+
+  HAVE_LOCK_CHECK (connection);
+  
+  _dbus_assert (connection != NULL);
+
+  /* The connection lock is better than the global
+   * lock in the atomic increment fallback
+   */
+  
+#ifdef DBUS_HAVE_ATOMIC_INT
+  last_unref = (_dbus_atomic_dec (&connection->refcount) == 1);
+#else
+  _dbus_assert (connection->refcount.value > 0);
+
+  connection->refcount.value -= 1;
+  last_unref = (connection->refcount.value == 0);  
+#if 0
+  printf ("unref_unlocked() connection %p count = %d\n", connection, connection->refcount.value);
+#endif
+#endif
+  
+  if (last_unref)
+    _dbus_connection_last_unref (connection);
+}
+
+static dbus_uint32_t
+_dbus_connection_get_next_client_serial (DBusConnection *connection)
+{
+  dbus_uint32_t serial;
+
+  serial = connection->client_serial++;
+
+  if (connection->client_serial == 0)
+    connection->client_serial = 1;
+
+  return serial;
+}
+
+/**
+ * A callback for use with dbus_watch_new() to create a DBusWatch.
+ * 
+ * @todo This is basically a hack - we could delete _dbus_transport_handle_watch()
+ * and the virtual handle_watch in DBusTransport if we got rid of it.
+ * The reason this is some work is threading, see the _dbus_connection_handle_watch()
+ * implementation.
+ *
+ * @param watch the watch.
+ * @param condition the current condition of the file descriptors being watched.
+ * @param data must be a pointer to a #DBusConnection
+ * @returns #FALSE if the IO condition may not have been fully handled due to lack of memory
+ */
+dbus_bool_t
+_dbus_connection_handle_watch (DBusWatch                   *watch,
+                               unsigned int                 condition,
+                               void                        *data)
+{
+  DBusConnection *connection;
+  dbus_bool_t retval;
+  DBusDispatchStatus status;
+
+  connection = data;
+
+  _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME);
+  
+  CONNECTION_LOCK (connection);
+  _dbus_connection_acquire_io_path (connection, -1);
+  HAVE_LOCK_CHECK (connection);
+  retval = _dbus_transport_handle_watch (connection->transport,
+                                         watch, condition);
+
+  _dbus_connection_release_io_path (connection);
+
+  HAVE_LOCK_CHECK (connection);
+
+  _dbus_verbose ("%s middle\n", _DBUS_FUNCTION_NAME);
+  
+  status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+  /* this calls out to user code */
+  _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+
+  _dbus_verbose ("%s end\n", _DBUS_FUNCTION_NAME);
+  
+  return retval;
+}
+
+_DBUS_DEFINE_GLOBAL_LOCK (shared_connections);
+static DBusHashTable *shared_connections = NULL;
+static DBusList *shared_connections_no_guid = NULL;
+
+static void
+close_connection_on_shutdown (DBusConnection *connection)
+{
+  DBusMessage *message;
+
+  dbus_connection_ref (connection);
+  _dbus_connection_close_possibly_shared (connection);
+
+  /* Churn through to the Disconnected message */
+  while ((message = dbus_connection_pop_message (connection)))
+    {
+      dbus_message_unref (message);
+    }
+  dbus_connection_unref (connection);
+}
+
+static void
+shared_connections_shutdown (void *data)
+{
+  int n_entries;
+  
+  _DBUS_LOCK (shared_connections);
+  
+  /* This is a little bit unpleasant... better ideas? */
+  while ((n_entries = _dbus_hash_table_get_n_entries (shared_connections)) > 0)
+    {
+      DBusConnection *connection;
+      DBusHashIter iter;
+      
+      _dbus_hash_iter_init (shared_connections, &iter);
+      _dbus_hash_iter_next (&iter);
+       
+      connection = _dbus_hash_iter_get_value (&iter);
+
+      _DBUS_UNLOCK (shared_connections);
+      close_connection_on_shutdown (connection);
+      _DBUS_LOCK (shared_connections);
+
+      /* The connection should now be dead and not in our hash ... */
+      _dbus_assert (_dbus_hash_table_get_n_entries (shared_connections) < n_entries);
+    }
+
+  _dbus_assert (_dbus_hash_table_get_n_entries (shared_connections) == 0);
+  
+  _dbus_hash_table_unref (shared_connections);
+  shared_connections = NULL;
+
+  if (shared_connections_no_guid != NULL)
+    {
+      DBusConnection *connection;
+      connection = _dbus_list_pop_first (&shared_connections_no_guid);
+      while (connection != NULL)
+        {
+          _DBUS_UNLOCK (shared_connections);
+          close_connection_on_shutdown (connection);
+          _DBUS_LOCK (shared_connections);
+          connection = _dbus_list_pop_first (&shared_connections_no_guid);
+        }
+    }
+
+  shared_connections_no_guid = NULL;
+  
+  _DBUS_UNLOCK (shared_connections);
+}
+
+static dbus_bool_t
+connection_lookup_shared (DBusAddressEntry  *entry,
+                          DBusConnection   **result)
+{
+  _dbus_verbose ("checking for existing connection\n");
+  
+  *result = NULL;
+  
+  _DBUS_LOCK (shared_connections);
+
+  if (shared_connections == NULL)
+    {
+      _dbus_verbose ("creating shared_connections hash table\n");
+      
+      shared_connections = _dbus_hash_table_new (DBUS_HASH_STRING,
+                                                 dbus_free,
+                                                 NULL);
+      if (shared_connections == NULL)
+        {
+          _DBUS_UNLOCK (shared_connections);
+          return FALSE;
+        }
+
+      if (!_dbus_register_shutdown_func (shared_connections_shutdown, NULL))
+        {
+          _dbus_hash_table_unref (shared_connections);
+          shared_connections = NULL;
+          _DBUS_UNLOCK (shared_connections);
+          return FALSE;
+        }
+
+      _dbus_verbose ("  successfully created shared_connections\n");
+      
+      _DBUS_UNLOCK (shared_connections);
+      return TRUE; /* no point looking up in the hash we just made */
+    }
+  else
+    {
+      const char *guid;
+
+      guid = dbus_address_entry_get_value (entry, "guid");
+      
+      if (guid != NULL)
+        {
+          DBusConnection *connection;
+          
+          connection = _dbus_hash_table_lookup_string (shared_connections,
+                                                       guid);
+
+          if (connection)
+            {
+              /* The DBusConnection can't be finalized without taking
+               * the shared_connections lock to remove it from the
+               * hash.  So it's safe to ref the connection here.
+               * However, it may be disconnected if the Disconnected
+               * message hasn't been processed yet, in which case we
+               * want to pretend it isn't in the hash and avoid
+               * returning it.
+               *
+               * The idea is to avoid ever returning a disconnected connection
+               * from dbus_connection_open(). We could just synchronously
+               * drop our shared ref to the connection on connection disconnect,
+               * and then assert here that the connection is connected, but
+               * that causes reentrancy headaches.
+               */
+              CONNECTION_LOCK (connection);
+              if (_dbus_connection_get_is_connected_unlocked (connection))
+                {
+                  _dbus_connection_ref_unlocked (connection);
+                  *result = connection;
+                  _dbus_verbose ("looked up existing connection to server guid %s\n",
+                                 guid);
+                }
+              else
+                {
+                  _dbus_verbose ("looked up existing connection to server guid %s but it was disconnected so ignoring it\n",
+                                 guid);
+                }
+              CONNECTION_UNLOCK (connection);
+            }
+        }
+      
+      _DBUS_UNLOCK (shared_connections);
+      return TRUE;
+    }
+}
+
+static dbus_bool_t
+connection_record_shared_unlocked (DBusConnection *connection,
+                                   const char     *guid)
+{
+  char *guid_key;
+  char *guid_in_connection;
+
+  HAVE_LOCK_CHECK (connection);
+  _dbus_assert (connection->server_guid == NULL);
+  _dbus_assert (connection->shareable);
+
+  /* get a hard ref on this connection, even if
+   * we won't in fact store it in the hash, we still
+   * need to hold a ref on it until it's disconnected.
+   */
+  _dbus_connection_ref_unlocked (connection);
+
+  if (guid == NULL)
+    {
+      _DBUS_LOCK (shared_connections);
+
+      if (!_dbus_list_prepend (&shared_connections_no_guid, connection))
+        {
+          _DBUS_UNLOCK (shared_connections);
+          return FALSE;
+        }
+
+      _DBUS_UNLOCK (shared_connections);
+      return TRUE; /* don't store in the hash */
+    }
+  
+  /* A separate copy of the key is required in the hash table, because
+   * we don't have a lock on the connection when we are doing a hash
+   * lookup.
+   */
+  
+  guid_key = _dbus_strdup (guid);
+  if (guid_key == NULL)
+    return FALSE;
+
+  guid_in_connection = _dbus_strdup (guid);
+  if (guid_in_connection == NULL)
+    {
+      dbus_free (guid_key);
+      return FALSE;
+    }
+  
+  _DBUS_LOCK (shared_connections);
+  _dbus_assert (shared_connections != NULL);
+  
+  if (!_dbus_hash_table_insert_string (shared_connections,
+                                       guid_key, connection))
+    {
+      dbus_free (guid_key);
+      dbus_free (guid_in_connection);
+      _DBUS_UNLOCK (shared_connections);
+      return FALSE;
+    }
+
+  connection->server_guid = guid_in_connection;
+
+  _dbus_verbose ("stored connection to %s to be shared\n",
+                 connection->server_guid);
+  
+  _DBUS_UNLOCK (shared_connections);
+
+  _dbus_assert (connection->server_guid != NULL);
+  
+  return TRUE;
+}
+
+static void
+connection_forget_shared_unlocked (DBusConnection *connection)
+{
+  HAVE_LOCK_CHECK (connection);
+
+  if (!connection->shareable)
+    return;
+  
+  _DBUS_LOCK (shared_connections);
+      
+  if (connection->server_guid != NULL)
+    {
+      _dbus_verbose ("dropping connection to %s out of the shared table\n",
+                     connection->server_guid);
+      
+      if (!_dbus_hash_table_remove_string (shared_connections,
+                                           connection->server_guid))
+        _dbus_assert_not_reached ("connection was not in the shared table");
+      
+      dbus_free (connection->server_guid);
+      connection->server_guid = NULL;
+    }
+  else
+    {
+      _dbus_list_remove (&shared_connections_no_guid, connection);
+    }
+
+  _DBUS_UNLOCK (shared_connections);
+  
+  /* remove our reference held on all shareable connections */
+  _dbus_connection_unref_unlocked (connection);
+}
+
+static DBusConnection*
+connection_try_from_address_entry (DBusAddressEntry *entry,
+                                   DBusError        *error)
+{
+  DBusTransport *transport;
+  DBusConnection *connection;
+
+  transport = _dbus_transport_open (entry, error);
+
+  if (transport == NULL)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      return NULL;
+    }
+
+  connection = _dbus_connection_new_for_transport (transport);
+
+  _dbus_transport_unref (transport);
+  
+  if (connection == NULL)
+    {
+      _DBUS_SET_OOM (error);
+      return NULL;
+    }
+
+#ifndef DBUS_DISABLE_CHECKS
+  _dbus_assert (!connection->have_connection_lock);
+#endif
+  return connection;
+}
+
+/*
+ * If the shared parameter is true, then any existing connection will
+ * be used (and if a new connection is created, it will be available
+ * for use by others). If the shared parameter is false, a new
+ * connection will always be created, and the new connection will
+ * never be returned to other callers.
+ *
+ * @param address the address
+ * @param shared whether the connection is shared or private
+ * @param error error return
+ * @returns the connection or #NULL on error
+ */
+static DBusConnection*
+_dbus_connection_open_internal (const char     *address,
+                                dbus_bool_t     shared,
+                                DBusError      *error)
+{
+  DBusConnection *connection;
+  DBusAddressEntry **entries;
+  DBusError tmp_error = DBUS_ERROR_INIT;
+  DBusError first_error = DBUS_ERROR_INIT;
+  int len, i;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  _dbus_verbose ("opening %s connection to: %s\n",
+                 shared ? "shared" : "private", address);
+  
+  if (!dbus_parse_address (address, &entries, &len, error))
+    return NULL;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  connection = NULL;
+
+  for (i = 0; i < len; i++)
+    {
+      if (shared)
+        {
+          if (!connection_lookup_shared (entries[i], &connection))
+            _DBUS_SET_OOM (&tmp_error);
+        }
+
+      if (connection == NULL)
+        {
+          connection = connection_try_from_address_entry (entries[i],
+                                                          &tmp_error);
+
+          if (connection != NULL && shared)
+            {
+              const char *guid;
+                  
+              connection->shareable = TRUE;
+                  
+              /* guid may be NULL */
+              guid = dbus_address_entry_get_value (entries[i], "guid");
+                  
+              CONNECTION_LOCK (connection);
+          
+              if (!connection_record_shared_unlocked (connection, guid))
+                {
+                  _DBUS_SET_OOM (&tmp_error);
+                  _dbus_connection_close_possibly_shared_and_unlock (connection);
+                  dbus_connection_unref (connection);
+                  connection = NULL;
+                }
+              else
+                CONNECTION_UNLOCK (connection);
+            }
+        }
+      
+      if (connection)
+        break;
+
+      _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
+      
+      if (i == 0)
+        dbus_move_error (&tmp_error, &first_error);
+      else
+        dbus_error_free (&tmp_error);
+    }
+  
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
+  
+  if (connection == NULL)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (&first_error);
+      dbus_move_error (&first_error, error);
+    }
+  else
+    dbus_error_free (&first_error);
+  
+  dbus_address_entries_free (entries);
+  return connection;
+}
+
+/**
+ * Closes a shared OR private connection, while dbus_connection_close() can
+ * only be used on private connections. Should only be called by the
+ * dbus code that owns the connection - an owner must be known,
+ * the open/close state is like malloc/free, not like ref/unref.
+ * 
+ * @param connection the connection
+ */
+void
+_dbus_connection_close_possibly_shared (DBusConnection *connection)
+{
+  _dbus_assert (connection != NULL);
+  _dbus_assert (connection->generation == _dbus_current_generation);
+
+  CONNECTION_LOCK (connection);
+  _dbus_connection_close_possibly_shared_and_unlock (connection);
+}
+
+static DBusPreallocatedSend*
+_dbus_connection_preallocate_send_unlocked (DBusConnection *connection)
+{
+  DBusPreallocatedSend *preallocated;
+
+  HAVE_LOCK_CHECK (connection);
+  
+  _dbus_assert (connection != NULL);
+  
+  preallocated = dbus_new (DBusPreallocatedSend, 1);
+  if (preallocated == NULL)
+    return NULL;
+
+  if (connection->link_cache != NULL)
+    {
+      preallocated->queue_link =
+        _dbus_list_pop_first_link (&connection->link_cache);
+      preallocated->queue_link->data = NULL;
+    }
+  else
+    {
+      preallocated->queue_link = _dbus_list_alloc_link (NULL);
+      if (preallocated->queue_link == NULL)
+        goto failed_0;
+    }
+  
+  if (connection->link_cache != NULL)
+    {
+      preallocated->counter_link =
+        _dbus_list_pop_first_link (&connection->link_cache);
+      preallocated->counter_link->data = connection->outgoing_counter;
+    }
+  else
+    {
+      preallocated->counter_link = _dbus_list_alloc_link (connection->outgoing_counter);
+      if (preallocated->counter_link == NULL)
+        goto failed_1;
+    }
+
+  _dbus_counter_ref (preallocated->counter_link->data);
+
+  preallocated->connection = connection;
+  
+  return preallocated;
+  
+ failed_1:
+  _dbus_list_free_link (preallocated->queue_link);
+ failed_0:
+  dbus_free (preallocated);
+  
+  return NULL;
+}
+
+/* Called with lock held, does not update dispatch status */
+static void
+_dbus_connection_send_preallocated_unlocked_no_update (DBusConnection       *connection,
+                                                       DBusPreallocatedSend *preallocated,
+                                                       DBusMessage          *message,
+                                                       dbus_uint32_t        *client_serial)
+{
+  dbus_uint32_t serial;
+  const char *sig;
+
+  preallocated->queue_link->data = message;
+  _dbus_list_prepend_link (&connection->outgoing_messages,
+                           preallocated->queue_link);
+
+  _dbus_message_add_size_counter_link (message,
+                                       preallocated->counter_link);
+
+  dbus_free (preallocated);
+  preallocated = NULL;
+  
+  dbus_message_ref (message);
+  
+  connection->n_outgoing += 1;
+
+  sig = dbus_message_get_signature (message);
+  
+  _dbus_verbose ("Message %p (%d %s %s %s '%s') for %s added to outgoing queue %p, %d pending to send\n",
+                 message,
+                 dbus_message_get_type (message),
+                 dbus_message_get_path (message) ?
+                 dbus_message_get_path (message) :
+                 "no path",
+                 dbus_message_get_interface (message) ?
+                 dbus_message_get_interface (message) :
+                 "no interface",
+                 dbus_message_get_member (message) ?
+                 dbus_message_get_member (message) :
+                 "no member",
+                 sig,
+                 dbus_message_get_destination (message) ?
+                 dbus_message_get_destination (message) :
+                 "null",
+                 connection,
+                 connection->n_outgoing);
+
+  if (dbus_message_get_serial (message) == 0)
+    {
+      serial = _dbus_connection_get_next_client_serial (connection);
+      dbus_message_set_serial (message, serial);
+      if (client_serial)
+        *client_serial = serial;
+    }
+  else
+    {
+      if (client_serial)
+        *client_serial = dbus_message_get_serial (message);
+    }
+
+  _dbus_verbose ("Message %p serial is %u\n",
+                 message, dbus_message_get_serial (message));
+  
+  dbus_message_lock (message);
+
+  /* Now we need to run an iteration to hopefully just write the messages
+   * out immediately, and otherwise get them queued up
+   */
+  _dbus_connection_do_iteration_unlocked (connection,
+                                          DBUS_ITERATION_DO_WRITING,
+                                          -1);
+
+  /* If stuff is still queued up, be sure we wake up the main loop */
+  if (connection->n_outgoing > 0)
+    _dbus_connection_wakeup_mainloop (connection);
+}
+
+static void
+_dbus_connection_send_preallocated_and_unlock (DBusConnection       *connection,
+                                              DBusPreallocatedSend *preallocated,
+                                              DBusMessage          *message,
+                                              dbus_uint32_t        *client_serial)
+{
+  DBusDispatchStatus status;
+
+  HAVE_LOCK_CHECK (connection);
+  
+  _dbus_connection_send_preallocated_unlocked_no_update (connection,
+                                                         preallocated,
+                                                         message, client_serial);
+
+  _dbus_verbose ("%s middle\n", _DBUS_FUNCTION_NAME);
+  status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+  /* this calls out to user code */
+  _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+}
+
+/**
+ * Like dbus_connection_send(), but assumes the connection
+ * is already locked on function entry, and unlocks before returning.
+ *
+ * @param connection the connection
+ * @param message the message to send
+ * @param client_serial return location for client serial of sent message
+ * @returns #FALSE on out-of-memory
+ */
+dbus_bool_t
+_dbus_connection_send_and_unlock (DBusConnection *connection,
+                                 DBusMessage    *message,
+                                 dbus_uint32_t  *client_serial)
+{
+  DBusPreallocatedSend *preallocated;
+
+  _dbus_assert (connection != NULL);
+  _dbus_assert (message != NULL);
+  
+  preallocated = _dbus_connection_preallocate_send_unlocked (connection);
+  if (preallocated == NULL)
+    {
+      CONNECTION_UNLOCK (connection);
+      return FALSE;
+    }
+
+  _dbus_connection_send_preallocated_and_unlock (connection,
+                                                preallocated,
+                                                message,
+                                                client_serial);
+  return TRUE;
+}
+
+/**
+ * Used internally to handle the semantics of dbus_server_set_new_connection_function().
+ * If the new connection function does not ref the connection, we want to close it.
+ *
+ * A bit of a hack, probably the new connection function should have returned a value
+ * for whether to close, or should have had to close the connection itself if it
+ * didn't want it.
+ *
+ * But, this works OK as long as the new connection function doesn't do anything
+ * crazy like keep the connection around without ref'ing it.
+ *
+ * We have to lock the connection across refcount check and close in case
+ * the new connection function spawns a thread that closes and unrefs.
+ * In that case, if the app thread
+ * closes and unrefs first, we'll harmlessly close again; if the app thread
+ * still has the ref, we'll close and then the app will close harmlessly.
+ * If the app unrefs without closing, the app is broken since if the
+ * app refs from the new connection function it is supposed to also close.
+ *
+ * If we didn't atomically check the refcount and close with the lock held
+ * though, we could screw this up.
+ * 
+ * @param connection the connection
+ */
+void
+_dbus_connection_close_if_only_one_ref (DBusConnection *connection)
+{
+  CONNECTION_LOCK (connection);
+  
+  _dbus_assert (connection->refcount.value > 0);
+
+  if (connection->refcount.value == 1)
+    _dbus_connection_close_possibly_shared_and_unlock (connection);
+  else
+    CONNECTION_UNLOCK (connection);
+}
+
+
+/**
+ * When a function that blocks has been called with a timeout, and we
+ * run out of memory, the time to wait for memory is based on the
+ * timeout. If the caller was willing to block a long time we wait a
+ * relatively long time for memory, if they were only willing to block
+ * briefly then we retry for memory at a rapid rate.
+ *
+ * @timeout_milliseconds the timeout requested for blocking
+ */
+static void
+_dbus_memory_pause_based_on_timeout (int timeout_milliseconds)
+{
+  if (timeout_milliseconds == -1)
+    _dbus_sleep_milliseconds (1000);
+  else if (timeout_milliseconds < 100)
+    ; /* just busy loop */
+  else if (timeout_milliseconds <= 1000)
+    _dbus_sleep_milliseconds (timeout_milliseconds / 3);
+  else
+    _dbus_sleep_milliseconds (1000);
+}
+
+static DBusMessage *
+generate_local_error_message (dbus_uint32_t serial, 
+                              char *error_name, 
+                              char *error_msg)
+{
+  DBusMessage *message;
+  message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR);
+  if (!message)
+    goto out;
+
+  if (!dbus_message_set_error_name (message, error_name))
+    {
+      dbus_message_unref (message);
+      message = NULL;
+      goto out; 
+    }
+
+  dbus_message_set_no_reply (message, TRUE); 
+
+  if (!dbus_message_set_reply_serial (message,
+                                      serial))
+    {
+      dbus_message_unref (message);
+      message = NULL;
+      goto out;
+    }
+
+  if (error_msg != NULL)
+    {
+      DBusMessageIter iter;
+
+      dbus_message_iter_init_append (message, &iter);
+      if (!dbus_message_iter_append_basic (&iter,
+                                           DBUS_TYPE_STRING,
+                                           &error_msg))
+        {
+          dbus_message_unref (message);
+          message = NULL;
+         goto out;
+        }
+    }
+
+ out:
+  return message;
+}
+
+
+/* This is slightly strange since we can pop a message here without
+ * the dispatch lock.
+ */
+static DBusMessage*
+check_for_reply_unlocked (DBusConnection *connection,
+                          dbus_uint32_t   client_serial)
+{
+  DBusList *link;
+
+  HAVE_LOCK_CHECK (connection);
+  
+  link = _dbus_list_get_first_link (&connection->incoming_messages);
+
+  while (link != NULL)
+    {
+      DBusMessage *reply = link->data;
+
+      if (dbus_message_get_reply_serial (reply) == client_serial)
+       {
+         _dbus_list_remove_link (&connection->incoming_messages, link);
+         connection->n_incoming  -= 1;
+         return reply;
+       }
+      link = _dbus_list_get_next_link (&connection->incoming_messages, link);
+    }
+
+  return NULL;
+}
+
+static void
+connection_timeout_and_complete_all_pending_calls_unlocked (DBusConnection *connection)
+{
+   /* We can't iterate over the hash in the normal way since we'll be
+    * dropping the lock for each item. So we restart the
+    * iter each time as we drain the hash table.
+    */
+   
+   while (_dbus_hash_table_get_n_entries (connection->pending_replies) > 0)
+    {
+      DBusPendingCall *pending;
+      DBusHashIter iter;
+      
+      _dbus_hash_iter_init (connection->pending_replies, &iter);
+      _dbus_hash_iter_next (&iter);
+       
+      pending = _dbus_hash_iter_get_value (&iter);
+      _dbus_pending_call_ref_unlocked (pending);
+       
+      _dbus_pending_call_queue_timeout_error_unlocked (pending, 
+                                                       connection);
+      _dbus_connection_remove_timeout_unlocked (connection,
+                                                _dbus_pending_call_get_timeout_unlocked (pending));
+      _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE);       
+      _dbus_hash_iter_remove_entry (&iter);
+
+      _dbus_pending_call_unref_and_unlock (pending);
+      CONNECTION_LOCK (connection);
+    }
+  HAVE_LOCK_CHECK (connection);
+}
+
+static void
+complete_pending_call_and_unlock (DBusConnection  *connection,
+                                  DBusPendingCall *pending,
+                                  DBusMessage     *message)
+{
+  _dbus_pending_call_set_reply_unlocked (pending, message);
+  _dbus_pending_call_ref_unlocked (pending); /* in case there's no app with a ref held */
+  _dbus_connection_detach_pending_call_and_unlock (connection, pending);
+  /* Must be called unlocked since it invokes app callback */
+  _dbus_pending_call_complete (pending);
+  dbus_pending_call_unref (pending);
+}
+
+static dbus_bool_t
+check_for_reply_and_update_dispatch_unlocked (DBusConnection  *connection,
+                                              DBusPendingCall *pending)
+{
+  DBusMessage *reply;
+  DBusDispatchStatus status;
+
+  reply = check_for_reply_unlocked (connection, 
+                                    _dbus_pending_call_get_reply_serial_unlocked (pending));
+  if (reply != NULL)
+    {
+      _dbus_verbose ("%s checked for reply\n", _DBUS_FUNCTION_NAME);
+
+      _dbus_verbose ("dbus_connection_send_with_reply_and_block(): got reply\n");
+
+      complete_pending_call_and_unlock (connection, pending, reply);
+      dbus_message_unref (reply);
+
+      CONNECTION_LOCK (connection);
+      status = _dbus_connection_get_dispatch_status_unlocked (connection);
+      _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+      dbus_pending_call_unref (pending);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+/**
+ * Blocks until a pending call times out or gets a reply.
+ *
+ * Does not re-enter the main loop or run filter/path-registered
+ * callbacks. The reply to the message will not be seen by
+ * filter callbacks.
+ *
+ * Returns immediately if pending call already got a reply.
+ * 
+ * @todo could use performance improvements (it keeps scanning
+ * the whole message queue for example)
+ *
+ * @param pending the pending call we block for a reply on
+ */
+void
+_dbus_connection_block_pending_call (DBusPendingCall *pending)
+{
+  long start_tv_sec, start_tv_usec;
+  long end_tv_sec, end_tv_usec;
+  long tv_sec, tv_usec;
+  DBusDispatchStatus status;
+  DBusConnection *connection;
+  dbus_uint32_t client_serial;
+  int timeout_milliseconds;
+
+  _dbus_assert (pending != NULL);
+
+  if (dbus_pending_call_get_completed (pending))
+    return;
+
+  dbus_pending_call_ref (pending); /* necessary because the call could be canceled */
+
+  connection = _dbus_pending_call_get_connection_and_lock (pending);
+  
+  /* Flush message queue - note, can affect dispatch status */
+  _dbus_connection_flush_unlocked (connection);
+
+  client_serial = _dbus_pending_call_get_reply_serial_unlocked (pending);
+
+  /* note that timeout_milliseconds is limited to a smallish value
+   * in _dbus_pending_call_new() so overflows aren't possible
+   * below
+   */
+  timeout_milliseconds = dbus_timeout_get_interval (_dbus_pending_call_get_timeout_unlocked (pending));
+  
+  _dbus_get_current_time (&start_tv_sec, &start_tv_usec);
+  end_tv_sec = start_tv_sec + timeout_milliseconds / 1000;
+  end_tv_usec = start_tv_usec + (timeout_milliseconds % 1000) * 1000;
+  end_tv_sec += end_tv_usec / _DBUS_USEC_PER_SECOND;
+  end_tv_usec = end_tv_usec % _DBUS_USEC_PER_SECOND;
+
+  _dbus_verbose ("dbus_connection_send_with_reply_and_block(): will block %d milliseconds for reply serial %u from %ld sec %ld usec to %ld sec %ld usec\n",
+                 timeout_milliseconds,
+                 client_serial,
+                 start_tv_sec, start_tv_usec,
+                 end_tv_sec, end_tv_usec);
+
+  /* check to see if we already got the data off the socket */
+  /* from another blocked pending call */
+  if (check_for_reply_and_update_dispatch_unlocked (connection, pending))
+    return;
+
+  /* Now we wait... */
+  /* always block at least once as we know we don't have the reply yet */
+  _dbus_connection_do_iteration_unlocked (connection,
+                                          DBUS_ITERATION_DO_READING |
+                                          DBUS_ITERATION_BLOCK,
+                                          timeout_milliseconds);
+
+ recheck_status:
+
+  _dbus_verbose ("%s top of recheck\n", _DBUS_FUNCTION_NAME);
+  
+  HAVE_LOCK_CHECK (connection);
+  
+  /* queue messages and get status */
+
+  status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+  /* the get_completed() is in case a dispatch() while we were blocking
+   * got the reply instead of us.
+   */
+  if (_dbus_pending_call_get_completed_unlocked (pending))
+    {
+      _dbus_verbose ("Pending call completed by dispatch in %s\n", _DBUS_FUNCTION_NAME);
+      _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+      dbus_pending_call_unref (pending);
+      return;
+    }
+  
+  if (status == DBUS_DISPATCH_DATA_REMAINS)
+    {
+      if (check_for_reply_and_update_dispatch_unlocked (connection, pending))
+        return;
+    }
+  
+  _dbus_get_current_time (&tv_sec, &tv_usec);
+  
+  if (!_dbus_connection_get_is_connected_unlocked (connection))
+    {
+      DBusMessage *error_msg;
+
+      error_msg = generate_local_error_message (client_serial,
+                                                DBUS_ERROR_DISCONNECTED, 
+                                                "Connection was disconnected before a reply was received"); 
+
+      /* on OOM error_msg is set to NULL */
+      complete_pending_call_and_unlock (connection, pending, error_msg);
+      dbus_pending_call_unref (pending);
+      return;
+    }
+  else if (tv_sec < start_tv_sec)
+    _dbus_verbose ("dbus_connection_send_with_reply_and_block(): clock set backward\n");
+  else if (connection->disconnect_message_link == NULL)
+    _dbus_verbose ("dbus_connection_send_with_reply_and_block(): disconnected\n");
+  else if (tv_sec < end_tv_sec ||
+           (tv_sec == end_tv_sec && tv_usec < end_tv_usec))
+    {
+      timeout_milliseconds = (end_tv_sec - tv_sec) * 1000 +
+        (end_tv_usec - tv_usec) / 1000;
+      _dbus_verbose ("dbus_connection_send_with_reply_and_block(): %d milliseconds remain\n", timeout_milliseconds);
+      _dbus_assert (timeout_milliseconds >= 0);
+      
+      if (status == DBUS_DISPATCH_NEED_MEMORY)
+        {
+          /* Try sleeping a bit, as we aren't sure we need to block for reading,
+           * we may already have a reply in the buffer and just can't process
+           * it.
+           */
+          _dbus_verbose ("dbus_connection_send_with_reply_and_block() waiting for more memory\n");
+
+          _dbus_memory_pause_based_on_timeout (timeout_milliseconds);
+        }
+      else
+        {          
+          /* block again, we don't have the reply buffered yet. */
+          _dbus_connection_do_iteration_unlocked (connection,
+                                                  DBUS_ITERATION_DO_READING |
+                                                  DBUS_ITERATION_BLOCK,
+                                                  timeout_milliseconds);
+        }
+
+      goto recheck_status;
+    }
+
+  _dbus_verbose ("dbus_connection_send_with_reply_and_block(): Waited %ld milliseconds and got no reply\n",
+                 (tv_sec - start_tv_sec) * 1000 + (tv_usec - start_tv_usec) / 1000);
+
+  _dbus_assert (!_dbus_pending_call_get_completed_unlocked (pending));
+  
+  /* unlock and call user code */
+  complete_pending_call_and_unlock (connection, pending, NULL);
+
+  /* update user code on dispatch status */
+  CONNECTION_LOCK (connection);
+  status = _dbus_connection_get_dispatch_status_unlocked (connection);
+  _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+  dbus_pending_call_unref (pending);
+}
+
+/** @} */
+
+/**
+ * @addtogroup DBusConnection
+ *
+ * @{
+ */
+
+/**
+ * Gets a connection to a remote address. If a connection to the given
+ * address already exists, returns the existing connection with its
+ * reference count incremented.  Otherwise, returns a new connection
+ * and saves the new connection for possible re-use if a future call
+ * to dbus_connection_open() asks to connect to the same server.
+ *
+ * Use dbus_connection_open_private() to get a dedicated connection
+ * not shared with other callers of dbus_connection_open().
+ *
+ * If the open fails, the function returns #NULL, and provides a
+ * reason for the failure in the error parameter. Pass #NULL for the
+ * error parameter if you aren't interested in the reason for
+ * failure.
+ *
+ * Because this connection is shared, no user of the connection
+ * may call dbus_connection_close(). However, when you are done with the
+ * connection you should call dbus_connection_unref().
+ *
+ * @note Prefer dbus_connection_open() to dbus_connection_open_private()
+ * unless you have good reason; connections are expensive enough
+ * that it's wasteful to create lots of connections to the same
+ * server.
+ * 
+ * @param address the address.
+ * @param error address where an error can be returned.
+ * @returns new connection, or #NULL on failure.
+ */
+DBusConnection*
+dbus_connection_open (const char     *address,
+                      DBusError      *error)
+{
+  DBusConnection *connection;
+
+  _dbus_return_val_if_fail (address != NULL, NULL);
+  _dbus_return_val_if_error_is_set (error, NULL);
+
+  connection = _dbus_connection_open_internal (address,
+                                               TRUE,
+                                               error);
+
+  return connection;
+}
+
+/**
+ * Opens a new, dedicated connection to a remote address. Unlike
+ * dbus_connection_open(), always creates a new connection.
+ * This connection will not be saved or recycled by libdbus.
+ *
+ * If the open fails, the function returns #NULL, and provides a
+ * reason for the failure in the error parameter. Pass #NULL for the
+ * error parameter if you aren't interested in the reason for
+ * failure.
+ *
+ * When you are done with this connection, you must
+ * dbus_connection_close() to disconnect it,
+ * and dbus_connection_unref() to free the connection object.
+ * 
+ * (The dbus_connection_close() can be skipped if the
+ * connection is already known to be disconnected, for example
+ * if you are inside a handler for the Disconnected signal.)
+ *
+ * @note Prefer dbus_connection_open() to dbus_connection_open_private()
+ * unless you have good reason; connections are expensive enough
+ * that it's wasteful to create lots of connections to the same
+ * server.
+ *
+ * @param address the address.
+ * @param error address where an error can be returned.
+ * @returns new connection, or #NULL on failure.
+ */
+DBusConnection*
+dbus_connection_open_private (const char     *address,
+                              DBusError      *error)
+{
+  DBusConnection *connection;
+
+  _dbus_return_val_if_fail (address != NULL, NULL);
+  _dbus_return_val_if_error_is_set (error, NULL);
+
+  connection = _dbus_connection_open_internal (address,
+                                               FALSE,
+                                               error);
+
+  return connection;
+}
+
+/**
+ * Increments the reference count of a DBusConnection.
+ *
+ * @param connection the connection.
+ * @returns the connection.
+ */
+DBusConnection *
+dbus_connection_ref (DBusConnection *connection)
+{
+  _dbus_return_val_if_fail (connection != NULL, NULL);
+  _dbus_return_val_if_fail (connection->generation == _dbus_current_generation, NULL);
+  
+  /* The connection lock is better than the global
+   * lock in the atomic increment fallback
+   */
+  
+#ifdef DBUS_HAVE_ATOMIC_INT
+  _dbus_atomic_inc (&connection->refcount);
+#else
+  CONNECTION_LOCK (connection);
+  _dbus_assert (connection->refcount.value > 0);
+
+  connection->refcount.value += 1;
+  CONNECTION_UNLOCK (connection);
+#endif
+
+  return connection;
+}
+
+static void
+free_outgoing_message (void *element,
+                       void *data)
+{
+  DBusMessage *message = element;
+  DBusConnection *connection = data;
+
+  _dbus_message_remove_size_counter (message,
+                                     connection->outgoing_counter,
+                                     NULL);
+  dbus_message_unref (message);
+}
+
+/* This is run without the mutex held, but after the last reference
+ * to the connection has been dropped we should have no thread-related
+ * problems
+ */
+static void
+_dbus_connection_last_unref (DBusConnection *connection)
+{
+  DBusList *link;
+
+  _dbus_verbose ("Finalizing connection %p\n", connection);
+  
+  _dbus_assert (connection->refcount.value == 0);
+  
+  /* You have to disconnect the connection before unref:ing it. Otherwise
+   * you won't get the disconnected message.
+   */
+  _dbus_assert (!_dbus_transport_get_is_connected (connection->transport));
+  _dbus_assert (connection->server_guid == NULL);
+  
+  /* ---- We're going to call various application callbacks here, hope it doesn't break anything... */
+  _dbus_object_tree_free_all_unlocked (connection->objects);
+  
+  dbus_connection_set_dispatch_status_function (connection, NULL, NULL, NULL);
+  dbus_connection_set_wakeup_main_function (connection, NULL, NULL, NULL);
+  dbus_connection_set_unix_user_function (connection, NULL, NULL, NULL);
+  
+  _dbus_watch_list_free (connection->watches);
+  connection->watches = NULL;
+  
+  _dbus_timeout_list_free (connection->timeouts);
+  connection->timeouts = NULL;
+
+  _dbus_data_slot_list_free (&connection->slot_list);
+  
+  link = _dbus_list_get_first_link (&connection->filter_list);
+  while (link != NULL)
+    {
+      DBusMessageFilter *filter = link->data;
+      DBusList *next = _dbus_list_get_next_link (&connection->filter_list, link);
+
+      filter->function = NULL;
+      _dbus_message_filter_unref (filter); /* calls app callback */
+      link->data = NULL;
+      
+      link = next;
+    }
+  _dbus_list_clear (&connection->filter_list);
+  
+  /* ---- Done with stuff that invokes application callbacks */
+
+  _dbus_object_tree_unref (connection->objects);  
+
+  _dbus_hash_table_unref (connection->pending_replies);
+  connection->pending_replies = NULL;
+  
+  _dbus_list_clear (&connection->filter_list);
+  
+  _dbus_list_foreach (&connection->outgoing_messages,
+                      free_outgoing_message,
+                     connection);
+  _dbus_list_clear (&connection->outgoing_messages);
+  
+  _dbus_list_foreach (&connection->incoming_messages,
+                     (DBusForeachFunction) dbus_message_unref,
+                     NULL);
+  _dbus_list_clear (&connection->incoming_messages);
+
+  _dbus_counter_unref (connection->outgoing_counter);
+
+  _dbus_transport_unref (connection->transport);
+
+  if (connection->disconnect_message_link)
+    {
+      DBusMessage *message = connection->disconnect_message_link->data;
+      dbus_message_unref (message);
+      _dbus_list_free_link (connection->disconnect_message_link);
+    }
+
+  _dbus_list_clear (&connection->link_cache);
+  
+  _dbus_condvar_free_at_location (&connection->dispatch_cond);
+  _dbus_condvar_free_at_location (&connection->io_path_cond);
+
+  _dbus_mutex_free_at_location (&connection->io_path_mutex);
+  _dbus_mutex_free_at_location (&connection->dispatch_mutex);
+
+  _dbus_mutex_free_at_location (&connection->mutex);
+  
+  dbus_free (connection);
+}
+
+/**
+ * Decrements the reference count of a DBusConnection, and finalizes
+ * it if the count reaches zero.
+ *
+ * Note: it is a bug to drop the last reference to a connection that
+ * is still connected.
+ *
+ * For shared connections, libdbus will own a reference
+ * as long as the connection is connected, so you can know that either
+ * you don't have the last reference, or it's OK to drop the last reference.
+ * Most connections are shared. dbus_connection_open() and dbus_bus_get()
+ * return shared connections.
+ *
+ * For private connections, the creator of the connection must arrange for
+ * dbus_connection_close() to be called prior to dropping the last reference.
+ * Private connections come from dbus_connection_open_private() or dbus_bus_get_private().
+ *
+ * @param connection the connection.
+ */
+void
+dbus_connection_unref (DBusConnection *connection)
+{
+  dbus_bool_t last_unref;
+
+  _dbus_return_if_fail (connection != NULL);
+  _dbus_return_if_fail (connection->generation == _dbus_current_generation);
+  
+  /* The connection lock is better than the global
+   * lock in the atomic increment fallback
+   */
+  
+#ifdef DBUS_HAVE_ATOMIC_INT
+  last_unref = (_dbus_atomic_dec (&connection->refcount) == 1);
+#else
+  CONNECTION_LOCK (connection);
+  
+  _dbus_assert (connection->refcount.value > 0);
+
+  connection->refcount.value -= 1;
+  last_unref = (connection->refcount.value == 0);
+
+#if 0
+  printf ("unref() connection %p count = %d\n", connection, connection->refcount.value);
+#endif
+  
+  CONNECTION_UNLOCK (connection);
+#endif
+  
+  if (last_unref)
+    {
+#ifndef DBUS_DISABLE_CHECKS
+      if (_dbus_transport_get_is_connected (connection->transport))
+        {
+          _dbus_warn_check_failed ("The last reference on a connection was dropped without closing the connection. This is a bug in an application. See dbus_connection_unref() documentation for details.\n%s",
+                                   connection->shareable ?
+                                   "Most likely, the application called unref() too many times and removed a reference belonging to libdbus, since this is a shared connection.\n" : 
+                                    "Most likely, the application was supposed to call dbus_connection_close(), since this is a private connection.\n");
+          return;
+        }
+#endif
+      _dbus_connection_last_unref (connection);
+    }
+}
+
+/*
+ * Note that the transport can disconnect itself (other end drops us)
+ * and in that case this function never runs. So this function must
+ * not do anything more than disconnect the transport and update the
+ * dispatch status.
+ * 
+ * If the transport self-disconnects, then we assume someone will
+ * dispatch the connection to cause the dispatch status update.
+ */
+static void
+_dbus_connection_close_possibly_shared_and_unlock (DBusConnection *connection)
+{
+  DBusDispatchStatus status;
+
+  HAVE_LOCK_CHECK (connection);
+  
+  _dbus_verbose ("Disconnecting %p\n", connection);
+
+  /* We need to ref because update_dispatch_status_and_unlock will unref
+   * the connection if it was shared and libdbus was the only remaining
+   * refcount holder.
+   */
+  _dbus_connection_ref_unlocked (connection);
+  
+  _dbus_transport_disconnect (connection->transport);
+
+  /* This has the side effect of queuing the disconnect message link
+   * (unless we don't have enough memory, possibly, so don't assert it).
+   * After the disconnect message link is queued, dbus_bus_get/dbus_connection_open
+   * should never again return the newly-disconnected connection.
+   *
+   * However, we only unref the shared connection and exit_on_disconnect when
+   * the disconnect message reaches the head of the message queue,
+   * NOT when it's first queued.
+   */
+  status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+  /* This calls out to user code */
+  _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+
+  /* Could also call out to user code */
+  dbus_connection_unref (connection);
+}
+
+/**
+ * Closes a private connection, so no further data can be sent or received.
+ * This disconnects the transport (such as a socket) underlying the
+ * connection.
+ *
+ * Attempts to send messages after closing a connection are safe, but will result in
+ * error replies generated locally in libdbus.
+ * 
+ * This function does not affect the connection's reference count.  It's
+ * safe to close a connection more than once; all calls after the
+ * first do nothing. It's impossible to "reopen" a connection, a
+ * new connection must be created. This function may result in a call
+ * to the DBusDispatchStatusFunction set with
+ * dbus_connection_set_dispatch_status_function(), as the disconnect
+ * message it generates needs to be dispatched.
+ *
+ * If a connection is dropped by the remote application, it will
+ * close itself. 
+ * 
+ * You must close a connection prior to releasing the last reference to
+ * the connection. If you dbus_connection_unref() for the last time
+ * without closing the connection, the results are undefined; it
+ * is a bug in your program and libdbus will try to print a warning.
+ *
+ * You may not close a shared connection. Connections created with
+ * dbus_connection_open() or dbus_bus_get() are shared.
+ * These connections are owned by libdbus, and applications should
+ * only unref them, never close them. Applications can know it is
+ * safe to unref these connections because libdbus will be holding a
+ * reference as long as the connection is open. Thus, either the
+ * connection is closed and it is OK to drop the last reference,
+ * or the connection is open and the app knows it does not have the
+ * last reference.
+ *
+ * Connections created with dbus_connection_open_private() or
+ * dbus_bus_get_private() are not kept track of or referenced by
+ * libdbus. The creator of these connections is responsible for
+ * calling dbus_connection_close() prior to releasing the last
+ * reference, if the connection is not already disconnected.
+ *
+ * @param connection the private (unshared) connection to close
+ */
+void
+dbus_connection_close (DBusConnection *connection)
+{
+  _dbus_return_if_fail (connection != NULL);
+  _dbus_return_if_fail (connection->generation == _dbus_current_generation);
+
+  CONNECTION_LOCK (connection);
+
+#ifndef DBUS_DISABLE_CHECKS
+  if (connection->shareable)
+    {
+      CONNECTION_UNLOCK (connection);
+
+      _dbus_warn_check_failed ("Applications must not close shared connections - see dbus_connection_close() docs. This is a bug in the application.\n");
+      return;
+    }
+#endif
+  
+  _dbus_connection_close_possibly_shared_and_unlock (connection);
+}
+
+static dbus_bool_t
+_dbus_connection_get_is_connected_unlocked (DBusConnection *connection)
+{
+  HAVE_LOCK_CHECK (connection);
+  return _dbus_transport_get_is_connected (connection->transport);
+}
+
+/**
+ * Gets whether the connection is currently open.  A connection may
+ * become disconnected when the remote application closes its end, or
+ * exits; a connection may also be disconnected with
+ * dbus_connection_close().
+ * 
+ * There are not separate states for "closed" and "disconnected," the two
+ * terms are synonymous. This function should really be called
+ * get_is_open() but for historical reasons is not.
+ *
+ * @param connection the connection.
+ * @returns #TRUE if the connection is still alive.
+ */
+dbus_bool_t
+dbus_connection_get_is_connected (DBusConnection *connection)
+{
+  dbus_bool_t res;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  
+  CONNECTION_LOCK (connection);
+  res = _dbus_connection_get_is_connected_unlocked (connection);
+  CONNECTION_UNLOCK (connection);
+  
+  return res;
+}
+
+/**
+ * Gets whether the connection was authenticated. (Note that
+ * if the connection was authenticated then disconnected,
+ * this function still returns #TRUE)
+ *
+ * @param connection the connection
+ * @returns #TRUE if the connection was ever authenticated
+ */
+dbus_bool_t
+dbus_connection_get_is_authenticated (DBusConnection *connection)
+{
+  dbus_bool_t res;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  
+  CONNECTION_LOCK (connection);
+  res = _dbus_transport_get_is_authenticated (connection->transport);
+  CONNECTION_UNLOCK (connection);
+  
+  return res;
+}
+
+/**
+ * Gets whether the connection is not authenticated as a specific
+ * user.  If the connection is not authenticated, this function
+ * returns #TRUE, and if it is authenticated but as an anonymous user,
+ * it returns #TRUE.  If it is authenticated as a specific user, then
+ * this returns #FALSE. (Note that if the connection was authenticated
+ * as anonymous then disconnected, this function still returns #TRUE.)
+ *
+ * If the connection is not anonymous, you can use
+ * dbus_connection_get_unix_user() and
+ * dbus_connection_get_windows_user() to see who it's authorized as.
+ *
+ * If you want to prevent non-anonymous authorization, use
+ * dbus_server_set_auth_mechanisms() to remove the mechanisms that
+ * allow proving user identity (i.e. only allow the ANONYMOUS
+ * mechanism).
+ * 
+ * @param connection the connection
+ * @returns #TRUE if not authenticated or authenticated as anonymous 
+ */
+dbus_bool_t
+dbus_connection_get_is_anonymous (DBusConnection *connection)
+{
+  dbus_bool_t res;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  
+  CONNECTION_LOCK (connection);
+  res = _dbus_transport_get_is_anonymous (connection->transport);
+  CONNECTION_UNLOCK (connection);
+  
+  return res;
+}
+
+/**
+ * Gets the ID of the server address we are authenticated to, if this
+ * connection is on the client side. If the connection is on the
+ * server side, this will always return #NULL - use dbus_server_get_id()
+ * to get the ID of your own server, if you are the server side.
+ * 
+ * If a client-side connection is not authenticated yet, the ID may be
+ * available if it was included in the server address, but may not be
+ * available. The only way to be sure the server ID is available
+ * is to wait for authentication to complete.
+ *
+ * In general, each mode of connecting to a given server will have
+ * its own ID. So for example, if the session bus daemon is listening
+ * on UNIX domain sockets and on TCP, then each of those modalities
+ * will have its own server ID.
+ *
+ * If you want an ID that identifies an entire session bus, look at
+ * dbus_bus_get_id() instead (which is just a convenience wrapper
+ * around the org.freedesktop.DBus.GetId method invoked on the bus).
+ *
+ * You can also get a machine ID; see dbus_get_local_machine_id() to
+ * get the machine you are on.  There isn't a convenience wrapper, but
+ * you can invoke org.freedesktop.DBus.Peer.GetMachineId on any peer
+ * to get the machine ID on the other end.
+ * 
+ * The D-Bus specification describes the server ID and other IDs in a
+ * bit more detail.
+ *
+ * @param connection the connection
+ * @returns the server ID or #NULL if no memory or the connection is server-side
+ */
+char*
+dbus_connection_get_server_id (DBusConnection *connection)
+{
+  char *id;
+
+  _dbus_return_val_if_fail (connection != NULL, NULL);
+  
+  CONNECTION_LOCK (connection);
+  id = _dbus_strdup (_dbus_transport_get_server_id (connection->transport));
+  CONNECTION_UNLOCK (connection);
+  
+  return id;
+}
+
+/**
+ * Set whether _exit() should be called when the connection receives a
+ * disconnect signal. The call to _exit() comes after any handlers for
+ * the disconnect signal run; handlers can cancel the exit by calling
+ * this function.
+ *
+ * By default, exit_on_disconnect is #FALSE; but for message bus
+ * connections returned from dbus_bus_get() it will be toggled on
+ * by default.
+ *
+ * @param connection the connection
+ * @param exit_on_disconnect #TRUE if _exit() should be called after a disconnect signal
+ */
+void
+dbus_connection_set_exit_on_disconnect (DBusConnection *connection,
+                                        dbus_bool_t     exit_on_disconnect)
+{
+  _dbus_return_if_fail (connection != NULL);
+
+  CONNECTION_LOCK (connection);
+  connection->exit_on_disconnect = exit_on_disconnect != FALSE;
+  CONNECTION_UNLOCK (connection);
+}
+
+/**
+ * Preallocates resources needed to send a message, allowing the message 
+ * to be sent without the possibility of memory allocation failure.
+ * Allows apps to create a future guarantee that they can send
+ * a message regardless of memory shortages.
+ *
+ * @param connection the connection we're preallocating for.
+ * @returns the preallocated resources, or #NULL
+ */
+DBusPreallocatedSend*
+dbus_connection_preallocate_send (DBusConnection *connection)
+{
+  DBusPreallocatedSend *preallocated;
+
+  _dbus_return_val_if_fail (connection != NULL, NULL);
+
+  CONNECTION_LOCK (connection);
+  
+  preallocated =
+    _dbus_connection_preallocate_send_unlocked (connection);
+
+  CONNECTION_UNLOCK (connection);
+
+  return preallocated;
+}
+
+/**
+ * Frees preallocated message-sending resources from
+ * dbus_connection_preallocate_send(). Should only
+ * be called if the preallocated resources are not used
+ * to send a message.
+ *
+ * @param connection the connection
+ * @param preallocated the resources
+ */
+void
+dbus_connection_free_preallocated_send (DBusConnection       *connection,
+                                        DBusPreallocatedSend *preallocated)
+{
+  _dbus_return_if_fail (connection != NULL);
+  _dbus_return_if_fail (preallocated != NULL);  
+  _dbus_return_if_fail (connection == preallocated->connection);
+
+  _dbus_list_free_link (preallocated->queue_link);
+  _dbus_counter_unref (preallocated->counter_link->data);
+  _dbus_list_free_link (preallocated->counter_link);
+  dbus_free (preallocated);
+}
+
+/**
+ * Sends a message using preallocated resources. This function cannot fail.
+ * It works identically to dbus_connection_send() in other respects.
+ * Preallocated resources comes from dbus_connection_preallocate_send().
+ * This function "consumes" the preallocated resources, they need not
+ * be freed separately.
+ *
+ * @param connection the connection
+ * @param preallocated the preallocated resources
+ * @param message the message to send
+ * @param client_serial return location for client serial assigned to the message
+ */
+void
+dbus_connection_send_preallocated (DBusConnection       *connection,
+                                   DBusPreallocatedSend *preallocated,
+                                   DBusMessage          *message,
+                                   dbus_uint32_t        *client_serial)
+{
+  _dbus_return_if_fail (connection != NULL);
+  _dbus_return_if_fail (preallocated != NULL);
+  _dbus_return_if_fail (message != NULL);
+  _dbus_return_if_fail (preallocated->connection == connection);
+  _dbus_return_if_fail (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL ||
+                        dbus_message_get_member (message) != NULL);
+  _dbus_return_if_fail (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_SIGNAL ||
+                        (dbus_message_get_interface (message) != NULL &&
+                         dbus_message_get_member (message) != NULL));
+  
+  CONNECTION_LOCK (connection);
+  _dbus_connection_send_preallocated_and_unlock (connection,
+                                                preallocated,
+                                                message, client_serial);
+}
+
+static dbus_bool_t
+_dbus_connection_send_unlocked_no_update (DBusConnection *connection,
+                                          DBusMessage    *message,
+                                          dbus_uint32_t  *client_serial)
+{
+  DBusPreallocatedSend *preallocated;
+
+  _dbus_assert (connection != NULL);
+  _dbus_assert (message != NULL);
+  
+  preallocated = _dbus_connection_preallocate_send_unlocked (connection);
+  if (preallocated == NULL)
+    return FALSE;
+
+  _dbus_connection_send_preallocated_unlocked_no_update (connection,
+                                                         preallocated,
+                                                         message,
+                                                         client_serial);
+  return TRUE;
+}
+
+/**
+ * Adds a message to the outgoing message queue. Does not block to
+ * write the message to the network; that happens asynchronously. To
+ * force the message to be written, call dbus_connection_flush() however
+ * it is not necessary to call dbus_connection_flush() by hand; the 
+ * message will be sent the next time the main loop is run. 
+ * dbus_connection_flush() should only be used, for example, if
+ * the application was expected to exit before running the main loop.
+ *
+ * Because this only queues the message, the only reason it can
+ * fail is lack of memory. Even if the connection is disconnected,
+ * no error will be returned. If the function fails due to lack of memory, 
+ * it returns #FALSE. The function will never fail for other reasons; even 
+ * if the connection is disconnected, you can queue an outgoing message,
+ * though obviously it won't be sent.
+ *
+ * The message serial is used by the remote application to send a
+ * reply; see dbus_message_get_serial() or the D-Bus specification.
+ *
+ * dbus_message_unref() can be called as soon as this method returns
+ * as the message queue will hold its own ref until the message is sent.
+ * 
+ * @param connection the connection.
+ * @param message the message to write.
+ * @param serial return location for message serial, or #NULL if you don't care
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+dbus_connection_send (DBusConnection *connection,
+                      DBusMessage    *message,
+                      dbus_uint32_t  *serial)
+{
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+
+  CONNECTION_LOCK (connection);
+
+  return _dbus_connection_send_and_unlock (connection,
+                                          message,
+                                          serial);
+}
+
+static dbus_bool_t
+reply_handler_timeout (void *data)
+{
+  DBusConnection *connection;
+  DBusDispatchStatus status;
+  DBusPendingCall *pending = data;
+
+  connection = _dbus_pending_call_get_connection_and_lock (pending);
+
+  _dbus_pending_call_queue_timeout_error_unlocked (pending, 
+                                                   connection);
+  _dbus_connection_remove_timeout_unlocked (connection,
+                                           _dbus_pending_call_get_timeout_unlocked (pending));
+  _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE);
+
+  _dbus_verbose ("%s middle\n", _DBUS_FUNCTION_NAME);
+  status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+  /* Unlocks, and calls out to user code */
+  _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+  
+  return TRUE;
+}
+
+/**
+ * Queues a message to send, as with dbus_connection_send(),
+ * but also returns a #DBusPendingCall used to receive a reply to the
+ * message. If no reply is received in the given timeout_milliseconds,
+ * this function expires the pending reply and generates a synthetic
+ * error reply (generated in-process, not by the remote application)
+ * indicating that a timeout occurred.
+ *
+ * A #DBusPendingCall will see a reply message before any filters or
+ * registered object path handlers. See dbus_connection_dispatch() for
+ * details on when handlers are run.
+ *
+ * A #DBusPendingCall will always see exactly one reply message,
+ * unless it's cancelled with dbus_pending_call_cancel().
+ * 
+ * If #NULL is passed for the pending_return, the #DBusPendingCall
+ * will still be generated internally, and used to track
+ * the message reply timeout. This means a timeout error will
+ * occur if no reply arrives, unlike with dbus_connection_send().
+ *
+ * If -1 is passed for the timeout, a sane default timeout is used. -1
+ * is typically the best value for the timeout for this reason, unless
+ * you want a very short or very long timeout.  There is no way to
+ * avoid a timeout entirely, other than passing INT_MAX for the
+ * timeout to mean "very long timeout." libdbus clamps an INT_MAX
+ * timeout down to a few hours timeout though.
+ *
+ * @warning if the connection is disconnected, the #DBusPendingCall
+ * will be set to #NULL, so be careful with this.
+ * 
+ * @param connection the connection
+ * @param message the message to send
+ * @param pending_return return location for a #DBusPendingCall object, or #NULL if connection is disconnected
+ * @param timeout_milliseconds timeout in milliseconds or -1 for default
+ * @returns #FALSE if no memory, #TRUE otherwise.
+ *
+ */
+dbus_bool_t
+dbus_connection_send_with_reply (DBusConnection     *connection,
+                                 DBusMessage        *message,
+                                 DBusPendingCall   **pending_return,
+                                 int                 timeout_milliseconds)
+{
+  DBusPendingCall *pending;
+  dbus_int32_t serial = -1;
+  DBusDispatchStatus status;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE);
+
+  if (pending_return)
+    *pending_return = NULL;
+
+  CONNECTION_LOCK (connection);
+
+   if (!_dbus_connection_get_is_connected_unlocked (connection))
+    {
+      CONNECTION_UNLOCK (connection);
+
+      return TRUE;
+    }
+
+  pending = _dbus_pending_call_new_unlocked (connection,
+                                             timeout_milliseconds,
+                                             reply_handler_timeout);
+
+  if (pending == NULL)
+    {
+      CONNECTION_UNLOCK (connection);
+      return FALSE;
+    }
+
+  /* Assign a serial to the message */
+  serial = dbus_message_get_serial (message);
+  if (serial == 0)
+    {
+      serial = _dbus_connection_get_next_client_serial (connection);
+      dbus_message_set_serial (message, serial);
+    }
+
+  if (!_dbus_pending_call_set_timeout_error_unlocked (pending, message, serial))
+    goto error;
+    
+  /* Insert the serial in the pending replies hash;
+   * hash takes a refcount on DBusPendingCall.
+   * Also, add the timeout.
+   */
+  if (!_dbus_connection_attach_pending_call_unlocked (connection,
+                                                     pending))
+    goto error;
+  if (!_dbus_connection_send_unlocked_no_update (connection, message, NULL))
+    {
+      _dbus_connection_detach_pending_call_and_unlock (connection,
+                                                      pending);
+      goto error_unlocked;
+    }
+
+  if (pending_return)
+    *pending_return = pending; /* hand off refcount */
+  else
+    {
+      _dbus_connection_detach_pending_call_unlocked (connection, pending);
+      /* we still have a ref to the pending call in this case, we unref
+       * after unlocking, below
+       */
+    }
+
+  status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+  /* this calls out to user code */
+  _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+
+  if (pending_return == NULL)
+    dbus_pending_call_unref (pending);
+  
+  return TRUE;
+
+ error:
+  CONNECTION_UNLOCK (connection);
+ error_unlocked:
+  dbus_pending_call_unref (pending);
+  return FALSE;
+}
+
+/**
+ * Sends a message and blocks a certain time period while waiting for
+ * a reply.  This function does not reenter the main loop,
+ * i.e. messages other than the reply are queued up but not
+ * processed. This function is used to invoke method calls on a
+ * remote object.
+ * 
+ * If a normal reply is received, it is returned, and removed from the
+ * incoming message queue. If it is not received, #NULL is returned
+ * and the error is set to #DBUS_ERROR_NO_REPLY.  If an error reply is
+ * received, it is converted to a #DBusError and returned as an error,
+ * then the reply message is deleted and #NULL is returned. If
+ * something else goes wrong, result is set to whatever is
+ * appropriate, such as #DBUS_ERROR_NO_MEMORY or
+ * #DBUS_ERROR_DISCONNECTED.
+ *
+ * @warning While this function blocks the calling thread will not be
+ * processing the incoming message queue. This means you can end up
+ * deadlocked if the application you're talking to needs you to reply
+ * to a method. To solve this, either avoid the situation, block in a
+ * separate thread from the main connection-dispatching thread, or use
+ * dbus_pending_call_set_notify() to avoid blocking.
+ *
+ * @param connection the connection
+ * @param message the message to send
+ * @param timeout_milliseconds timeout in milliseconds or -1 for default
+ * @param error return location for error message
+ * @returns the message that is the reply or #NULL with an error code if the
+ * function fails.
+ */
+DBusMessage*
+dbus_connection_send_with_reply_and_block (DBusConnection     *connection,
+                                           DBusMessage        *message,
+                                           int                 timeout_milliseconds,
+                                           DBusError          *error)
+{
+  DBusMessage *reply;
+  DBusPendingCall *pending;
+  
+  _dbus_return_val_if_fail (connection != NULL, NULL);
+  _dbus_return_val_if_fail (message != NULL, NULL);
+  _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, NULL);
+  _dbus_return_val_if_error_is_set (error, NULL);
+  
+  if (!dbus_connection_send_with_reply (connection, message,
+                                        &pending, timeout_milliseconds))
+    {
+      _DBUS_SET_OOM (error);
+      return NULL;
+    }
+
+  if (pending == NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_DISCONNECTED, "Connection is closed");
+      return NULL;
+    }
+  
+  dbus_pending_call_block (pending);
+
+  reply = dbus_pending_call_steal_reply (pending);
+  dbus_pending_call_unref (pending);
+
+  /* call_complete_and_unlock() called from pending_call_block() should
+   * always fill this in.
+   */
+  _dbus_assert (reply != NULL);
+  
+   if (dbus_set_error_from_message (error, reply))
+    {
+      dbus_message_unref (reply);
+      return NULL;
+    }
+  else
+    return reply;
+}
+
+/**
+ * Blocks until the outgoing message queue is empty.
+ * Assumes connection lock already held.
+ *
+ * If you call this, you MUST call update_dispatch_status afterword...
+ * 
+ * @param connection the connection.
+ */
+static DBusDispatchStatus
+_dbus_connection_flush_unlocked (DBusConnection *connection)
+{
+  /* We have to specify DBUS_ITERATION_DO_READING here because
+   * otherwise we could have two apps deadlock if they are both doing
+   * a flush(), and the kernel buffers fill up. This could change the
+   * dispatch status.
+   */
+  DBusDispatchStatus status;
+
+  HAVE_LOCK_CHECK (connection);
+  
+  while (connection->n_outgoing > 0 &&
+         _dbus_connection_get_is_connected_unlocked (connection))
+    {
+      _dbus_verbose ("doing iteration in %s\n", _DBUS_FUNCTION_NAME);
+      HAVE_LOCK_CHECK (connection);
+      _dbus_connection_do_iteration_unlocked (connection,
+                                              DBUS_ITERATION_DO_READING |
+                                              DBUS_ITERATION_DO_WRITING |
+                                              DBUS_ITERATION_BLOCK,
+                                              -1);
+    }
+
+  HAVE_LOCK_CHECK (connection);
+  _dbus_verbose ("%s middle\n", _DBUS_FUNCTION_NAME);
+  status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+  HAVE_LOCK_CHECK (connection);
+  return status;
+}
+
+/**
+ * Blocks until the outgoing message queue is empty.
+ *
+ * @param connection the connection.
+ */
+void
+dbus_connection_flush (DBusConnection *connection)
+{
+  /* We have to specify DBUS_ITERATION_DO_READING here because
+   * otherwise we could have two apps deadlock if they are both doing
+   * a flush(), and the kernel buffers fill up. This could change the
+   * dispatch status.
+   */
+  DBusDispatchStatus status;
+
+  _dbus_return_if_fail (connection != NULL);
+  
+  CONNECTION_LOCK (connection);
+
+  status = _dbus_connection_flush_unlocked (connection);
+  
+  HAVE_LOCK_CHECK (connection);
+  /* Unlocks and calls out to user code */
+  _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+
+  _dbus_verbose ("%s end\n", _DBUS_FUNCTION_NAME);
+}
+
+/**
+ * This function implements dbus_connection_read_write_dispatch() and
+ * dbus_connection_read_write() (they pass a different value for the
+ * dispatch parameter).
+ * 
+ * @param connection the connection
+ * @param timeout_milliseconds max time to block or -1 for infinite
+ * @param dispatch dispatch new messages or leave them on the incoming queue
+ * @returns #TRUE if the disconnect message has not been processed
+ */
+static dbus_bool_t
+_dbus_connection_read_write_dispatch (DBusConnection *connection,
+                                     int             timeout_milliseconds, 
+                                     dbus_bool_t     dispatch)
+{
+  DBusDispatchStatus dstatus;
+  dbus_bool_t progress_possible;
+
+  /* Need to grab a ref here in case we're a private connection and
+   * the user drops the last ref in a handler we call; see bug 
+   * https://bugs.freedesktop.org/show_bug.cgi?id=15635
+   */
+  dbus_connection_ref (connection);
+  dstatus = dbus_connection_get_dispatch_status (connection);
+
+  if (dispatch && dstatus == DBUS_DISPATCH_DATA_REMAINS)
+    {
+      _dbus_verbose ("doing dispatch in %s\n", _DBUS_FUNCTION_NAME);
+      dbus_connection_dispatch (connection);
+      CONNECTION_LOCK (connection);
+    }
+  else if (dstatus == DBUS_DISPATCH_NEED_MEMORY)
+    {
+      _dbus_verbose ("pausing for memory in %s\n", _DBUS_FUNCTION_NAME);
+      _dbus_memory_pause_based_on_timeout (timeout_milliseconds);
+      CONNECTION_LOCK (connection);
+    }
+  else
+    {
+      CONNECTION_LOCK (connection);
+      if (_dbus_connection_get_is_connected_unlocked (connection))
+        {
+          _dbus_verbose ("doing iteration in %s\n", _DBUS_FUNCTION_NAME);
+          _dbus_connection_do_iteration_unlocked (connection,
+                                                  DBUS_ITERATION_DO_READING |
+                                                  DBUS_ITERATION_DO_WRITING |
+                                                  DBUS_ITERATION_BLOCK,
+                                                  timeout_milliseconds);
+        }
+    }
+  
+  HAVE_LOCK_CHECK (connection);
+  /* If we can dispatch, we can make progress until the Disconnected message
+   * has been processed; if we can only read/write, we can make progress
+   * as long as the transport is open.
+   */
+  if (dispatch)
+    progress_possible = connection->n_incoming != 0 ||
+      connection->disconnect_message_link != NULL;
+  else
+    progress_possible = _dbus_connection_get_is_connected_unlocked (connection);
+
+  CONNECTION_UNLOCK (connection);
+
+  dbus_connection_unref (connection);
+
+  return progress_possible; /* TRUE if we can make more progress */
+}
+
+
+/**
+ * This function is intended for use with applications that don't want
+ * to write a main loop and deal with #DBusWatch and #DBusTimeout. An
+ * example usage would be:
+ * 
+ * @code
+ *   while (dbus_connection_read_write_dispatch (connection, -1))
+ *     ; // empty loop body
+ * @endcode
+ * 
+ * In this usage you would normally have set up a filter function to look
+ * at each message as it is dispatched. The loop terminates when the last
+ * message from the connection (the disconnected signal) is processed.
+ * 
+ * If there are messages to dispatch, this function will
+ * dbus_connection_dispatch() once, and return. If there are no
+ * messages to dispatch, this function will block until it can read or
+ * write, then read or write, then return.
+ *
+ * The way to think of this function is that it either makes some sort
+ * of progress, or it blocks. Note that, while it is blocked on I/O, it
+ * cannot be interrupted (even by other threads), which makes this function
+ * unsuitable for applications that do more than just react to received
+ * messages.
+ *
+ * The return value indicates whether the disconnect message has been
+ * processed, NOT whether the connection is connected. This is
+ * important because even after disconnecting, you want to process any
+ * messages you received prior to the disconnect.
+ *
+ * @param connection the connection
+ * @param timeout_milliseconds max time to block or -1 for infinite
+ * @returns #TRUE if the disconnect message has not been processed
+ */
+dbus_bool_t
+dbus_connection_read_write_dispatch (DBusConnection *connection,
+                                     int             timeout_milliseconds)
+{
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE);
+   return _dbus_connection_read_write_dispatch(connection, timeout_milliseconds, TRUE);
+}
+
+/** 
+ * This function is intended for use with applications that don't want to
+ * write a main loop and deal with #DBusWatch and #DBusTimeout. See also
+ * dbus_connection_read_write_dispatch().
+ * 
+ * As long as the connection is open, this function will block until it can
+ * read or write, then read or write, then return #TRUE.
+ *
+ * If the connection is closed, the function returns #FALSE.
+ *
+ * The return value indicates whether reading or writing is still
+ * possible, i.e. whether the connection is connected.
+ *
+ * Note that even after disconnection, messages may remain in the
+ * incoming queue that need to be
+ * processed. dbus_connection_read_write_dispatch() dispatches
+ * incoming messages for you; with dbus_connection_read_write() you
+ * have to arrange to drain the incoming queue yourself.
+ * 
+ * @param connection the connection 
+ * @param timeout_milliseconds max time to block or -1 for infinite 
+ * @returns #TRUE if still connected
+ */
+dbus_bool_t 
+dbus_connection_read_write (DBusConnection *connection, 
+                            int             timeout_milliseconds) 
+{ 
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE);
+   return _dbus_connection_read_write_dispatch(connection, timeout_milliseconds, FALSE);
+}
+
+/* We need to call this anytime we pop the head of the queue, and then
+ * update_dispatch_status_and_unlock needs to be called afterward
+ * which will "process" the disconnected message and set
+ * disconnected_message_processed.
+ */
+static void
+check_disconnected_message_arrived_unlocked (DBusConnection *connection,
+                                             DBusMessage    *head_of_queue)
+{
+  HAVE_LOCK_CHECK (connection);
+
+  /* checking that the link is NULL is an optimization to avoid the is_signal call */
+  if (connection->disconnect_message_link == NULL &&
+      dbus_message_is_signal (head_of_queue,
+                              DBUS_INTERFACE_LOCAL,
+                              "Disconnected"))
+    {
+      connection->disconnected_message_arrived = TRUE;
+    }
+}
+
+/**
+ * Returns the first-received message from the incoming message queue,
+ * leaving it in the queue. If the queue is empty, returns #NULL.
+ * 
+ * The caller does not own a reference to the returned message, and
+ * must either return it using dbus_connection_return_message() or
+ * keep it after calling dbus_connection_steal_borrowed_message(). No
+ * one can get at the message while its borrowed, so return it as
+ * quickly as possible and don't keep a reference to it after
+ * returning it. If you need to keep the message, make a copy of it.
+ *
+ * dbus_connection_dispatch() will block if called while a borrowed
+ * message is outstanding; only one piece of code can be playing with
+ * the incoming queue at a time. This function will block if called
+ * during a dbus_connection_dispatch().
+ *
+ * @param connection the connection.
+ * @returns next message in the incoming queue.
+ */
+DBusMessage*
+dbus_connection_borrow_message (DBusConnection *connection)
+{
+  DBusDispatchStatus status;
+  DBusMessage *message;
+
+  _dbus_return_val_if_fail (connection != NULL, NULL);
+
+  _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME);
+  
+  /* this is called for the side effect that it queues
+   * up any messages from the transport
+   */
+  status = dbus_connection_get_dispatch_status (connection);
+  if (status != DBUS_DISPATCH_DATA_REMAINS)
+    return NULL;
+  
+  CONNECTION_LOCK (connection);
+
+  _dbus_connection_acquire_dispatch (connection);
+
+  /* While a message is outstanding, the dispatch lock is held */
+  _dbus_assert (connection->message_borrowed == NULL);
+
+  connection->message_borrowed = _dbus_list_get_first (&connection->incoming_messages);
+  
+  message = connection->message_borrowed;
+
+  check_disconnected_message_arrived_unlocked (connection, message);
+  
+  /* Note that we KEEP the dispatch lock until the message is returned */
+  if (message == NULL)
+    _dbus_connection_release_dispatch (connection);
+
+  CONNECTION_UNLOCK (connection);
+
+  /* We don't update dispatch status until it's returned or stolen */
+  
+  return message;
+}
+
+/**
+ * Used to return a message after peeking at it using
+ * dbus_connection_borrow_message(). Only called if
+ * message from dbus_connection_borrow_message() was non-#NULL.
+ *
+ * @param connection the connection
+ * @param message the message from dbus_connection_borrow_message()
+ */
+void
+dbus_connection_return_message (DBusConnection *connection,
+                               DBusMessage    *message)
+{
+  DBusDispatchStatus status;
+  
+  _dbus_return_if_fail (connection != NULL);
+  _dbus_return_if_fail (message != NULL);
+  _dbus_return_if_fail (message == connection->message_borrowed);
+  _dbus_return_if_fail (connection->dispatch_acquired);
+  
+  CONNECTION_LOCK (connection);
+  
+  _dbus_assert (message == connection->message_borrowed);
+  
+  connection->message_borrowed = NULL;
+
+  _dbus_connection_release_dispatch (connection); 
+
+  status = _dbus_connection_get_dispatch_status_unlocked (connection);
+  _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+}
+
+/**
+ * Used to keep a message after peeking at it using
+ * dbus_connection_borrow_message(). Before using this function, see
+ * the caveats/warnings in the documentation for
+ * dbus_connection_pop_message().
+ *
+ * @param connection the connection
+ * @param message the message from dbus_connection_borrow_message()
+ */
+void
+dbus_connection_steal_borrowed_message (DBusConnection *connection,
+                                       DBusMessage    *message)
+{
+  DBusMessage *pop_message;
+  DBusDispatchStatus status;
+
+  _dbus_return_if_fail (connection != NULL);
+  _dbus_return_if_fail (message != NULL);
+  _dbus_return_if_fail (message == connection->message_borrowed);
+  _dbus_return_if_fail (connection->dispatch_acquired);
+  
+  CONNECTION_LOCK (connection);
+  _dbus_assert (message == connection->message_borrowed);
+
+  pop_message = _dbus_list_pop_first (&connection->incoming_messages);
+  _dbus_assert (message == pop_message);
+  
+  connection->n_incoming -= 1;
+  _dbus_verbose ("Incoming message %p stolen from queue, %d incoming\n",
+                message, connection->n_incoming);
+  connection->message_borrowed = NULL;
+
+  _dbus_connection_release_dispatch (connection);
+
+  status = _dbus_connection_get_dispatch_status_unlocked (connection);
+  _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+}
+
+/* See dbus_connection_pop_message, but requires the caller to own
+ * the lock before calling. May drop the lock while running.
+ */
+static DBusList*
+_dbus_connection_pop_message_link_unlocked (DBusConnection *connection)
+{
+  HAVE_LOCK_CHECK (connection);
+  
+  _dbus_assert (connection->message_borrowed == NULL);
+  
+  if (connection->n_incoming > 0)
+    {
+      DBusList *link;
+
+      link = _dbus_list_pop_first_link (&connection->incoming_messages);
+      connection->n_incoming -= 1;
+
+      _dbus_verbose ("Message %p (%d %s %s %s '%s') removed from incoming queue %p, %d incoming\n",
+                     link->data,
+                     dbus_message_get_type (link->data),
+                     dbus_message_get_path (link->data) ?
+                     dbus_message_get_path (link->data) :
+                     "no path",
+                     dbus_message_get_interface (link->data) ?
+                     dbus_message_get_interface (link->data) :
+                     "no interface",
+                     dbus_message_get_member (link->data) ?
+                     dbus_message_get_member (link->data) :
+                     "no member",
+                     dbus_message_get_signature (link->data),
+                     connection, connection->n_incoming);
+
+      check_disconnected_message_arrived_unlocked (connection, link->data);
+      
+      return link;
+    }
+  else
+    return NULL;
+}
+
+/* See dbus_connection_pop_message, but requires the caller to own
+ * the lock before calling. May drop the lock while running.
+ */
+static DBusMessage*
+_dbus_connection_pop_message_unlocked (DBusConnection *connection)
+{
+  DBusList *link;
+
+  HAVE_LOCK_CHECK (connection);
+  
+  link = _dbus_connection_pop_message_link_unlocked (connection);
+
+  if (link != NULL)
+    {
+      DBusMessage *message;
+      
+      message = link->data;
+      
+      _dbus_list_free_link (link);
+      
+      return message;
+    }
+  else
+    return NULL;
+}
+
+static void
+_dbus_connection_putback_message_link_unlocked (DBusConnection *connection,
+                                                DBusList       *message_link)
+{
+  HAVE_LOCK_CHECK (connection);
+  
+  _dbus_assert (message_link != NULL);
+  /* You can't borrow a message while a link is outstanding */
+  _dbus_assert (connection->message_borrowed == NULL);
+  /* We had to have the dispatch lock across the pop/putback */
+  _dbus_assert (connection->dispatch_acquired);
+
+  _dbus_list_prepend_link (&connection->incoming_messages,
+                           message_link);
+  connection->n_incoming += 1;
+
+  _dbus_verbose ("Message %p (%d %s %s '%s') put back into queue %p, %d incoming\n",
+                 message_link->data,
+                 dbus_message_get_type (message_link->data),
+                 dbus_message_get_interface (message_link->data) ?
+                 dbus_message_get_interface (message_link->data) :
+                 "no interface",
+                 dbus_message_get_member (message_link->data) ?
+                 dbus_message_get_member (message_link->data) :
+                 "no member",
+                 dbus_message_get_signature (message_link->data),
+                 connection, connection->n_incoming);
+}
+
+/**
+ * Returns the first-received message from the incoming message queue,
+ * removing it from the queue. The caller owns a reference to the
+ * returned message. If the queue is empty, returns #NULL.
+ *
+ * This function bypasses any message handlers that are registered,
+ * and so using it is usually wrong. Instead, let the main loop invoke
+ * dbus_connection_dispatch(). Popping messages manually is only
+ * useful in very simple programs that don't share a #DBusConnection
+ * with any libraries or other modules.
+ *
+ * There is a lock that covers all ways of accessing the incoming message
+ * queue, so dbus_connection_dispatch(), dbus_connection_pop_message(),
+ * dbus_connection_borrow_message(), etc. will all block while one of the others
+ * in the group is running.
+ * 
+ * @param connection the connection.
+ * @returns next message in the incoming queue.
+ */
+DBusMessage*
+dbus_connection_pop_message (DBusConnection *connection)
+{
+  DBusMessage *message;
+  DBusDispatchStatus status;
+
+  _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME);
+  
+  /* this is called for the side effect that it queues
+   * up any messages from the transport
+   */
+  status = dbus_connection_get_dispatch_status (connection);
+  if (status != DBUS_DISPATCH_DATA_REMAINS)
+    return NULL;
+  
+  CONNECTION_LOCK (connection);
+  _dbus_connection_acquire_dispatch (connection);
+  HAVE_LOCK_CHECK (connection);
+  
+  message = _dbus_connection_pop_message_unlocked (connection);
+
+  _dbus_verbose ("Returning popped message %p\n", message);    
+
+  _dbus_connection_release_dispatch (connection);
+
+  status = _dbus_connection_get_dispatch_status_unlocked (connection);
+  _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+  
+  return message;
+}
+
+/**
+ * Acquire the dispatcher. This is a separate lock so the main
+ * connection lock can be dropped to call out to application dispatch
+ * handlers.
+ *
+ * @param connection the connection.
+ */
+static void
+_dbus_connection_acquire_dispatch (DBusConnection *connection)
+{
+  HAVE_LOCK_CHECK (connection);
+
+  _dbus_connection_ref_unlocked (connection);
+  CONNECTION_UNLOCK (connection);
+  
+  _dbus_verbose ("%s locking dispatch_mutex\n", _DBUS_FUNCTION_NAME);
+  _dbus_mutex_lock (connection->dispatch_mutex);
+
+  while (connection->dispatch_acquired)
+    {
+      _dbus_verbose ("%s waiting for dispatch to be acquirable\n", _DBUS_FUNCTION_NAME);
+      _dbus_condvar_wait (connection->dispatch_cond, 
+                          connection->dispatch_mutex);
+    }
+  
+  _dbus_assert (!connection->dispatch_acquired);
+
+  connection->dispatch_acquired = TRUE;
+
+  _dbus_verbose ("%s unlocking dispatch_mutex\n", _DBUS_FUNCTION_NAME);
+  _dbus_mutex_unlock (connection->dispatch_mutex);
+  
+  CONNECTION_LOCK (connection);
+  _dbus_connection_unref_unlocked (connection);
+}
+
+/**
+ * Release the dispatcher when you're done with it. Only call
+ * after you've acquired the dispatcher. Wakes up at most one
+ * thread currently waiting to acquire the dispatcher.
+ *
+ * @param connection the connection.
+ */
+static void
+_dbus_connection_release_dispatch (DBusConnection *connection)
+{
+  HAVE_LOCK_CHECK (connection);
+  
+  _dbus_verbose ("%s locking dispatch_mutex\n", _DBUS_FUNCTION_NAME);
+  _dbus_mutex_lock (connection->dispatch_mutex);
+  
+  _dbus_assert (connection->dispatch_acquired);
+
+  connection->dispatch_acquired = FALSE;
+  _dbus_condvar_wake_one (connection->dispatch_cond);
+
+  _dbus_verbose ("%s unlocking dispatch_mutex\n", _DBUS_FUNCTION_NAME);
+  _dbus_mutex_unlock (connection->dispatch_mutex);
+}
+
+static void
+_dbus_connection_failed_pop (DBusConnection *connection,
+                            DBusList       *message_link)
+{
+  _dbus_list_prepend_link (&connection->incoming_messages,
+                          message_link);
+  connection->n_incoming += 1;
+}
+
+/* Note this may be called multiple times since we don't track whether we already did it */
+static void
+notify_disconnected_unlocked (DBusConnection *connection)
+{
+  HAVE_LOCK_CHECK (connection);
+
+  /* Set the weakref in dbus-bus.c to NULL, so nobody will get a disconnected
+   * connection from dbus_bus_get(). We make the same guarantee for
+   * dbus_connection_open() but in a different way since we don't want to
+   * unref right here; we instead check for connectedness before returning
+   * the connection from the hash.
+   */
+  _dbus_bus_notify_shared_connection_disconnected_unlocked (connection);
+
+  /* Dump the outgoing queue, we aren't going to be able to
+   * send it now, and we'd like accessors like
+   * dbus_connection_get_outgoing_size() to be accurate.
+   */
+  if (connection->n_outgoing > 0)
+    {
+      DBusList *link;
+      
+      _dbus_verbose ("Dropping %d outgoing messages since we're disconnected\n",
+                     connection->n_outgoing);
+      
+      while ((link = _dbus_list_get_last_link (&connection->outgoing_messages)))
+        {
+          _dbus_connection_message_sent (connection, link->data);
+        }
+    } 
+}
+
+/* Note this may be called multiple times since we don't track whether we already did it */
+static DBusDispatchStatus
+notify_disconnected_and_dispatch_complete_unlocked (DBusConnection *connection)
+{
+  HAVE_LOCK_CHECK (connection);
+  
+  if (connection->disconnect_message_link != NULL)
+    {
+      _dbus_verbose ("Sending disconnect message from %s\n",
+                     _DBUS_FUNCTION_NAME);
+      
+      /* If we have pending calls, queue their timeouts - we want the Disconnected
+       * to be the last message, after these timeouts.
+       */
+      connection_timeout_and_complete_all_pending_calls_unlocked (connection);
+      
+      /* We haven't sent the disconnect message already,
+       * and all real messages have been queued up.
+       */
+      _dbus_connection_queue_synthesized_message_link (connection,
+                                                       connection->disconnect_message_link);
+      connection->disconnect_message_link = NULL;
+
+      return DBUS_DISPATCH_DATA_REMAINS;
+    }
+
+  return DBUS_DISPATCH_COMPLETE;
+}
+
+static DBusDispatchStatus
+_dbus_connection_get_dispatch_status_unlocked (DBusConnection *connection)
+{
+  HAVE_LOCK_CHECK (connection);
+  
+  if (connection->n_incoming > 0)
+    return DBUS_DISPATCH_DATA_REMAINS;
+  else if (!_dbus_transport_queue_messages (connection->transport))
+    return DBUS_DISPATCH_NEED_MEMORY;
+  else
+    {
+      DBusDispatchStatus status;
+      dbus_bool_t is_connected;
+      
+      status = _dbus_transport_get_dispatch_status (connection->transport);
+      is_connected = _dbus_transport_get_is_connected (connection->transport);
+
+      _dbus_verbose ("dispatch status = %s is_connected = %d\n",
+                     DISPATCH_STATUS_NAME (status), is_connected);
+      
+      if (!is_connected)
+        {
+          /* It's possible this would be better done by having an explicit
+           * notification from _dbus_transport_disconnect() that would
+           * synchronously do this, instead of waiting for the next dispatch
+           * status check. However, probably not good to change until it causes
+           * a problem.
+           */
+          notify_disconnected_unlocked (connection);
+
+          /* I'm not sure this is needed; the idea is that we want to
+           * queue the Disconnected only after we've read all the
+           * messages, but if we're disconnected maybe we are guaranteed
+           * to have read them all ?
+           */
+          if (status == DBUS_DISPATCH_COMPLETE)
+            status = notify_disconnected_and_dispatch_complete_unlocked (connection);
+        }
+      
+      if (status != DBUS_DISPATCH_COMPLETE)
+        return status;
+      else if (connection->n_incoming > 0)
+        return DBUS_DISPATCH_DATA_REMAINS;
+      else
+        return DBUS_DISPATCH_COMPLETE;
+    }
+}
+
+static void
+_dbus_connection_update_dispatch_status_and_unlock (DBusConnection    *connection,
+                                                    DBusDispatchStatus new_status)
+{
+  dbus_bool_t changed;
+  DBusDispatchStatusFunction function;
+  void *data;
+
+  HAVE_LOCK_CHECK (connection);
+
+  _dbus_connection_ref_unlocked (connection);
+
+  changed = new_status != connection->last_dispatch_status;
+
+  connection->last_dispatch_status = new_status;
+
+  function = connection->dispatch_status_function;
+  data = connection->dispatch_status_data;
+
+  if (connection->disconnected_message_arrived &&
+      !connection->disconnected_message_processed)
+    {
+      connection->disconnected_message_processed = TRUE;
+      
+      /* this does an unref, but we have a ref
+       * so we should not run the finalizer here
+       * inside the lock.
+       */
+      connection_forget_shared_unlocked (connection);
+
+      if (connection->exit_on_disconnect)
+        {
+          CONNECTION_UNLOCK (connection);            
+          
+          _dbus_verbose ("Exiting on Disconnected signal\n");
+          _dbus_exit (1);
+          _dbus_assert_not_reached ("Call to exit() returned");
+        }
+    }
+  
+  /* We drop the lock */
+  CONNECTION_UNLOCK (connection);
+  
+  if (changed && function)
+    {
+      _dbus_verbose ("Notifying of change to dispatch status of %p now %d (%s)\n",
+                     connection, new_status,
+                     DISPATCH_STATUS_NAME (new_status));
+      (* function) (connection, new_status, data);      
+    }
+  
+  dbus_connection_unref (connection);
+}
+
+/**
+ * Gets the current state of the incoming message queue.
+ * #DBUS_DISPATCH_DATA_REMAINS indicates that the message queue
+ * may contain messages. #DBUS_DISPATCH_COMPLETE indicates that the
+ * incoming queue is empty. #DBUS_DISPATCH_NEED_MEMORY indicates that
+ * there could be data, but we can't know for sure without more
+ * memory.
+ *
+ * To process the incoming message queue, use dbus_connection_dispatch()
+ * or (in rare cases) dbus_connection_pop_message().
+ *
+ * Note, #DBUS_DISPATCH_DATA_REMAINS really means that either we
+ * have messages in the queue, or we have raw bytes buffered up
+ * that need to be parsed. When these bytes are parsed, they
+ * may not add up to an entire message. Thus, it's possible
+ * to see a status of #DBUS_DISPATCH_DATA_REMAINS but not
+ * have a message yet.
+ *
+ * In particular this happens on initial connection, because all sorts
+ * of authentication protocol stuff has to be parsed before the
+ * first message arrives.
+ * 
+ * @param connection the connection.
+ * @returns current dispatch status
+ */
+DBusDispatchStatus
+dbus_connection_get_dispatch_status (DBusConnection *connection)
+{
+  DBusDispatchStatus status;
+
+  _dbus_return_val_if_fail (connection != NULL, DBUS_DISPATCH_COMPLETE);
+
+  _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME);
+  
+  CONNECTION_LOCK (connection);
+
+  status = _dbus_connection_get_dispatch_status_unlocked (connection);
+  
+  CONNECTION_UNLOCK (connection);
+
+  return status;
+}
+
+/**
+ * Filter funtion for handling the Peer standard interface.
+ */
+static DBusHandlerResult
+_dbus_connection_peer_filter_unlocked_no_update (DBusConnection *connection,
+                                                 DBusMessage    *message)
+{
+  if (connection->route_peer_messages && dbus_message_get_destination (message) != NULL)
+    {
+      /* This means we're letting the bus route this message */
+      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+  else if (dbus_message_is_method_call (message,
+                                        DBUS_INTERFACE_PEER,
+                                        "Ping"))
+    {
+      DBusMessage *ret;
+      dbus_bool_t sent;
+      
+      ret = dbus_message_new_method_return (message);
+      if (ret == NULL)
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+     
+      sent = _dbus_connection_send_unlocked_no_update (connection, ret, NULL);
+
+      dbus_message_unref (ret);
+
+      if (!sent)
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+      
+      return DBUS_HANDLER_RESULT_HANDLED;
+    }
+  else if (dbus_message_is_method_call (message,
+                                        DBUS_INTERFACE_PEER,
+                                        "GetMachineId"))
+    {
+      DBusMessage *ret;
+      dbus_bool_t sent;
+      DBusString uuid;
+      
+      ret = dbus_message_new_method_return (message);
+      if (ret == NULL)
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+      sent = FALSE;
+      _dbus_string_init (&uuid);
+      if (_dbus_get_local_machine_uuid_encoded (&uuid))
+        {
+          const char *v_STRING = _dbus_string_get_const_data (&uuid);
+          if (dbus_message_append_args (ret,
+                                        DBUS_TYPE_STRING, &v_STRING,
+                                        DBUS_TYPE_INVALID))
+            {
+              sent = _dbus_connection_send_unlocked_no_update (connection, ret, NULL);
+            }
+        }
+      _dbus_string_free (&uuid);
+      
+      dbus_message_unref (ret);
+
+      if (!sent)
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+      
+      return DBUS_HANDLER_RESULT_HANDLED;
+    }
+  else if (dbus_message_has_interface (message, DBUS_INTERFACE_PEER))
+    {
+      /* We need to bounce anything else with this interface, otherwise apps
+       * could start extending the interface and when we added extensions
+       * here to DBusConnection we'd break those apps.
+       */
+      
+      DBusMessage *ret;
+      dbus_bool_t sent;
+      
+      ret = dbus_message_new_error (message,
+                                    DBUS_ERROR_UNKNOWN_METHOD,
+                                    "Unknown method invoked on org.freedesktop.DBus.Peer interface");
+      if (ret == NULL)
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+      
+      sent = _dbus_connection_send_unlocked_no_update (connection, ret, NULL);
+      
+      dbus_message_unref (ret);
+      
+      if (!sent)
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+      
+      return DBUS_HANDLER_RESULT_HANDLED;
+    }
+  else
+    {
+      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+}
+
+/**
+* Processes all builtin filter functions
+*
+* If the spec specifies a standard interface
+* they should be processed from this method
+**/
+static DBusHandlerResult
+_dbus_connection_run_builtin_filters_unlocked_no_update (DBusConnection *connection,
+                                                           DBusMessage    *message)
+{
+  /* We just run one filter for now but have the option to run more
+     if the spec calls for it in the future */
+
+  return _dbus_connection_peer_filter_unlocked_no_update (connection, message);
+}
+
+/**
+ * Processes any incoming data.
+ *
+ * If there's incoming raw data that has not yet been parsed, it is
+ * parsed, which may or may not result in adding messages to the
+ * incoming queue.
+ *
+ * The incoming data buffer is filled when the connection reads from
+ * its underlying transport (such as a socket).  Reading usually
+ * happens in dbus_watch_handle() or dbus_connection_read_write().
+ * 
+ * If there are complete messages in the incoming queue,
+ * dbus_connection_dispatch() removes one message from the queue and
+ * processes it. Processing has three steps.
+ *
+ * First, any method replies are passed to #DBusPendingCall or
+ * dbus_connection_send_with_reply_and_block() in order to
+ * complete the pending method call.
+ * 
+ * Second, any filters registered with dbus_connection_add_filter()
+ * are run. If any filter returns #DBUS_HANDLER_RESULT_HANDLED
+ * then processing stops after that filter.
+ *
+ * Third, if the message is a method call it is forwarded to
+ * any registered object path handlers added with
+ * dbus_connection_register_object_path() or
+ * dbus_connection_register_fallback().
+ *
+ * A single call to dbus_connection_dispatch() will process at most
+ * one message; it will not clear the entire message queue.
+ *
+ * Be careful about calling dbus_connection_dispatch() from inside a
+ * message handler, i.e. calling dbus_connection_dispatch()
+ * recursively.  If threads have been initialized with a recursive
+ * mutex function, then this will not deadlock; however, it can
+ * certainly confuse your application.
+ * 
+ * @todo some FIXME in here about handling DBUS_HANDLER_RESULT_NEED_MEMORY
+ * 
+ * @param connection the connection
+ * @returns dispatch status, see dbus_connection_get_dispatch_status()
+ */
+DBusDispatchStatus
+dbus_connection_dispatch (DBusConnection *connection)
+{
+  DBusMessage *message;
+  DBusList *link, *filter_list_copy, *message_link;
+  DBusHandlerResult result;
+  DBusPendingCall *pending;
+  dbus_int32_t reply_serial;
+  DBusDispatchStatus status;
+
+  _dbus_return_val_if_fail (connection != NULL, DBUS_DISPATCH_COMPLETE);
+
+  _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME);
+  
+  CONNECTION_LOCK (connection);
+  status = _dbus_connection_get_dispatch_status_unlocked (connection);
+  if (status != DBUS_DISPATCH_DATA_REMAINS)
+    {
+      /* unlocks and calls out to user code */
+      _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+      return status;
+    }
+  
+  /* We need to ref the connection since the callback could potentially
+   * drop the last ref to it
+   */
+  _dbus_connection_ref_unlocked (connection);
+
+  _dbus_connection_acquire_dispatch (connection);
+  HAVE_LOCK_CHECK (connection);
+
+  message_link = _dbus_connection_pop_message_link_unlocked (connection);
+  if (message_link == NULL)
+    {
+      /* another thread dispatched our stuff */
+
+      _dbus_verbose ("another thread dispatched message (during acquire_dispatch above)\n");
+      
+      _dbus_connection_release_dispatch (connection);
+
+      status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+      _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+      
+      dbus_connection_unref (connection);
+      
+      return status;
+    }
+
+  message = message_link->data;
+
+  _dbus_verbose (" dispatching message %p (%d %s %s '%s')\n",
+                 message,
+                 dbus_message_get_type (message),
+                 dbus_message_get_interface (message) ?
+                 dbus_message_get_interface (message) :
+                 "no interface",
+                 dbus_message_get_member (message) ?
+                 dbus_message_get_member (message) :
+                 "no member",
+                 dbus_message_get_signature (message));
+
+  result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+  
+  /* Pending call handling must be first, because if you do
+   * dbus_connection_send_with_reply_and_block() or
+   * dbus_pending_call_block() then no handlers/filters will be run on
+   * the reply. We want consistent semantics in the case where we
+   * dbus_connection_dispatch() the reply.
+   */
+  
+  reply_serial = dbus_message_get_reply_serial (message);
+  pending = _dbus_hash_table_lookup_int (connection->pending_replies,
+                                         reply_serial);
+  if (pending)
+    {
+      _dbus_verbose ("Dispatching a pending reply\n");
+      complete_pending_call_and_unlock (connection, pending, message);
+      pending = NULL; /* it's probably unref'd */
+      
+      CONNECTION_LOCK (connection);
+      _dbus_verbose ("pending call completed in dispatch\n");
+      result = DBUS_HANDLER_RESULT_HANDLED;
+      goto out;
+    }
+
+  result = _dbus_connection_run_builtin_filters_unlocked_no_update (connection, message);
+  if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
+    goto out;
+  if (!_dbus_list_copy (&connection->filter_list, &filter_list_copy))
+    {
+      _dbus_connection_release_dispatch (connection);
+      HAVE_LOCK_CHECK (connection);
+      
+      _dbus_connection_failed_pop (connection, message_link);
+
+      /* unlocks and calls user code */
+      _dbus_connection_update_dispatch_status_and_unlock (connection,
+                                                          DBUS_DISPATCH_NEED_MEMORY);
+
+      if (pending)
+        dbus_pending_call_unref (pending);
+      dbus_connection_unref (connection);
+      
+      return DBUS_DISPATCH_NEED_MEMORY;
+    }
+  
+  _dbus_list_foreach (&filter_list_copy,
+                     (DBusForeachFunction)_dbus_message_filter_ref,
+                     NULL);
+
+  /* We're still protected from dispatch() reentrancy here
+   * since we acquired the dispatcher
+   */
+  CONNECTION_UNLOCK (connection);
+  
+  link = _dbus_list_get_first_link (&filter_list_copy);
+  while (link != NULL)
+    {
+      DBusMessageFilter *filter = link->data;
+      DBusList *next = _dbus_list_get_next_link (&filter_list_copy, link);
+
+      if (filter->function == NULL)
+        {
+          _dbus_verbose ("  filter was removed in a callback function\n");
+          link = next;
+          continue;
+        }
+
+      _dbus_verbose ("  running filter on message %p\n", message);
+      result = (* filter->function) (connection, message, filter->user_data);
+
+      if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
+       break;
+
+      link = next;
+    }
+
+  _dbus_list_foreach (&filter_list_copy,
+                     (DBusForeachFunction)_dbus_message_filter_unref,
+                     NULL);
+  _dbus_list_clear (&filter_list_copy);
+  
+  CONNECTION_LOCK (connection);
+
+  if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
+    {
+      _dbus_verbose ("No memory in %s\n", _DBUS_FUNCTION_NAME);
+      goto out;
+    }
+  else if (result == DBUS_HANDLER_RESULT_HANDLED)
+    {
+      _dbus_verbose ("filter handled message in dispatch\n");
+      goto out;
+    }
+
+  /* We're still protected from dispatch() reentrancy here
+   * since we acquired the dispatcher
+   */
+  _dbus_verbose ("  running object path dispatch on message %p (%d %s %s '%s')\n",
+                 message,
+                 dbus_message_get_type (message),
+                 dbus_message_get_interface (message) ?
+                 dbus_message_get_interface (message) :
+                 "no interface",
+                 dbus_message_get_member (message) ?
+                 dbus_message_get_member (message) :
+                 "no member",
+                 dbus_message_get_signature (message));
+
+  HAVE_LOCK_CHECK (connection);
+  result = _dbus_object_tree_dispatch_and_unlock (connection->objects,
+                                                  message);
+  
+  CONNECTION_LOCK (connection);
+
+  if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
+    {
+      _dbus_verbose ("object tree handled message in dispatch\n");
+      goto out;
+    }
+
+  if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
+    {
+      DBusMessage *reply;
+      DBusString str;
+      DBusPreallocatedSend *preallocated;
+
+      _dbus_verbose ("  sending error %s\n",
+                     DBUS_ERROR_UNKNOWN_METHOD);
+      
+      if (!_dbus_string_init (&str))
+        {
+          result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+          _dbus_verbose ("no memory for error string in dispatch\n");
+          goto out;
+        }
+              
+      if (!_dbus_string_append_printf (&str,
+                                       "Method \"%s\" with signature \"%s\" on interface \"%s\" doesn't exist\n",
+                                       dbus_message_get_member (message),
+                                       dbus_message_get_signature (message),
+                                       dbus_message_get_interface (message)))
+        {
+          _dbus_string_free (&str);
+          result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+          _dbus_verbose ("no memory for error string in dispatch\n");
+          goto out;
+        }
+      
+      reply = dbus_message_new_error (message,
+                                      DBUS_ERROR_UNKNOWN_METHOD,
+                                      _dbus_string_get_const_data (&str));
+      _dbus_string_free (&str);
+
+      if (reply == NULL)
+        {
+          result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+          _dbus_verbose ("no memory for error reply in dispatch\n");
+          goto out;
+        }
+      
+      preallocated = _dbus_connection_preallocate_send_unlocked (connection);
+
+      if (preallocated == NULL)
+        {
+          dbus_message_unref (reply);
+          result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+          _dbus_verbose ("no memory for error send in dispatch\n");
+          goto out;
+        }
+
+      _dbus_connection_send_preallocated_unlocked_no_update (connection, preallocated,
+                                                             reply, NULL);
+
+      dbus_message_unref (reply);
+      
+      result = DBUS_HANDLER_RESULT_HANDLED;
+    }
+  
+  _dbus_verbose ("  done dispatching %p (%d %s %s '%s') on connection %p\n", message,
+                 dbus_message_get_type (message),
+                 dbus_message_get_interface (message) ?
+                 dbus_message_get_interface (message) :
+                 "no interface",
+                 dbus_message_get_member (message) ?
+                 dbus_message_get_member (message) :
+                 "no member",
+                 dbus_message_get_signature (message),
+                 connection);
+  
+ out:
+  if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
+    {
+      _dbus_verbose ("out of memory in %s\n", _DBUS_FUNCTION_NAME);
+      
+      /* Put message back, and we'll start over.
+       * Yes this means handlers must be idempotent if they
+       * don't return HANDLED; c'est la vie.
+       */
+      _dbus_connection_putback_message_link_unlocked (connection,
+                                                      message_link);
+    }
+  else
+    {
+      _dbus_verbose (" ... done dispatching in %s\n", _DBUS_FUNCTION_NAME);
+      
+      _dbus_list_free_link (message_link);
+      dbus_message_unref (message); /* don't want the message to count in max message limits
+                                     * in computing dispatch status below
+                                     */
+    }
+  
+  _dbus_connection_release_dispatch (connection);
+  HAVE_LOCK_CHECK (connection);
+
+  _dbus_verbose ("%s before final status update\n", _DBUS_FUNCTION_NAME);
+  status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+  /* unlocks and calls user code */
+  _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+  
+  dbus_connection_unref (connection);
+  
+  return status;
+}
+
+/**
+ * Sets the watch functions for the connection. These functions are
+ * responsible for making the application's main loop aware of file
+ * descriptors that need to be monitored for events, using select() or
+ * poll(). When using Qt, typically the DBusAddWatchFunction would
+ * create a QSocketNotifier. When using GLib, the DBusAddWatchFunction
+ * could call g_io_add_watch(), or could be used as part of a more
+ * elaborate GSource. Note that when a watch is added, it may
+ * not be enabled.
+ *
+ * The DBusWatchToggledFunction notifies the application that the
+ * watch has been enabled or disabled. Call dbus_watch_get_enabled()
+ * to check this. A disabled watch should have no effect, and enabled
+ * watch should be added to the main loop. This feature is used
+ * instead of simply adding/removing the watch because
+ * enabling/disabling can be done without memory allocation.  The
+ * toggled function may be NULL if a main loop re-queries
+ * dbus_watch_get_enabled() every time anyway.
+ * 
+ * The DBusWatch can be queried for the file descriptor to watch using
+ * dbus_watch_get_unix_fd() or dbus_watch_get_socket(), and for the
+ * events to watch for using dbus_watch_get_flags(). The flags
+ * returned by dbus_watch_get_flags() will only contain
+ * DBUS_WATCH_READABLE and DBUS_WATCH_WRITABLE, never
+ * DBUS_WATCH_HANGUP or DBUS_WATCH_ERROR; all watches implicitly
+ * include a watch for hangups, errors, and other exceptional
+ * conditions.
+ *
+ * Once a file descriptor becomes readable or writable, or an exception
+ * occurs, dbus_watch_handle() should be called to
+ * notify the connection of the file descriptor's condition.
+ *
+ * dbus_watch_handle() cannot be called during the
+ * DBusAddWatchFunction, as the connection will not be ready to handle
+ * that watch yet.
+ * 
+ * It is not allowed to reference a DBusWatch after it has been passed
+ * to remove_function.
+ *
+ * If #FALSE is returned due to lack of memory, the failure may be due
+ * to a #FALSE return from the new add_function. If so, the
+ * add_function may have been called successfully one or more times,
+ * but the remove_function will also have been called to remove any
+ * successful adds. i.e. if #FALSE is returned the net result
+ * should be that dbus_connection_set_watch_functions() has no effect,
+ * but the add_function and remove_function may have been called.
+ *
+ * @todo We need to drop the lock when we call the
+ * add/remove/toggled functions which can be a side effect
+ * of setting the watch functions.
+ * 
+ * @param connection the connection.
+ * @param add_function function to begin monitoring a new descriptor.
+ * @param remove_function function to stop monitoring a descriptor.
+ * @param toggled_function function to notify of enable/disable
+ * @param data data to pass to add_function and remove_function.
+ * @param free_data_function function to be called to free the data.
+ * @returns #FALSE on failure (no memory)
+ */
+dbus_bool_t
+dbus_connection_set_watch_functions (DBusConnection              *connection,
+                                     DBusAddWatchFunction         add_function,
+                                     DBusRemoveWatchFunction      remove_function,
+                                     DBusWatchToggledFunction     toggled_function,
+                                     void                        *data,
+                                     DBusFreeFunction             free_data_function)
+{
+  dbus_bool_t retval;
+  DBusWatchList *watches;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  
+  CONNECTION_LOCK (connection);
+
+#ifndef DBUS_DISABLE_CHECKS
+  if (connection->watches == NULL)
+    {
+      _dbus_warn_check_failed ("Re-entrant call to %s is not allowed\n",
+                               _DBUS_FUNCTION_NAME);
+      return FALSE;
+    }
+#endif
+  
+  /* ref connection for slightly better reentrancy */
+  _dbus_connection_ref_unlocked (connection);
+
+  /* This can call back into user code, and we need to drop the
+   * connection lock when it does. This is kind of a lame
+   * way to do it.
+   */
+  watches = connection->watches;
+  connection->watches = NULL;
+  CONNECTION_UNLOCK (connection);
+
+  retval = _dbus_watch_list_set_functions (watches,
+                                           add_function, remove_function,
+                                           toggled_function,
+                                           data, free_data_function);
+  CONNECTION_LOCK (connection);
+  connection->watches = watches;
+  
+  CONNECTION_UNLOCK (connection);
+  /* drop our paranoid refcount */
+  dbus_connection_unref (connection);
+  
+  return retval;
+}
+
+/**
+ * Sets the timeout functions for the connection. These functions are
+ * responsible for making the application's main loop aware of timeouts.
+ * When using Qt, typically the DBusAddTimeoutFunction would create a
+ * QTimer. When using GLib, the DBusAddTimeoutFunction would call
+ * g_timeout_add.
+ * 
+ * The DBusTimeoutToggledFunction notifies the application that the
+ * timeout has been enabled or disabled. Call
+ * dbus_timeout_get_enabled() to check this. A disabled timeout should
+ * have no effect, and enabled timeout should be added to the main
+ * loop. This feature is used instead of simply adding/removing the
+ * timeout because enabling/disabling can be done without memory
+ * allocation. With Qt, QTimer::start() and QTimer::stop() can be used
+ * to enable and disable. The toggled function may be NULL if a main
+ * loop re-queries dbus_timeout_get_enabled() every time anyway.
+ * Whenever a timeout is toggled, its interval may change.
+ *
+ * The DBusTimeout can be queried for the timer interval using
+ * dbus_timeout_get_interval(). dbus_timeout_handle() should be called
+ * repeatedly, each time the interval elapses, starting after it has
+ * elapsed once. The timeout stops firing when it is removed with the
+ * given remove_function.  The timer interval may change whenever the
+ * timeout is added, removed, or toggled.
+ *
+ * @param connection the connection.
+ * @param add_function function to add a timeout.
+ * @param remove_function function to remove a timeout.
+ * @param toggled_function function to notify of enable/disable
+ * @param data data to pass to add_function and remove_function.
+ * @param free_data_function function to be called to free the data.
+ * @returns #FALSE on failure (no memory)
+ */
+dbus_bool_t
+dbus_connection_set_timeout_functions   (DBusConnection            *connection,
+                                        DBusAddTimeoutFunction     add_function,
+                                        DBusRemoveTimeoutFunction  remove_function,
+                                         DBusTimeoutToggledFunction toggled_function,
+                                        void                      *data,
+                                        DBusFreeFunction           free_data_function)
+{
+  dbus_bool_t retval;
+  DBusTimeoutList *timeouts;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  
+  CONNECTION_LOCK (connection);
+
+#ifndef DBUS_DISABLE_CHECKS
+  if (connection->timeouts == NULL)
+    {
+      _dbus_warn_check_failed ("Re-entrant call to %s is not allowed\n",
+                               _DBUS_FUNCTION_NAME);
+      return FALSE;
+    }
+#endif
+  
+  /* ref connection for slightly better reentrancy */
+  _dbus_connection_ref_unlocked (connection);
+
+  timeouts = connection->timeouts;
+  connection->timeouts = NULL;
+  CONNECTION_UNLOCK (connection);
+  
+  retval = _dbus_timeout_list_set_functions (timeouts,
+                                             add_function, remove_function,
+                                             toggled_function,
+                                             data, free_data_function);
+  CONNECTION_LOCK (connection);
+  connection->timeouts = timeouts;
+  
+  CONNECTION_UNLOCK (connection);
+  /* drop our paranoid refcount */
+  dbus_connection_unref (connection);
+
+  return retval;
+}
+
+/**
+ * Sets the mainloop wakeup function for the connection. This function
+ * is responsible for waking up the main loop (if its sleeping in
+ * another thread) when some some change has happened to the
+ * connection that the mainloop needs to reconsider (e.g. a message
+ * has been queued for writing).  When using Qt, this typically
+ * results in a call to QEventLoop::wakeUp().  When using GLib, it
+ * would call g_main_context_wakeup().
+ *
+ * @param connection the connection.
+ * @param wakeup_main_function function to wake up the mainloop
+ * @param data data to pass wakeup_main_function
+ * @param free_data_function function to be called to free the data.
+ */
+void
+dbus_connection_set_wakeup_main_function (DBusConnection            *connection,
+                                         DBusWakeupMainFunction     wakeup_main_function,
+                                         void                      *data,
+                                         DBusFreeFunction           free_data_function)
+{
+  void *old_data;
+  DBusFreeFunction old_free_data;
+
+  _dbus_return_if_fail (connection != NULL);
+  
+  CONNECTION_LOCK (connection);
+  old_data = connection->wakeup_main_data;
+  old_free_data = connection->free_wakeup_main_data;
+
+  connection->wakeup_main_function = wakeup_main_function;
+  connection->wakeup_main_data = data;
+  connection->free_wakeup_main_data = free_data_function;
+  
+  CONNECTION_UNLOCK (connection);
+
+  /* Callback outside the lock */
+  if (old_free_data)
+    (*old_free_data) (old_data);
+}
+
+/**
+ * Set a function to be invoked when the dispatch status changes.
+ * If the dispatch status is #DBUS_DISPATCH_DATA_REMAINS, then
+ * dbus_connection_dispatch() needs to be called to process incoming
+ * messages. However, dbus_connection_dispatch() MUST NOT BE CALLED
+ * from inside the DBusDispatchStatusFunction. Indeed, almost
+ * any reentrancy in this function is a bad idea. Instead,
+ * the DBusDispatchStatusFunction should simply save an indication
+ * that messages should be dispatched later, when the main loop
+ * is re-entered.
+ *
+ * If you don't set a dispatch status function, you have to be sure to
+ * dispatch on every iteration of your main loop, especially if
+ * dbus_watch_handle() or dbus_timeout_handle() were called.
+ *
+ * @param connection the connection
+ * @param function function to call on dispatch status changes
+ * @param data data for function
+ * @param free_data_function free the function data
+ */
+void
+dbus_connection_set_dispatch_status_function (DBusConnection             *connection,
+                                              DBusDispatchStatusFunction  function,
+                                              void                       *data,
+                                              DBusFreeFunction            free_data_function)
+{
+  void *old_data;
+  DBusFreeFunction old_free_data;
+
+  _dbus_return_if_fail (connection != NULL);
+  
+  CONNECTION_LOCK (connection);
+  old_data = connection->dispatch_status_data;
+  old_free_data = connection->free_dispatch_status_data;
+
+  connection->dispatch_status_function = function;
+  connection->dispatch_status_data = data;
+  connection->free_dispatch_status_data = free_data_function;
+  
+  CONNECTION_UNLOCK (connection);
+
+  /* Callback outside the lock */
+  if (old_free_data)
+    (*old_free_data) (old_data);
+}
+
+/**
+ * Get the UNIX file descriptor of the connection, if any.  This can
+ * be used for SELinux access control checks with getpeercon() for
+ * example. DO NOT read or write to the file descriptor, or try to
+ * select() on it; use DBusWatch for main loop integration. Not all
+ * connections will have a file descriptor. So for adding descriptors
+ * to the main loop, use dbus_watch_get_unix_fd() and so forth.
+ *
+ * If the connection is socket-based, you can also use
+ * dbus_connection_get_socket(), which will work on Windows too.
+ * This function always fails on Windows.
+ *
+ * Right now the returned descriptor is always a socket, but
+ * that is not guaranteed.
+ * 
+ * @param connection the connection
+ * @param fd return location for the file descriptor.
+ * @returns #TRUE if fd is successfully obtained.
+ */
+dbus_bool_t
+dbus_connection_get_unix_fd (DBusConnection *connection,
+                             int            *fd)
+{
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (connection->transport != NULL, FALSE);
+
+#ifdef DBUS_WIN
+  /* FIXME do this on a lower level */
+  return FALSE;
+#endif
+  
+  return dbus_connection_get_socket(connection, fd);
+}
+
+/**
+ * Gets the underlying Windows or UNIX socket file descriptor
+ * of the connection, if any. DO NOT read or write to the file descriptor, or try to
+ * select() on it; use DBusWatch for main loop integration. Not all
+ * connections will have a socket. So for adding descriptors
+ * to the main loop, use dbus_watch_get_socket() and so forth.
+ *
+ * If the connection is not socket-based, this function will return FALSE,
+ * even if the connection does have a file descriptor of some kind.
+ * i.e. this function always returns specifically a socket file descriptor.
+ * 
+ * @param connection the connection
+ * @param fd return location for the file descriptor.
+ * @returns #TRUE if fd is successfully obtained.
+ */
+dbus_bool_t
+dbus_connection_get_socket(DBusConnection              *connection,
+                           int                         *fd)
+{
+  dbus_bool_t retval;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (connection->transport != NULL, FALSE);
+  
+  CONNECTION_LOCK (connection);
+  
+  retval = _dbus_transport_get_socket_fd (connection->transport,
+                                          fd);
+
+  CONNECTION_UNLOCK (connection);
+
+  return retval;
+}
+
+
+/**
+ * Gets the UNIX user ID of the connection if known.  Returns #TRUE if
+ * the uid is filled in.  Always returns #FALSE on non-UNIX platforms
+ * for now, though in theory someone could hook Windows to NIS or
+ * something.  Always returns #FALSE prior to authenticating the
+ * connection.
+ *
+ * The UID is only read by servers from clients; clients can't usually
+ * get the UID of servers, because servers do not authenticate to
+ * clients.  The returned UID is the UID the connection authenticated
+ * as.
+ *
+ * The message bus is a server and the apps connecting to the bus
+ * are clients.
+ *
+ * You can ask the bus to tell you the UID of another connection though
+ * if you like; this is done with dbus_bus_get_unix_user().
+ *
+ * @param connection the connection
+ * @param uid return location for the user ID
+ * @returns #TRUE if uid is filled in with a valid user ID
+ */
+dbus_bool_t
+dbus_connection_get_unix_user (DBusConnection *connection,
+                               unsigned long  *uid)
+{
+  dbus_bool_t result;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (uid != NULL, FALSE);
+  
+  CONNECTION_LOCK (connection);
+
+  if (!_dbus_transport_get_is_authenticated (connection->transport))
+    result = FALSE;
+  else
+    result = _dbus_transport_get_unix_user (connection->transport,
+                                            uid);
+
+#ifdef DBUS_WIN
+  _dbus_assert (!result);
+#endif
+  
+  CONNECTION_UNLOCK (connection);
+
+  return result;
+}
+
+/**
+ * Gets the process ID of the connection if any.
+ * Returns #TRUE if the pid is filled in.
+ * Always returns #FALSE prior to authenticating the
+ * connection.
+ *
+ * @param connection the connection
+ * @param pid return location for the process ID
+ * @returns #TRUE if uid is filled in with a valid process ID
+ */
+dbus_bool_t
+dbus_connection_get_unix_process_id (DBusConnection *connection,
+                                    unsigned long  *pid)
+{
+  dbus_bool_t result;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (pid != NULL, FALSE);
+  
+  CONNECTION_LOCK (connection);
+
+  if (!_dbus_transport_get_is_authenticated (connection->transport))
+    result = FALSE;
+  else
+    result = _dbus_transport_get_unix_process_id (connection->transport,
+                                                 pid);
+#ifdef DBUS_WIN
+  _dbus_assert (!result);
+#endif
+  
+  CONNECTION_UNLOCK (connection);
+
+  return result;
+}
+
+/**
+ * Gets the ADT audit data of the connection if any.
+ * Returns #TRUE if the structure pointer is returned.
+ * Always returns #FALSE prior to authenticating the
+ * connection.
+ *
+ * @param connection the connection
+ * @param data return location for audit data 
+ * @returns #TRUE if audit data is filled in with a valid ucred pointer
+ */
+dbus_bool_t
+dbus_connection_get_adt_audit_session_data (DBusConnection *connection,
+                                           void          **data,
+                                           dbus_int32_t   *data_size)
+{
+  dbus_bool_t result;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (data != NULL, FALSE);
+  _dbus_return_val_if_fail (data_size != NULL, FALSE);
+  
+  CONNECTION_LOCK (connection);
+
+  if (!_dbus_transport_get_is_authenticated (connection->transport))
+    result = FALSE;
+  else
+    result = _dbus_transport_get_adt_audit_session_data (connection->transport,
+                                                        data,
+                                                        data_size);
+  CONNECTION_UNLOCK (connection);
+
+  return result;
+}
+
+/**
+ * Sets a predicate function used to determine whether a given user ID
+ * is allowed to connect. When an incoming connection has
+ * authenticated with a particular user ID, this function is called;
+ * if it returns #TRUE, the connection is allowed to proceed,
+ * otherwise the connection is disconnected.
+ *
+ * If the function is set to #NULL (as it is by default), then
+ * only the same UID as the server process will be allowed to
+ * connect. Also, root is always allowed to connect.
+ *
+ * On Windows, the function will be set and its free_data_function will
+ * be invoked when the connection is freed or a new function is set.
+ * However, the function will never be called, because there are
+ * no UNIX user ids to pass to it, or at least none of the existing
+ * auth protocols would allow authenticating as a UNIX user on Windows.
+ * 
+ * @param connection the connection
+ * @param function the predicate
+ * @param data data to pass to the predicate
+ * @param free_data_function function to free the data
+ */
+void
+dbus_connection_set_unix_user_function (DBusConnection             *connection,
+                                        DBusAllowUnixUserFunction   function,
+                                        void                       *data,
+                                        DBusFreeFunction            free_data_function)
+{
+  void *old_data = NULL;
+  DBusFreeFunction old_free_function = NULL;
+
+  _dbus_return_if_fail (connection != NULL);
+  
+  CONNECTION_LOCK (connection);
+  _dbus_transport_set_unix_user_function (connection->transport,
+                                          function, data, free_data_function,
+                                          &old_data, &old_free_function);
+  CONNECTION_UNLOCK (connection);
+
+  if (old_free_function != NULL)
+    (* old_free_function) (old_data);
+}
+
+/**
+ * Gets the Windows user SID of the connection if known.  Returns
+ * #TRUE if the ID is filled in.  Always returns #FALSE on non-Windows
+ * platforms for now, though in theory someone could hook UNIX to
+ * Active Directory or something.  Always returns #FALSE prior to
+ * authenticating the connection.
+ *
+ * The user is only read by servers from clients; clients can't usually
+ * get the user of servers, because servers do not authenticate to
+ * clients. The returned user is the user the connection authenticated
+ * as.
+ *
+ * The message bus is a server and the apps connecting to the bus
+ * are clients.
+ *
+ * The returned user string has to be freed with dbus_free().
+ *
+ * The return value indicates whether the user SID is available;
+ * if it's available but we don't have the memory to copy it,
+ * then the return value is #TRUE and #NULL is given as the SID.
+ * 
+ * @todo We would like to be able to say "You can ask the bus to tell
+ * you the user of another connection though if you like; this is done
+ * with dbus_bus_get_windows_user()." But this has to be implemented
+ * in bus/driver.c and dbus/dbus-bus.c, and is pointless anyway
+ * since on Windows we only use the session bus for now.
+ *
+ * @param connection the connection
+ * @param windows_sid_p return location for an allocated copy of the user ID, or #NULL if no memory
+ * @returns #TRUE if user is available (returned value may be #NULL anyway if no memory)
+ */
+dbus_bool_t
+dbus_connection_get_windows_user (DBusConnection             *connection,
+                                  char                      **windows_sid_p)
+{
+  dbus_bool_t result;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (windows_sid_p != NULL, FALSE);
+  
+  CONNECTION_LOCK (connection);
+
+  if (!_dbus_transport_get_is_authenticated (connection->transport))
+    result = FALSE;
+  else
+    result = _dbus_transport_get_windows_user (connection->transport,
+                                               windows_sid_p);
+
+#ifdef DBUS_UNIX
+  _dbus_assert (!result);
+#endif
+  
+  CONNECTION_UNLOCK (connection);
+
+  return result;
+}
+
+/**
+ * Sets a predicate function used to determine whether a given user ID
+ * is allowed to connect. When an incoming connection has
+ * authenticated with a particular user ID, this function is called;
+ * if it returns #TRUE, the connection is allowed to proceed,
+ * otherwise the connection is disconnected.
+ *
+ * If the function is set to #NULL (as it is by default), then
+ * only the same user owning the server process will be allowed to
+ * connect.
+ *
+ * On UNIX, the function will be set and its free_data_function will
+ * be invoked when the connection is freed or a new function is set.
+ * However, the function will never be called, because there is no
+ * way right now to authenticate as a Windows user on UNIX.
+ * 
+ * @param connection the connection
+ * @param function the predicate
+ * @param data data to pass to the predicate
+ * @param free_data_function function to free the data
+ */
+void
+dbus_connection_set_windows_user_function (DBusConnection              *connection,
+                                           DBusAllowWindowsUserFunction function,
+                                           void                        *data,
+                                           DBusFreeFunction             free_data_function)
+{
+  void *old_data = NULL;
+  DBusFreeFunction old_free_function = NULL;
+
+  _dbus_return_if_fail (connection != NULL);
+  
+  CONNECTION_LOCK (connection);
+  _dbus_transport_set_windows_user_function (connection->transport,
+                                             function, data, free_data_function,
+                                             &old_data, &old_free_function);
+  CONNECTION_UNLOCK (connection);
+
+  if (old_free_function != NULL)
+    (* old_free_function) (old_data);
+}
+
+/**
+ * This function must be called on the server side of a connection when the
+ * connection is first seen in the #DBusNewConnectionFunction. If set to
+ * #TRUE (the default is #FALSE), then the connection can proceed even if
+ * the client does not authenticate as some user identity, i.e. clients
+ * can connect anonymously.
+ * 
+ * This setting interacts with the available authorization mechanisms
+ * (see dbus_server_set_auth_mechanisms()). Namely, an auth mechanism
+ * such as ANONYMOUS that supports anonymous auth must be included in
+ * the list of available mechanisms for anonymous login to work.
+ *
+ * This setting also changes the default rule for connections
+ * authorized as a user; normally, if a connection authorizes as
+ * a user identity, it is permitted if the user identity is
+ * root or the user identity matches the user identity of the server
+ * process. If anonymous connections are allowed, however,
+ * then any user identity is allowed.
+ *
+ * You can override the rules for connections authorized as a
+ * user identity with dbus_connection_set_unix_user_function()
+ * and dbus_connection_set_windows_user_function().
+ * 
+ * @param connection the connection
+ * @param value whether to allow authentication as an anonymous user
+ */
+void
+dbus_connection_set_allow_anonymous (DBusConnection             *connection,
+                                     dbus_bool_t                 value)
+{
+  _dbus_return_if_fail (connection != NULL);
+  
+  CONNECTION_LOCK (connection);
+  _dbus_transport_set_allow_anonymous (connection->transport, value);
+  CONNECTION_UNLOCK (connection);
+}
+
+/**
+ *
+ * Normally #DBusConnection automatically handles all messages to the
+ * org.freedesktop.DBus.Peer interface. However, the message bus wants
+ * to be able to route methods on that interface through the bus and
+ * to other applications. If routing peer messages is enabled, then
+ * messages with the org.freedesktop.DBus.Peer interface that also
+ * have a bus destination name set will not be automatically
+ * handled by the #DBusConnection and instead will be dispatched
+ * normally to the application.
+ *
+ * If a normal application sets this flag, it can break things badly.
+ * So don't set this unless you are the message bus.
+ *
+ * @param connection the connection
+ * @param value #TRUE to pass through org.freedesktop.DBus.Peer messages with a bus name set
+ */
+void
+dbus_connection_set_route_peer_messages (DBusConnection             *connection,
+                                         dbus_bool_t                 value)
+{
+  _dbus_return_if_fail (connection != NULL);
+  
+  CONNECTION_LOCK (connection);
+  connection->route_peer_messages = TRUE;
+  CONNECTION_UNLOCK (connection);
+}
+
+/**
+ * Adds a message filter. Filters are handlers that are run on all
+ * incoming messages, prior to the objects registered with
+ * dbus_connection_register_object_path().  Filters are run in the
+ * order that they were added.  The same handler can be added as a
+ * filter more than once, in which case it will be run more than once.
+ * Filters added during a filter callback won't be run on the message
+ * being processed.
+ *
+ * @todo we don't run filters on messages while blocking without
+ * entering the main loop, since filters are run as part of
+ * dbus_connection_dispatch(). This is probably a feature, as filters
+ * could create arbitrary reentrancy. But kind of sucks if you're
+ * trying to filter METHOD_RETURN for some reason.
+ *
+ * @param connection the connection
+ * @param function function to handle messages
+ * @param user_data user data to pass to the function
+ * @param free_data_function function to use for freeing user data
+ * @returns #TRUE on success, #FALSE if not enough memory.
+ */
+dbus_bool_t
+dbus_connection_add_filter (DBusConnection            *connection,
+                            DBusHandleMessageFunction  function,
+                            void                      *user_data,
+                            DBusFreeFunction           free_data_function)
+{
+  DBusMessageFilter *filter;
+  
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (function != NULL, FALSE);
+
+  filter = dbus_new0 (DBusMessageFilter, 1);
+  if (filter == NULL)
+    return FALSE;
+
+  filter->refcount.value = 1;
+  
+  CONNECTION_LOCK (connection);
+
+  if (!_dbus_list_append (&connection->filter_list,
+                          filter))
+    {
+      _dbus_message_filter_unref (filter);
+      CONNECTION_UNLOCK (connection);
+      return FALSE;
+    }
+
+  /* Fill in filter after all memory allocated,
+   * so we don't run the free_user_data_function
+   * if the add_filter() fails
+   */
+  
+  filter->function = function;
+  filter->user_data = user_data;
+  filter->free_user_data_function = free_data_function;
+        
+  CONNECTION_UNLOCK (connection);
+  return TRUE;
+}
+
+/**
+ * Removes a previously-added message filter. It is a programming
+ * error to call this function for a handler that has not been added
+ * as a filter. If the given handler was added more than once, only
+ * one instance of it will be removed (the most recently-added
+ * instance).
+ *
+ * @param connection the connection
+ * @param function the handler to remove
+ * @param user_data user data for the handler to remove
+ *
+ */
+void
+dbus_connection_remove_filter (DBusConnection            *connection,
+                               DBusHandleMessageFunction  function,
+                               void                      *user_data)
+{
+  DBusList *link;
+  DBusMessageFilter *filter;
+  
+  _dbus_return_if_fail (connection != NULL);
+  _dbus_return_if_fail (function != NULL);
+  
+  CONNECTION_LOCK (connection);
+
+  filter = NULL;
+  
+  link = _dbus_list_get_last_link (&connection->filter_list);
+  while (link != NULL)
+    {
+      filter = link->data;
+
+      if (filter->function == function &&
+          filter->user_data == user_data)
+        {
+          _dbus_list_remove_link (&connection->filter_list, link);
+          filter->function = NULL;
+          
+          break;
+        }
+        
+      link = _dbus_list_get_prev_link (&connection->filter_list, link);
+    }
+  
+  CONNECTION_UNLOCK (connection);
+
+#ifndef DBUS_DISABLE_CHECKS
+  if (filter == NULL)
+    {
+      _dbus_warn_check_failed ("Attempt to remove filter function %p user data %p, but no such filter has been added\n",
+                               function, user_data);
+      return;
+    }
+#endif
+  
+  /* Call application code */
+  if (filter->free_user_data_function)
+    (* filter->free_user_data_function) (filter->user_data);
+
+  filter->free_user_data_function = NULL;
+  filter->user_data = NULL;
+  
+  _dbus_message_filter_unref (filter);
+}
+
+/**
+ * Registers a handler for a given path in the object hierarchy.
+ * The given vtable handles messages sent to exactly the given path.
+ *
+ * @param connection the connection
+ * @param path a '/' delimited string of path elements
+ * @param vtable the virtual table
+ * @param user_data data to pass to functions in the vtable
+ * @param error address where an error can be returned
+ * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
+ *    #DBUS_ERROR_ADDRESS_IN_USE) is reported
+ */
+dbus_bool_t
+dbus_connection_try_register_object_path (DBusConnection              *connection,
+                                          const char                  *path,
+                                          const DBusObjectPathVTable  *vtable,
+                                          void                        *user_data,
+                                          DBusError                   *error)
+{
+  char **decomposed_path;
+  dbus_bool_t retval;
+  
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (path != NULL, FALSE);
+  _dbus_return_val_if_fail (path[0] == '/', FALSE);
+  _dbus_return_val_if_fail (vtable != NULL, FALSE);
+
+  if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
+    return FALSE;
+
+  CONNECTION_LOCK (connection);
+
+  retval = _dbus_object_tree_register (connection->objects,
+                                       FALSE,
+                                       (const char **) decomposed_path, vtable,
+                                       user_data, error);
+
+  CONNECTION_UNLOCK (connection);
+
+  dbus_free_string_array (decomposed_path);
+
+  return retval;
+}
+
+/**
+ * Registers a handler for a given path in the object hierarchy.
+ * The given vtable handles messages sent to exactly the given path.
+ *
+ * It is a bug to call this function for object paths which already
+ * have a handler. Use dbus_connection_try_register_object_path() if this
+ * might be the case.
+ *
+ * @param connection the connection
+ * @param path a '/' delimited string of path elements
+ * @param vtable the virtual table
+ * @param user_data data to pass to functions in the vtable
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_connection_register_object_path (DBusConnection              *connection,
+                                      const char                  *path,
+                                      const DBusObjectPathVTable  *vtable,
+                                      void                        *user_data)
+{
+  char **decomposed_path;
+  dbus_bool_t retval;
+  DBusError error = DBUS_ERROR_INIT;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (path != NULL, FALSE);
+  _dbus_return_val_if_fail (path[0] == '/', FALSE);
+  _dbus_return_val_if_fail (vtable != NULL, FALSE);
+
+  if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
+    return FALSE;
+
+  CONNECTION_LOCK (connection);
+
+  retval = _dbus_object_tree_register (connection->objects,
+                                       FALSE,
+                                       (const char **) decomposed_path, vtable,
+                                       user_data, &error);
+
+  CONNECTION_UNLOCK (connection);
+
+  dbus_free_string_array (decomposed_path);
+
+  if (dbus_error_has_name (&error, DBUS_ERROR_ADDRESS_IN_USE))
+    {
+      _dbus_warn ("%s\n", error.message);
+      dbus_error_free (&error);
+      return FALSE;
+    }
+
+  return retval;
+}
+
+/**
+ * Registers a fallback handler for a given subsection of the object
+ * hierarchy.  The given vtable handles messages at or below the given
+ * path. You can use this to establish a default message handling
+ * policy for a whole "subdirectory."
+ *
+ * @param connection the connection
+ * @param path a '/' delimited string of path elements
+ * @param vtable the virtual table
+ * @param user_data data to pass to functions in the vtable
+ * @param error address where an error can be returned
+ * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
+ *    #DBUS_ERROR_ADDRESS_IN_USE) is reported
+ */
+dbus_bool_t
+dbus_connection_try_register_fallback (DBusConnection              *connection,
+                                       const char                  *path,
+                                       const DBusObjectPathVTable  *vtable,
+                                       void                        *user_data,
+                                       DBusError                   *error)
+{
+  char **decomposed_path;
+  dbus_bool_t retval;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (path != NULL, FALSE);
+  _dbus_return_val_if_fail (path[0] == '/', FALSE);
+  _dbus_return_val_if_fail (vtable != NULL, FALSE);
+
+  if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
+    return FALSE;
+
+  CONNECTION_LOCK (connection);
+
+  retval = _dbus_object_tree_register (connection->objects,
+                                       TRUE,
+                                       (const char **) decomposed_path, vtable,
+                                       user_data, error);
+
+  CONNECTION_UNLOCK (connection);
+
+  dbus_free_string_array (decomposed_path);
+
+  return retval;
+}
+
+/**
+ * Registers a fallback handler for a given subsection of the object
+ * hierarchy.  The given vtable handles messages at or below the given
+ * path. You can use this to establish a default message handling
+ * policy for a whole "subdirectory."
+ *
+ * It is a bug to call this function for object paths which already
+ * have a handler. Use dbus_connection_try_register_fallback() if this
+ * might be the case.
+ *
+ * @param connection the connection
+ * @param path a '/' delimited string of path elements
+ * @param vtable the virtual table
+ * @param user_data data to pass to functions in the vtable
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_connection_register_fallback (DBusConnection              *connection,
+                                   const char                  *path,
+                                   const DBusObjectPathVTable  *vtable,
+                                   void                        *user_data)
+{
+  char **decomposed_path;
+  dbus_bool_t retval;
+  DBusError error = DBUS_ERROR_INIT;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (path != NULL, FALSE);
+  _dbus_return_val_if_fail (path[0] == '/', FALSE);
+  _dbus_return_val_if_fail (vtable != NULL, FALSE);
+
+  if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
+    return FALSE;
+
+  CONNECTION_LOCK (connection);
+
+  retval = _dbus_object_tree_register (connection->objects,
+                                       TRUE,
+                                      (const char **) decomposed_path, vtable,
+                                       user_data, &error);
+
+  CONNECTION_UNLOCK (connection);
+
+  dbus_free_string_array (decomposed_path);
+
+  if (dbus_error_has_name (&error, DBUS_ERROR_ADDRESS_IN_USE))
+    {
+      _dbus_warn ("%s\n", error.message);
+      dbus_error_free (&error);
+      return FALSE;
+    }
+
+  return retval;
+}
+
+/**
+ * Unregisters the handler registered with exactly the given path.
+ * It's a bug to call this function for a path that isn't registered.
+ * Can unregister both fallback paths and object paths.
+ *
+ * @param connection the connection
+ * @param path a '/' delimited string of path elements
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_connection_unregister_object_path (DBusConnection              *connection,
+                                        const char                  *path)
+{
+  char **decomposed_path;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (path != NULL, FALSE);
+  _dbus_return_val_if_fail (path[0] == '/', FALSE);
+
+  if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
+      return FALSE;
+
+  CONNECTION_LOCK (connection);
+
+  _dbus_object_tree_unregister_and_unlock (connection->objects, (const char **) decomposed_path);
+
+  dbus_free_string_array (decomposed_path);
+
+  return TRUE;
+}
+
+/**
+ * Gets the user data passed to dbus_connection_register_object_path()
+ * or dbus_connection_register_fallback(). If nothing was registered
+ * at this path, the data is filled in with #NULL.
+ *
+ * @param connection the connection
+ * @param path the path you registered with
+ * @param data_p location to store the user data, or #NULL
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_connection_get_object_path_data (DBusConnection *connection,
+                                      const char     *path,
+                                      void          **data_p)
+{
+  char **decomposed_path;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (path != NULL, FALSE);
+  _dbus_return_val_if_fail (data_p != NULL, FALSE);
+
+  *data_p = NULL;
+  
+  if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
+    return FALSE;
+  
+  CONNECTION_LOCK (connection);
+
+  *data_p = _dbus_object_tree_get_user_data_unlocked (connection->objects, (const char**) decomposed_path);
+
+  CONNECTION_UNLOCK (connection);
+
+  dbus_free_string_array (decomposed_path);
+
+  return TRUE;
+}
+
+/**
+ * Lists the registered fallback handlers and object path handlers at
+ * the given parent_path. The returned array should be freed with
+ * dbus_free_string_array().
+ *
+ * @param connection the connection
+ * @param parent_path the path to list the child handlers of
+ * @param child_entries returns #NULL-terminated array of children
+ * @returns #FALSE if no memory to allocate the child entries
+ */
+dbus_bool_t
+dbus_connection_list_registered (DBusConnection              *connection,
+                                 const char                  *parent_path,
+                                 char                      ***child_entries)
+{
+  char **decomposed_path;
+  dbus_bool_t retval;
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (parent_path != NULL, FALSE);
+  _dbus_return_val_if_fail (parent_path[0] == '/', FALSE);
+  _dbus_return_val_if_fail (child_entries != NULL, FALSE);
+
+  if (!_dbus_decompose_path (parent_path, strlen (parent_path), &decomposed_path, NULL))
+    return FALSE;
+
+  CONNECTION_LOCK (connection);
+
+  retval = _dbus_object_tree_list_registered_and_unlock (connection->objects,
+                                                        (const char **) decomposed_path,
+                                                        child_entries);
+  dbus_free_string_array (decomposed_path);
+
+  return retval;
+}
+
+static DBusDataSlotAllocator slot_allocator;
+_DBUS_DEFINE_GLOBAL_LOCK (connection_slots);
+
+/**
+ * Allocates an integer ID to be used for storing application-specific
+ * data on any DBusConnection. The allocated ID may then be used
+ * with dbus_connection_set_data() and dbus_connection_get_data().
+ * The passed-in slot must be initialized to -1, and is filled in
+ * with the slot ID. If the passed-in slot is not -1, it's assumed
+ * to be already allocated, and its refcount is incremented.
+ * 
+ * The allocated slot is global, i.e. all DBusConnection objects will
+ * have a slot with the given integer ID reserved.
+ *
+ * @param slot_p address of a global variable storing the slot
+ * @returns #FALSE on failure (no memory)
+ */
+dbus_bool_t
+dbus_connection_allocate_data_slot (dbus_int32_t *slot_p)
+{
+  return _dbus_data_slot_allocator_alloc (&slot_allocator,
+                                          &_DBUS_LOCK_NAME (connection_slots),
+                                          slot_p);
+}
+
+/**
+ * Deallocates a global ID for connection data slots.
+ * dbus_connection_get_data() and dbus_connection_set_data() may no
+ * longer be used with this slot.  Existing data stored on existing
+ * DBusConnection objects will be freed when the connection is
+ * finalized, but may not be retrieved (and may only be replaced if
+ * someone else reallocates the slot).  When the refcount on the
+ * passed-in slot reaches 0, it is set to -1.
+ *
+ * @param slot_p address storing the slot to deallocate
+ */
+void
+dbus_connection_free_data_slot (dbus_int32_t *slot_p)
+{
+  _dbus_return_if_fail (*slot_p >= 0);
+  
+  _dbus_data_slot_allocator_free (&slot_allocator, slot_p);
+}
+
+/**
+ * Stores a pointer on a DBusConnection, along
+ * with an optional function to be used for freeing
+ * the data when the data is set again, or when
+ * the connection is finalized. The slot number
+ * must have been allocated with dbus_connection_allocate_data_slot().
+ *
+ * @param connection the connection
+ * @param slot the slot number
+ * @param data the data to store
+ * @param free_data_func finalizer function for the data
+ * @returns #TRUE if there was enough memory to store the data
+ */
+dbus_bool_t
+dbus_connection_set_data (DBusConnection   *connection,
+                          dbus_int32_t      slot,
+                          void             *data,
+                          DBusFreeFunction  free_data_func)
+{
+  DBusFreeFunction old_free_func;
+  void *old_data;
+  dbus_bool_t retval;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (slot >= 0, FALSE);
+  
+  CONNECTION_LOCK (connection);
+
+  retval = _dbus_data_slot_list_set (&slot_allocator,
+                                     &connection->slot_list,
+                                     slot, data, free_data_func,
+                                     &old_free_func, &old_data);
+  
+  CONNECTION_UNLOCK (connection);
+
+  if (retval)
+    {
+      /* Do the actual free outside the connection lock */
+      if (old_free_func)
+        (* old_free_func) (old_data);
+    }
+
+  return retval;
+}
+
+/**
+ * Retrieves data previously set with dbus_connection_set_data().
+ * The slot must still be allocated (must not have been freed).
+ *
+ * @param connection the connection
+ * @param slot the slot to get data from
+ * @returns the data, or #NULL if not found
+ */
+void*
+dbus_connection_get_data (DBusConnection   *connection,
+                          dbus_int32_t      slot)
+{
+  void *res;
+
+  _dbus_return_val_if_fail (connection != NULL, NULL);
+  
+  CONNECTION_LOCK (connection);
+
+  res = _dbus_data_slot_list_get (&slot_allocator,
+                                  &connection->slot_list,
+                                  slot);
+  
+  CONNECTION_UNLOCK (connection);
+
+  return res;
+}
+
+/**
+ * This function sets a global flag for whether dbus_connection_new()
+ * will set SIGPIPE behavior to SIG_IGN.
+ *
+ * @param will_modify_sigpipe #TRUE to allow sigpipe to be set to SIG_IGN
+ */
+void
+dbus_connection_set_change_sigpipe (dbus_bool_t will_modify_sigpipe)
+{  
+  _dbus_modify_sigpipe = will_modify_sigpipe != FALSE;
+}
+
+/**
+ * Specifies the maximum size message this connection is allowed to
+ * receive. Larger messages will result in disconnecting the
+ * connection.
+ * 
+ * @param connection a #DBusConnection
+ * @param size maximum message size the connection can receive, in bytes
+ */
+void
+dbus_connection_set_max_message_size (DBusConnection *connection,
+                                      long            size)
+{
+  _dbus_return_if_fail (connection != NULL);
+  
+  CONNECTION_LOCK (connection);
+  _dbus_transport_set_max_message_size (connection->transport,
+                                        size);
+  CONNECTION_UNLOCK (connection);
+}
+
+/**
+ * Gets the value set by dbus_connection_set_max_message_size().
+ *
+ * @param connection the connection
+ * @returns the max size of a single message
+ */
+long
+dbus_connection_get_max_message_size (DBusConnection *connection)
+{
+  long res;
+
+  _dbus_return_val_if_fail (connection != NULL, 0);
+  
+  CONNECTION_LOCK (connection);
+  res = _dbus_transport_get_max_message_size (connection->transport);
+  CONNECTION_UNLOCK (connection);
+  return res;
+}
+
+/**
+ * Sets the maximum total number of bytes that can be used for all messages
+ * received on this connection. Messages count toward the maximum until
+ * they are finalized. When the maximum is reached, the connection will
+ * not read more data until some messages are finalized.
+ *
+ * The semantics of the maximum are: if outstanding messages are
+ * already above the maximum, additional messages will not be read.
+ * The semantics are not: if the next message would cause us to exceed
+ * the maximum, we don't read it. The reason is that we don't know the
+ * size of a message until after we read it.
+ *
+ * Thus, the max live messages size can actually be exceeded
+ * by up to the maximum size of a single message.
+ * 
+ * Also, if we read say 1024 bytes off the wire in a single read(),
+ * and that contains a half-dozen small messages, we may exceed the
+ * size max by that amount. But this should be inconsequential.
+ *
+ * This does imply that we can't call read() with a buffer larger
+ * than we're willing to exceed this limit by.
+ *
+ * @param connection the connection
+ * @param size the maximum size in bytes of all outstanding messages
+ */
+void
+dbus_connection_set_max_received_size (DBusConnection *connection,
+                                       long            size)
+{
+  _dbus_return_if_fail (connection != NULL);
+  
+  CONNECTION_LOCK (connection);
+  _dbus_transport_set_max_received_size (connection->transport,
+                                         size);
+  CONNECTION_UNLOCK (connection);
+}
+
+/**
+ * Gets the value set by dbus_connection_set_max_received_size().
+ *
+ * @param connection the connection
+ * @returns the max size of all live messages
+ */
+long
+dbus_connection_get_max_received_size (DBusConnection *connection)
+{
+  long res;
+
+  _dbus_return_val_if_fail (connection != NULL, 0);
+  
+  CONNECTION_LOCK (connection);
+  res = _dbus_transport_get_max_received_size (connection->transport);
+  CONNECTION_UNLOCK (connection);
+  return res;
+}
+
+/**
+ * Gets the approximate size in bytes of all messages in the outgoing
+ * message queue. The size is approximate in that you shouldn't use
+ * it to decide how many bytes to read off the network or anything
+ * of that nature, as optimizations may choose to tell small white lies
+ * to avoid performance overhead.
+ *
+ * @param connection the connection
+ * @returns the number of bytes that have been queued up but not sent
+ */
+long
+dbus_connection_get_outgoing_size (DBusConnection *connection)
+{
+  long res;
+
+  _dbus_return_val_if_fail (connection != NULL, 0);
+  
+  CONNECTION_LOCK (connection);
+  res = _dbus_counter_get_value (connection->outgoing_counter);
+  CONNECTION_UNLOCK (connection);
+  return res;
+}
+
+/** @} */
diff --git a/src/dbus/dbus-connection.h b/src/dbus/dbus-connection.h
new file mode 100644 (file)
index 0000000..b8fd35f
--- /dev/null
@@ -0,0 +1,406 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-connection.h DBusConnection object
+ *
+ * Copyright (C) 2002, 2003  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_CONNECTION_H
+#define DBUS_CONNECTION_H
+
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-memory.h>
+#include <dbus/dbus-message.h>
+#include <dbus/dbus-shared.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusConnection
+ * @{
+ */
+
+/* documented in dbus-watch.c */
+typedef struct DBusWatch DBusWatch;
+/* documented in dbus-timeout.c */
+typedef struct DBusTimeout DBusTimeout;
+/** Opaque type representing preallocated resources so a message can be sent without further memory allocation. */
+typedef struct DBusPreallocatedSend DBusPreallocatedSend;
+/** Opaque type representing a method call that has not yet received a reply. */
+typedef struct DBusPendingCall DBusPendingCall;
+/** Opaque type representing a connection to a remote application and associated incoming/outgoing message queues. */
+typedef struct DBusConnection DBusConnection;
+/** Set of functions that must be implemented to handle messages sent to a particular object path. */
+typedef struct DBusObjectPathVTable DBusObjectPathVTable;
+
+/**
+ * Indicates the status of a #DBusWatch.
+ */
+typedef enum
+{
+  DBUS_WATCH_READABLE = 1 << 0, /**< As in POLLIN */
+  DBUS_WATCH_WRITABLE = 1 << 1, /**< As in POLLOUT */
+  DBUS_WATCH_ERROR    = 1 << 2, /**< As in POLLERR (can't watch for
+                                 *   this, but can be present in
+                                 *   current state passed to
+                                 *   dbus_watch_handle()).
+                                 */
+  DBUS_WATCH_HANGUP   = 1 << 3  /**< As in POLLHUP (can't watch for
+                                 *   it, but can be present in current
+                                 *   state passed to
+                                 *   dbus_watch_handle()).
+                                 */
+} DBusWatchFlags;
+
+/**
+ * Indicates the status of incoming data on a #DBusConnection. This determines whether
+ * dbus_connection_dispatch() needs to be called.
+ */
+typedef enum
+{
+  DBUS_DISPATCH_DATA_REMAINS,  /**< There is more data to potentially convert to messages. */
+  DBUS_DISPATCH_COMPLETE,      /**< All currently available data has been processed. */
+  DBUS_DISPATCH_NEED_MEMORY    /**< More memory is needed to continue. */
+} DBusDispatchStatus;
+
+/** Called when libdbus needs a new watch to be monitored by the main
+ * loop. Returns #FALSE if it lacks enough memory to add the
+ * watch. Set by dbus_connection_set_watch_functions() or
+ * dbus_server_set_watch_functions().
+ */
+typedef dbus_bool_t (* DBusAddWatchFunction)       (DBusWatch      *watch,
+                                                    void           *data);
+/** Called when dbus_watch_get_enabled() may return a different value
+ *  than it did before.  Set by dbus_connection_set_watch_functions()
+ *  or dbus_server_set_watch_functions().
+ */
+typedef void        (* DBusWatchToggledFunction)   (DBusWatch      *watch,
+                                                    void           *data);
+/** Called when libdbus no longer needs a watch to be monitored by the
+ * main loop. Set by dbus_connection_set_watch_functions() or
+ * dbus_server_set_watch_functions().
+ */
+typedef void        (* DBusRemoveWatchFunction)    (DBusWatch      *watch,
+                                                    void           *data);
+/** Called when libdbus needs a new timeout to be monitored by the main
+ * loop. Returns #FALSE if it lacks enough memory to add the
+ * watch. Set by dbus_connection_set_timeout_functions() or
+ * dbus_server_set_timeout_functions().
+ */
+typedef dbus_bool_t (* DBusAddTimeoutFunction)     (DBusTimeout    *timeout,
+                                                    void           *data);
+/** Called when dbus_timeout_get_enabled() may return a different
+ * value than it did before.
+ * Set by dbus_connection_set_timeout_functions() or
+ * dbus_server_set_timeout_functions().
+ */
+typedef void        (* DBusTimeoutToggledFunction) (DBusTimeout    *timeout,
+                                                    void           *data);
+/** Called when libdbus no longer needs a timeout to be monitored by the
+ * main loop. Set by dbus_connection_set_timeout_functions() or
+ * dbus_server_set_timeout_functions().
+ */
+typedef void        (* DBusRemoveTimeoutFunction)  (DBusTimeout    *timeout,
+                                                    void           *data);
+/** Called when the return value of dbus_connection_get_dispatch_status()
+ * may have changed. Set with dbus_connection_set_dispatch_status_function().
+ */
+typedef void        (* DBusDispatchStatusFunction) (DBusConnection *connection,
+                                                    DBusDispatchStatus new_status,
+                                                    void           *data);
+/**
+ * Called when the main loop's thread should be notified that there's now work
+ * to do. Set with dbus_connection_set_wakeup_main_function().
+ */
+typedef void        (* DBusWakeupMainFunction)     (void           *data);
+
+/**
+ * Called during authentication to check whether the given UNIX user
+ * ID is allowed to connect, if the client tried to auth as a UNIX
+ * user ID. Normally on Windows this would never happen. Set with
+ * dbus_connection_set_unix_user_function().
+ */ 
+typedef dbus_bool_t (* DBusAllowUnixUserFunction)  (DBusConnection *connection,
+                                                    unsigned long   uid,
+                                                    void           *data);
+
+/**
+ * Called during authentication to check whether the given Windows user
+ * ID is allowed to connect, if the client tried to auth as a Windows
+ * user ID. Normally on UNIX this would never happen. Set with
+ * dbus_connection_set_windows_user_function().
+ */ 
+typedef dbus_bool_t (* DBusAllowWindowsUserFunction)  (DBusConnection *connection,
+                                                       const char     *user_sid,
+                                                       void           *data);
+
+
+/**
+ * Called when a pending call now has a reply available. Set with
+ * dbus_pending_call_set_notify().
+ */
+typedef void (* DBusPendingCallNotifyFunction) (DBusPendingCall *pending,
+                                                void            *user_data);
+
+/**
+ * Called when a message needs to be handled. The result indicates whether or
+ * not more handlers should be run. Set with dbus_connection_add_filter().
+ */
+typedef DBusHandlerResult (* DBusHandleMessageFunction) (DBusConnection     *connection,
+                                                         DBusMessage        *message,
+                                                         void               *user_data);
+
+DBusConnection*    dbus_connection_open                         (const char                 *address,
+                                                                 DBusError                  *error);
+DBusConnection*    dbus_connection_open_private                 (const char                 *address,
+                                                                 DBusError                  *error);
+DBusConnection*    dbus_connection_ref                          (DBusConnection             *connection);
+void               dbus_connection_unref                        (DBusConnection             *connection);
+void               dbus_connection_close                        (DBusConnection             *connection);
+dbus_bool_t        dbus_connection_get_is_connected             (DBusConnection             *connection);
+dbus_bool_t        dbus_connection_get_is_authenticated         (DBusConnection             *connection);
+dbus_bool_t        dbus_connection_get_is_anonymous             (DBusConnection             *connection);
+char*              dbus_connection_get_server_id                (DBusConnection             *connection);
+void               dbus_connection_set_exit_on_disconnect       (DBusConnection             *connection,
+                                                                 dbus_bool_t                 exit_on_disconnect);
+void               dbus_connection_flush                        (DBusConnection             *connection);
+dbus_bool_t        dbus_connection_read_write_dispatch          (DBusConnection             *connection,
+                                                                 int                         timeout_milliseconds);
+dbus_bool_t        dbus_connection_read_write                   (DBusConnection             *connection,
+                                                                 int                         timeout_milliseconds);
+DBusMessage*       dbus_connection_borrow_message               (DBusConnection             *connection);
+void               dbus_connection_return_message               (DBusConnection             *connection,
+                                                                 DBusMessage                *message);
+void               dbus_connection_steal_borrowed_message       (DBusConnection             *connection,
+                                                                 DBusMessage                *message);
+DBusMessage*       dbus_connection_pop_message                  (DBusConnection             *connection);
+DBusDispatchStatus dbus_connection_get_dispatch_status          (DBusConnection             *connection);
+DBusDispatchStatus dbus_connection_dispatch                     (DBusConnection             *connection);
+dbus_bool_t        dbus_connection_has_messages_to_send         (DBusConnection *connection);
+dbus_bool_t        dbus_connection_send                         (DBusConnection             *connection,
+                                                                 DBusMessage                *message,
+                                                                 dbus_uint32_t              *client_serial);
+dbus_bool_t        dbus_connection_send_with_reply              (DBusConnection             *connection,
+                                                                 DBusMessage                *message,
+                                                                 DBusPendingCall           **pending_return,
+                                                                 int                         timeout_milliseconds);
+DBusMessage *      dbus_connection_send_with_reply_and_block    (DBusConnection             *connection,
+                                                                 DBusMessage                *message,
+                                                                 int                         timeout_milliseconds,
+                                                                 DBusError                  *error);
+dbus_bool_t        dbus_connection_set_watch_functions          (DBusConnection             *connection,
+                                                                 DBusAddWatchFunction        add_function,
+                                                                 DBusRemoveWatchFunction     remove_function,
+                                                                 DBusWatchToggledFunction    toggled_function,
+                                                                 void                       *data,
+                                                                 DBusFreeFunction            free_data_function);
+dbus_bool_t        dbus_connection_set_timeout_functions        (DBusConnection             *connection,
+                                                                 DBusAddTimeoutFunction      add_function,
+                                                                 DBusRemoveTimeoutFunction   remove_function,
+                                                                 DBusTimeoutToggledFunction  toggled_function,
+                                                                 void                       *data,
+                                                                 DBusFreeFunction            free_data_function);
+void               dbus_connection_set_wakeup_main_function     (DBusConnection             *connection,
+                                                                 DBusWakeupMainFunction      wakeup_main_function,
+                                                                 void                       *data,
+                                                                 DBusFreeFunction            free_data_function);
+void               dbus_connection_set_dispatch_status_function (DBusConnection             *connection,
+                                                                 DBusDispatchStatusFunction  function,
+                                                                 void                       *data,
+                                                                 DBusFreeFunction            free_data_function);
+dbus_bool_t        dbus_connection_get_unix_user                (DBusConnection             *connection,
+                                                                 unsigned long              *uid);
+dbus_bool_t        dbus_connection_get_unix_process_id          (DBusConnection             *connection,
+                                                                 unsigned long              *pid);
+dbus_bool_t        dbus_connection_get_adt_audit_session_data   (DBusConnection             *connection,
+                                                                 void                      **data,
+                                                                 dbus_int32_t               *data_size);
+void               dbus_connection_set_unix_user_function       (DBusConnection             *connection,
+                                                                 DBusAllowUnixUserFunction   function,
+                                                                 void                       *data,
+                                                                 DBusFreeFunction            free_data_function);
+dbus_bool_t        dbus_connection_get_windows_user             (DBusConnection             *connection,
+                                                                 char                      **windows_sid_p); 
+void               dbus_connection_set_windows_user_function    (DBusConnection             *connection,
+                                                                 DBusAllowWindowsUserFunction function,
+                                                                 void                       *data,
+                                                                 DBusFreeFunction            free_data_function);
+void               dbus_connection_set_allow_anonymous          (DBusConnection             *connection,
+                                                                 dbus_bool_t                 value);
+void               dbus_connection_set_route_peer_messages      (DBusConnection             *connection,
+                                                                 dbus_bool_t                 value);
+
+
+/* Filters */
+
+dbus_bool_t dbus_connection_add_filter    (DBusConnection            *connection,
+                                           DBusHandleMessageFunction  function,
+                                           void                      *user_data,
+                                           DBusFreeFunction           free_data_function);
+void        dbus_connection_remove_filter (DBusConnection            *connection,
+                                           DBusHandleMessageFunction  function,
+                                           void                      *user_data);
+
+
+/* Other */
+dbus_bool_t dbus_connection_allocate_data_slot (dbus_int32_t     *slot_p);
+void        dbus_connection_free_data_slot     (dbus_int32_t     *slot_p);
+dbus_bool_t dbus_connection_set_data           (DBusConnection   *connection,
+                                                dbus_int32_t      slot,
+                                                void             *data,
+                                                DBusFreeFunction  free_data_func);
+void*       dbus_connection_get_data           (DBusConnection   *connection,
+                                                dbus_int32_t      slot);
+
+void        dbus_connection_set_change_sigpipe (dbus_bool_t       will_modify_sigpipe); 
+
+void dbus_connection_set_max_message_size  (DBusConnection *connection,
+                                            long            size);
+long dbus_connection_get_max_message_size  (DBusConnection *connection);
+void dbus_connection_set_max_received_size (DBusConnection *connection,
+                                            long            size);
+long dbus_connection_get_max_received_size (DBusConnection *connection);
+long dbus_connection_get_outgoing_size     (DBusConnection *connection);
+
+DBusPreallocatedSend* dbus_connection_preallocate_send       (DBusConnection       *connection);
+void                  dbus_connection_free_preallocated_send (DBusConnection       *connection,
+                                                              DBusPreallocatedSend *preallocated);
+void                  dbus_connection_send_preallocated      (DBusConnection       *connection,
+                                                              DBusPreallocatedSend *preallocated,
+                                                              DBusMessage          *message,
+                                                              dbus_uint32_t        *client_serial);
+
+
+/* Object tree functionality */
+
+/**
+ * Called when a #DBusObjectPathVTable is unregistered (or its connection is freed).
+ * Found in #DBusObjectPathVTable.
+ */
+typedef void              (* DBusObjectPathUnregisterFunction) (DBusConnection  *connection,
+                                                                void            *user_data);
+/**
+ * Called when a message is sent to a registered object path. Found in
+ * #DBusObjectPathVTable which is registered with dbus_connection_register_object_path()
+ * or dbus_connection_register_fallback().
+ */
+typedef DBusHandlerResult (* DBusObjectPathMessageFunction)    (DBusConnection  *connection,
+                                                                DBusMessage     *message,
+                                                                void            *user_data);
+
+/**
+ * Virtual table that must be implemented to handle a portion of the
+ * object path hierarchy. Attach the vtable to a particular path using
+ * dbus_connection_register_object_path() or
+ * dbus_connection_register_fallback().
+ */
+struct DBusObjectPathVTable
+{
+  DBusObjectPathUnregisterFunction   unregister_function; /**< Function to unregister this handler */
+  DBusObjectPathMessageFunction      message_function; /**< Function to handle messages */
+  
+  void (* dbus_internal_pad1) (void *); /**< Reserved for future expansion */
+  void (* dbus_internal_pad2) (void *); /**< Reserved for future expansion */
+  void (* dbus_internal_pad3) (void *); /**< Reserved for future expansion */
+  void (* dbus_internal_pad4) (void *); /**< Reserved for future expansion */
+};
+
+dbus_bool_t dbus_connection_try_register_object_path (DBusConnection              *connection,
+                                                      const char                  *path,
+                                                      const DBusObjectPathVTable  *vtable,
+                                                      void                        *user_data,
+                                                      DBusError                   *error);
+
+dbus_bool_t dbus_connection_register_object_path   (DBusConnection              *connection,
+                                                    const char                  *path,
+                                                    const DBusObjectPathVTable  *vtable,
+                                                    void                        *user_data);
+
+dbus_bool_t dbus_connection_try_register_fallback (DBusConnection              *connection,
+                                                   const char                  *path,
+                                                   const DBusObjectPathVTable  *vtable,
+                                                   void                        *user_data,
+                                                   DBusError                   *error);
+
+dbus_bool_t dbus_connection_register_fallback      (DBusConnection              *connection,
+                                                    const char                  *path,
+                                                    const DBusObjectPathVTable  *vtable,
+                                                    void                        *user_data);
+dbus_bool_t dbus_connection_unregister_object_path (DBusConnection              *connection,
+                                                    const char                  *path);
+
+dbus_bool_t dbus_connection_get_object_path_data   (DBusConnection              *connection,
+                                                    const char                  *path,
+                                                    void                       **data_p);
+
+dbus_bool_t dbus_connection_list_registered        (DBusConnection              *connection,
+                                                    const char                  *parent_path,
+                                                    char                      ***child_entries);
+
+dbus_bool_t dbus_connection_get_unix_fd            (DBusConnection              *connection,
+                                                    int                         *fd);
+dbus_bool_t dbus_connection_get_socket             (DBusConnection              *connection,
+                                                    int                         *fd);
+
+/** @} */
+
+
+/**
+ * @addtogroup DBusWatch
+ * @{
+ */
+
+#ifndef DBUS_DISABLE_DEPRECATED
+DBUS_DEPRECATED int dbus_watch_get_fd      (DBusWatch        *watch);
+#endif
+
+int          dbus_watch_get_unix_fd (DBusWatch        *watch);
+int          dbus_watch_get_socket  (DBusWatch        *watch);
+unsigned int dbus_watch_get_flags   (DBusWatch        *watch);
+void*        dbus_watch_get_data    (DBusWatch        *watch);
+void         dbus_watch_set_data    (DBusWatch        *watch,
+                                     void             *data,
+                                     DBusFreeFunction  free_data_function);
+dbus_bool_t  dbus_watch_handle      (DBusWatch        *watch,
+                                     unsigned int      flags);
+dbus_bool_t  dbus_watch_get_enabled (DBusWatch        *watch);
+
+/** @} */
+
+/**
+ * @addtogroup DBusTimeout
+ * @{
+ */
+
+int         dbus_timeout_get_interval (DBusTimeout      *timeout);
+void*       dbus_timeout_get_data     (DBusTimeout      *timeout);
+void        dbus_timeout_set_data     (DBusTimeout      *timeout,
+                                       void             *data,
+                                       DBusFreeFunction  free_data_function);
+dbus_bool_t dbus_timeout_handle       (DBusTimeout      *timeout);
+dbus_bool_t dbus_timeout_get_enabled  (DBusTimeout      *timeout);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_CONNECTION_H */
diff --git a/src/dbus/dbus-credentials-util.c b/src/dbus/dbus-credentials-util.c
new file mode 100644 (file)
index 0000000..7430597
--- /dev/null
@@ -0,0 +1,204 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-credentials-util.c Would be in dbus-credentials.c, but only used for tests/bus
+ *
+ * Copyright (C) 2007 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-internals.h"
+#include "dbus-test.h"
+#include "dbus-credentials.h"
+
+/**
+ * @addtogroup DBusCredentials
+ * @{
+ */
+
+/** @} */
+
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-test.h"
+#include <stdio.h>
+#include <string.h>
+
+static DBusCredentials*
+make_credentials(dbus_uid_t  unix_uid,
+                 dbus_pid_t  unix_pid,
+                 const char *windows_sid)
+{
+  DBusCredentials *credentials;
+
+  credentials = _dbus_credentials_new ();
+
+  if (unix_uid != DBUS_UID_UNSET)
+    {
+      if (!_dbus_credentials_add_unix_uid (credentials, unix_uid))
+        {
+          _dbus_credentials_unref (credentials);
+          return NULL;
+        }
+    }
+
+  if (unix_pid != DBUS_PID_UNSET)
+    {
+      if (!_dbus_credentials_add_unix_pid (credentials, unix_pid))
+        {
+          _dbus_credentials_unref (credentials);
+          return NULL;
+        }
+    }
+
+  if (windows_sid != NULL)
+    {
+      if (!_dbus_credentials_add_windows_sid (credentials, windows_sid))
+        {
+          _dbus_credentials_unref (credentials);
+          return NULL;
+        }
+    }
+
+  return credentials;
+}
+
+#define SAMPLE_SID "whatever a windows sid looks like"
+#define OTHER_SAMPLE_SID "whatever else"
+
+dbus_bool_t
+_dbus_credentials_test (const char *test_data_dir)
+{
+  DBusCredentials *creds;
+  DBusCredentials *creds2;
+  
+  if (test_data_dir == NULL)
+    return TRUE;
+
+  creds = make_credentials (12, 511, SAMPLE_SID);
+  if (creds == NULL)
+    _dbus_assert_not_reached ("oom");
+
+  /* test refcounting */
+  _dbus_credentials_ref (creds);
+  _dbus_credentials_unref (creds);
+  
+  _dbus_assert (_dbus_credentials_include (creds, DBUS_CREDENTIAL_UNIX_USER_ID));
+  _dbus_assert (_dbus_credentials_include (creds, DBUS_CREDENTIAL_UNIX_PROCESS_ID));
+  _dbus_assert (_dbus_credentials_include (creds, DBUS_CREDENTIAL_WINDOWS_SID));
+
+  _dbus_assert (_dbus_credentials_get_unix_uid (creds) == 12);
+  _dbus_assert (_dbus_credentials_get_unix_pid (creds) == 511);
+  _dbus_assert (strcmp (_dbus_credentials_get_windows_sid (creds), SAMPLE_SID) == 0);
+
+  _dbus_assert (!_dbus_credentials_are_empty (creds));
+  _dbus_assert (!_dbus_credentials_are_anonymous (creds));
+
+  /* Test copy */
+  creds2 = _dbus_credentials_copy (creds);
+  if (creds2 == NULL)
+    _dbus_assert_not_reached ("oom");
+
+  _dbus_assert (_dbus_credentials_include (creds2, DBUS_CREDENTIAL_UNIX_USER_ID));
+  _dbus_assert (_dbus_credentials_include (creds2, DBUS_CREDENTIAL_UNIX_PROCESS_ID));
+  _dbus_assert (_dbus_credentials_include (creds2, DBUS_CREDENTIAL_WINDOWS_SID));
+
+  _dbus_assert (_dbus_credentials_get_unix_uid (creds2) == 12);
+  _dbus_assert (_dbus_credentials_get_unix_pid (creds2) == 511);
+  _dbus_assert (strcmp (_dbus_credentials_get_windows_sid (creds2), SAMPLE_SID) == 0);  
+
+  _dbus_assert (_dbus_credentials_are_superset (creds, creds2));
+  
+  _dbus_credentials_unref (creds2);
+  
+  /* Same user if both unix and windows are the same */
+  creds2 = make_credentials (12, DBUS_PID_UNSET, SAMPLE_SID);
+  if (creds2 == NULL)
+    _dbus_assert_not_reached ("oom");
+
+  _dbus_assert (_dbus_credentials_same_user (creds, creds2));
+
+  _dbus_credentials_unref (creds2);
+
+  /* Not the same user if Windows is missing */
+  creds2 = make_credentials (12, DBUS_PID_UNSET, NULL);
+  if (creds2 == NULL)
+    _dbus_assert_not_reached ("oom");
+
+  _dbus_assert (!_dbus_credentials_same_user (creds, creds2));
+  _dbus_assert (_dbus_credentials_are_superset (creds, creds2));
+  
+  _dbus_credentials_unref (creds2);
+
+  /* Not the same user if Windows is different */
+  creds2 = make_credentials (12, DBUS_PID_UNSET, OTHER_SAMPLE_SID);
+  if (creds2 == NULL)
+    _dbus_assert_not_reached ("oom");
+
+  _dbus_assert (!_dbus_credentials_same_user (creds, creds2));
+  _dbus_assert (!_dbus_credentials_are_superset (creds, creds2));
+  
+  _dbus_credentials_unref (creds2);
+
+  /* Not the same user if Unix is missing */
+  creds2 = make_credentials (DBUS_UID_UNSET, DBUS_PID_UNSET, SAMPLE_SID);
+  if (creds2 == NULL)
+    _dbus_assert_not_reached ("oom");
+
+  _dbus_assert (!_dbus_credentials_same_user (creds, creds2));
+  _dbus_assert (_dbus_credentials_are_superset (creds, creds2));
+  
+  _dbus_credentials_unref (creds2);
+
+  /* Not the same user if Unix is different */
+  creds2 = make_credentials (15, DBUS_PID_UNSET, SAMPLE_SID);
+  if (creds2 == NULL)
+    _dbus_assert_not_reached ("oom");
+
+  _dbus_assert (!_dbus_credentials_same_user (creds, creds2));
+  _dbus_assert (!_dbus_credentials_are_superset (creds, creds2));
+  
+  _dbus_credentials_unref (creds2);
+
+  /* Not the same user if both are missing */
+  creds2 = make_credentials (DBUS_UID_UNSET, DBUS_PID_UNSET, NULL);
+  if (creds2 == NULL)
+    _dbus_assert_not_reached ("oom");
+
+  _dbus_assert (!_dbus_credentials_same_user (creds, creds2));
+  _dbus_assert (_dbus_credentials_are_superset (creds, creds2));
+  
+  _dbus_credentials_unref (creds2);
+
+  /* Clearing credentials works */
+  _dbus_credentials_clear (creds);
+
+  _dbus_assert (!_dbus_credentials_include (creds, DBUS_CREDENTIAL_UNIX_USER_ID));
+  _dbus_assert (!_dbus_credentials_include (creds, DBUS_CREDENTIAL_UNIX_PROCESS_ID));
+  _dbus_assert (!_dbus_credentials_include (creds, DBUS_CREDENTIAL_WINDOWS_SID));
+
+  _dbus_assert (_dbus_credentials_get_unix_uid (creds) == DBUS_UID_UNSET);
+  _dbus_assert (_dbus_credentials_get_unix_pid (creds) == DBUS_PID_UNSET);
+  _dbus_assert (_dbus_credentials_get_windows_sid (creds) == NULL);
+
+  _dbus_assert (_dbus_credentials_are_empty (creds));
+  _dbus_assert (_dbus_credentials_are_anonymous (creds));
+
+  _dbus_credentials_unref (creds);
+  
+  return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-credentials.c b/src/dbus/dbus-credentials.c
new file mode 100644 (file)
index 0000000..f21ecb2
--- /dev/null
@@ -0,0 +1,507 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-credentials.c Credentials provable through authentication
+ *
+ * Copyright (C) 2007 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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 <config.h>
+#include <string.h>
+#include "dbus-credentials.h"
+#include "dbus-internals.h"
+
+/**
+ * @defgroup DBusCredentials Credentials provable through authentication
+ * @ingroup  DBusInternals
+ * @brief DBusCredentials object
+ *
+ * Credentials are what you have to prove you have in order to
+ * authenticate.  The main credentials right now are a unix user
+ * account, a Windows user account, or a UNIX process ID.
+ */
+
+/**
+ * @defgroup DBusCredentialsInternals Credentials implementation details
+ * @ingroup  DBusInternals
+ * @brief DBusCredentials implementation details
+ *
+ * Private details of credentials code.
+ *
+ * @{
+ */
+
+struct DBusCredentials {
+  int refcount;
+  dbus_uid_t unix_uid;
+  dbus_pid_t unix_pid;
+  char *windows_sid;
+  void *adt_audit_data;
+  dbus_int32_t adt_audit_data_size;
+};
+
+/** @} */
+
+/**
+ * @addtogroup DBusCredentials
+ * @{
+ */
+
+/**
+ * Creates a new credentials object.
+ *
+ * @returns the new object or #NULL if no memory
+ */
+DBusCredentials*
+_dbus_credentials_new (void)
+{
+  DBusCredentials *creds;
+
+  creds = dbus_new (DBusCredentials, 1);
+  if (creds == NULL)
+    return NULL;
+  
+  creds->refcount = 1;
+  creds->unix_uid = DBUS_UID_UNSET;
+  creds->unix_pid = DBUS_PID_UNSET;
+  creds->windows_sid = NULL;
+  creds->adt_audit_data = NULL;
+  creds->adt_audit_data_size = 0;
+
+  return creds;
+}
+
+/**
+ * Creates a new object with credentials (user ID and process ID) from the current process.
+ * @returns the new object or #NULL if no memory
+ */
+DBusCredentials*
+_dbus_credentials_new_from_current_process (void)
+{
+  DBusCredentials *creds;
+
+  creds = _dbus_credentials_new ();
+  if (creds == NULL)
+    return NULL;
+
+  if (!_dbus_credentials_add_from_current_process (creds))
+    {
+      _dbus_credentials_unref (creds);
+      return NULL;
+    }
+  
+  return creds;
+}
+
+/**
+ * Increment refcount on credentials.
+ *
+ * @param credentials the object
+ */
+void
+_dbus_credentials_ref (DBusCredentials *credentials)
+{
+  _dbus_assert (credentials->refcount > 0);
+  credentials->refcount += 1;
+}
+
+/**
+ * Decrement refcount on credentials.
+ *
+ * @param credentials the object
+ */
+void
+_dbus_credentials_unref (DBusCredentials    *credentials)
+{
+  _dbus_assert (credentials->refcount > 0);
+
+  credentials->refcount -= 1;
+  if (credentials->refcount == 0)
+    {
+      dbus_free (credentials->windows_sid);
+      dbus_free (credentials->adt_audit_data);
+      dbus_free (credentials);
+    }
+}
+
+/**
+ * Add a UNIX process ID to the credentials.
+ *
+ * @param credentials the object
+ * @param pid the process ID
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_credentials_add_unix_pid (DBusCredentials    *credentials,
+                                dbus_pid_t          pid)
+{
+  credentials->unix_pid = pid;
+  return TRUE;
+}
+
+/**
+ * Add a UNIX user ID to the credentials.
+ *
+ * @param credentials the object
+ * @param uid the user ID
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_credentials_add_unix_uid(DBusCredentials    *credentials,
+                               dbus_uid_t          uid)
+{
+  credentials->unix_uid = uid;
+  return TRUE;
+
+}
+
+/**
+ * Add a Windows user SID to the credentials.
+ *
+ * @param credentials the object
+ * @param windows_sid the user SID
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_credentials_add_windows_sid (DBusCredentials    *credentials,
+                                   const char         *windows_sid)
+{
+  char *copy;
+
+  copy = _dbus_strdup (windows_sid);
+  if (copy == NULL)
+    return FALSE;
+
+  dbus_free (credentials->windows_sid);
+  credentials->windows_sid = copy;
+
+  return TRUE;
+}
+
+/**
+ * Add ADT audit data to the credentials.
+ *
+ * @param credentials the object
+ * @param audit_data the audit data
+ * @param size the length of audit data
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_credentials_add_adt_audit_data (DBusCredentials    *credentials,
+                                      void               *audit_data,
+                                      dbus_int32_t        size)
+{
+  void *copy;
+  copy = _dbus_memdup (audit_data, size);
+  if (copy == NULL)
+    return FALSE;
+
+  dbus_free (credentials->adt_audit_data);
+  credentials->adt_audit_data = copy;
+  credentials->adt_audit_data_size = size;
+
+  return TRUE;
+}
+
+/**
+ * Checks whether the given credential is present.
+ *
+ * @param credentials the object
+ * @param type the credential to check for
+ * @returns #TRUE if the credential is present
+ */
+dbus_bool_t
+_dbus_credentials_include (DBusCredentials    *credentials,
+                           DBusCredentialType  type)
+{
+  switch (type)
+    {
+    case DBUS_CREDENTIAL_UNIX_PROCESS_ID:
+      return credentials->unix_pid != DBUS_PID_UNSET;
+    case DBUS_CREDENTIAL_UNIX_USER_ID:
+      return credentials->unix_uid != DBUS_UID_UNSET;
+    case DBUS_CREDENTIAL_WINDOWS_SID:
+      return credentials->windows_sid != NULL;
+    case DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID:
+      return credentials->adt_audit_data != NULL;
+    }
+
+  _dbus_assert_not_reached ("Unknown credential enum value");
+  return FALSE;
+}
+
+/**
+ * Gets the UNIX process ID in the credentials, or #DBUS_PID_UNSET if
+ * the credentials object doesn't contain a process ID.
+ *
+ * @param credentials the object
+ * @returns UNIX process ID
+ */
+dbus_pid_t
+_dbus_credentials_get_unix_pid (DBusCredentials    *credentials)
+{
+  return credentials->unix_pid;
+}
+
+/**
+ * Gets the UNIX user ID in the credentials, or #DBUS_UID_UNSET if
+ * the credentials object doesn't contain a user ID.
+ *
+ * @param credentials the object
+ * @returns UNIX user ID
+ */
+dbus_uid_t
+_dbus_credentials_get_unix_uid (DBusCredentials    *credentials)
+{
+  return credentials->unix_uid;
+}
+
+/**
+ * Gets the Windows user SID in the credentials, or #NULL if
+ * the credentials object doesn't contain a Windows user SID.
+ *
+ * @param credentials the object
+ * @returns Windows user SID
+ */
+const char*
+_dbus_credentials_get_windows_sid (DBusCredentials    *credentials)
+{
+  return credentials->windows_sid;
+}
+
+/**
+ * Gets the ADT audit data in the credentials, or #NULL if
+ * the credentials object doesn't contain ADT audit data.
+ *
+ * @param credentials the object
+ * @returns Solaris ADT audit data 
+ */
+void *
+_dbus_credentials_get_adt_audit_data (DBusCredentials    *credentials)
+{
+  return credentials->adt_audit_data;
+}
+
+/**
+ * Gets the ADT audit data size in the credentials, or 0 if
+ * the credentials object doesn't contain ADT audit data.
+ *
+ * @param credentials the object
+ * @returns Solaris ADT audit data size
+ */
+dbus_int32_t 
+_dbus_credentials_get_adt_audit_data_size (DBusCredentials    *credentials)
+{
+  return credentials->adt_audit_data_size;
+}
+
+/**
+ * Checks whether the first credentials object contains
+ * all the credentials found in the second credentials object.
+ *
+ * @param credentials the object
+ * @param possible_subset see if credentials in here are also in the first arg
+ * @returns #TRUE if second arg is contained in first
+ */
+dbus_bool_t
+_dbus_credentials_are_superset (DBusCredentials    *credentials,
+                                DBusCredentials    *possible_subset)
+{
+  return
+    (possible_subset->unix_pid == DBUS_PID_UNSET ||
+     possible_subset->unix_pid == credentials->unix_pid) &&
+    (possible_subset->unix_uid == DBUS_UID_UNSET ||
+     possible_subset->unix_uid == credentials->unix_uid) &&
+    (possible_subset->windows_sid == NULL ||
+     (credentials->windows_sid && strcmp (possible_subset->windows_sid,
+                                          credentials->windows_sid) == 0)) &&
+    (possible_subset->adt_audit_data == NULL ||
+     (credentials->adt_audit_data && memcmp (possible_subset->adt_audit_data,
+                                             credentials->adt_audit_data,
+                                             credentials->adt_audit_data_size) == 0));
+}
+
+/**
+ * Checks whether a credentials object contains anything.
+ * 
+ * @param credentials the object
+ * @returns #TRUE if there are no credentials in the object
+ */
+dbus_bool_t
+_dbus_credentials_are_empty (DBusCredentials    *credentials)
+{
+  return
+    credentials->unix_pid == DBUS_PID_UNSET &&
+    credentials->unix_uid == DBUS_UID_UNSET &&
+    credentials->windows_sid == NULL &&
+    credentials->adt_audit_data == NULL;
+}
+
+/**
+ * Checks whether a credentials object contains a user identity.
+ * 
+ * @param credentials the object
+ * @returns #TRUE if there are no user identities in the object
+ */
+dbus_bool_t
+_dbus_credentials_are_anonymous (DBusCredentials    *credentials)
+{
+  return
+    credentials->unix_uid == DBUS_UID_UNSET &&
+    credentials->windows_sid == NULL;
+}
+
+/**
+ * Merge all credentials found in the second object into the first object,
+ * overwriting the first object if there are any overlaps.
+ * 
+ * @param credentials the object
+ * @param other_credentials credentials to merge
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_credentials_add_credentials (DBusCredentials    *credentials,
+                                   DBusCredentials    *other_credentials)
+{
+  return
+    _dbus_credentials_add_credential (credentials,
+                                      DBUS_CREDENTIAL_UNIX_PROCESS_ID,
+                                      other_credentials) &&
+    _dbus_credentials_add_credential (credentials,
+                                      DBUS_CREDENTIAL_UNIX_USER_ID,
+                                      other_credentials) &&
+    _dbus_credentials_add_credential (credentials,
+                                      DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID,
+                                      other_credentials) &&
+    _dbus_credentials_add_credential (credentials,
+                                      DBUS_CREDENTIAL_WINDOWS_SID,
+                                      other_credentials);
+}
+
+/**
+ * Merge the given credential found in the second object into the first object,
+ * overwriting the first object's value for that credential.
+ *
+ * Does nothing if the second object does not contain the specified credential.
+ * i.e., will never delete a credential from the first object.
+ * 
+ * @param credentials the object
+ * @param which the credential to overwrite
+ * @param other_credentials credentials to merge
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_credentials_add_credential (DBusCredentials    *credentials,
+                                  DBusCredentialType  which,
+                                  DBusCredentials    *other_credentials)
+{
+  if (which == DBUS_CREDENTIAL_UNIX_PROCESS_ID &&
+      other_credentials->unix_pid != DBUS_PID_UNSET)
+    {
+      if (!_dbus_credentials_add_unix_pid (credentials, other_credentials->unix_pid))
+        return FALSE;
+    }
+  else if (which == DBUS_CREDENTIAL_UNIX_USER_ID &&
+           other_credentials->unix_uid != DBUS_UID_UNSET)
+    {
+      if (!_dbus_credentials_add_unix_uid (credentials, other_credentials->unix_uid))
+        return FALSE;
+    }
+  else if (which == DBUS_CREDENTIAL_WINDOWS_SID &&
+           other_credentials->windows_sid != NULL)
+    {
+      if (!_dbus_credentials_add_windows_sid (credentials, other_credentials->windows_sid))
+        return FALSE;
+    } 
+  else if (which == DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID &&
+           other_credentials->adt_audit_data != NULL) 
+    {
+      if (!_dbus_credentials_add_adt_audit_data (credentials, other_credentials->adt_audit_data, other_credentials->adt_audit_data_size))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+/**
+ * Clear all credentials in the object.
+ * 
+ * @param credentials the object
+ */
+void
+_dbus_credentials_clear (DBusCredentials    *credentials)
+{
+  credentials->unix_pid = DBUS_PID_UNSET;
+  credentials->unix_uid = DBUS_UID_UNSET;
+  dbus_free (credentials->windows_sid);
+  credentials->windows_sid = NULL;
+  dbus_free (credentials->adt_audit_data);
+  credentials->adt_audit_data = NULL;
+  credentials->adt_audit_data_size = 0;
+}
+
+/**
+ * Copy a credentials object.
+ * 
+ * @param credentials the object
+ * @returns the copy or #NULL
+ */
+DBusCredentials*
+_dbus_credentials_copy (DBusCredentials    *credentials)
+{
+  DBusCredentials *copy;
+
+  copy = _dbus_credentials_new ();
+  if (copy == NULL)
+    return NULL;
+
+  if (!_dbus_credentials_add_credentials (copy, credentials))
+    {
+      _dbus_credentials_unref (copy);
+      return NULL;
+    }
+
+  return copy;
+}
+
+/**
+ * Check whether the user-identifying credentials in two credentials
+ * objects are identical. Credentials that are not related to the
+ * user are ignored, but any kind of user ID credentials must be the
+ * same (UNIX user ID, Windows user SID, etc.) and present in both
+ * objects for the function to return #TRUE.
+ * 
+ * @param credentials the object
+ * @param other_credentials credentials to compare
+ * @returns #TRUE if the two credentials refer to the same user
+ */
+dbus_bool_t
+_dbus_credentials_same_user (DBusCredentials    *credentials,
+                             DBusCredentials    *other_credentials)
+{
+  /* both windows and unix user must be the same (though pretty much
+   * in all conceivable cases, one will be unset)
+   */
+  return credentials->unix_uid == other_credentials->unix_uid &&
+    ((!(credentials->windows_sid || other_credentials->windows_sid)) ||
+     (credentials->windows_sid && other_credentials->windows_sid &&
+      strcmp (credentials->windows_sid, other_credentials->windows_sid) == 0));
+}
+
+/** @} */
+
+/* tests in dbus-credentials-util.c */
diff --git a/src/dbus/dbus-credentials.h b/src/dbus/dbus-credentials.h
new file mode 100644 (file)
index 0000000..8eed2bd
--- /dev/null
@@ -0,0 +1,78 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-credentials.h Credentials provable through authentication
+ *
+ * Copyright (C) 2007 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_CREDENTIALS_H
+#define DBUS_CREDENTIALS_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-sysdeps.h>
+
+DBUS_BEGIN_DECLS
+
+typedef enum {
+  DBUS_CREDENTIAL_UNIX_PROCESS_ID,
+  DBUS_CREDENTIAL_UNIX_USER_ID,
+  DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID,
+  DBUS_CREDENTIAL_WINDOWS_SID
+} DBusCredentialType;
+
+DBusCredentials* _dbus_credentials_new_from_current_process (void);
+DBusCredentials* _dbus_credentials_new                      (void);
+void             _dbus_credentials_ref                      (DBusCredentials    *credentials);
+void             _dbus_credentials_unref                    (DBusCredentials    *credentials);
+dbus_bool_t      _dbus_credentials_add_unix_pid             (DBusCredentials    *credentials,
+                                                             dbus_pid_t          pid);
+dbus_bool_t      _dbus_credentials_add_unix_uid             (DBusCredentials    *credentials,
+                                                             dbus_uid_t          uid);
+dbus_bool_t      _dbus_credentials_add_windows_sid          (DBusCredentials    *credentials,
+                                                             const char         *windows_sid);
+dbus_bool_t      _dbus_credentials_add_adt_audit_data       (DBusCredentials    *credentials,
+                                                             void               *audit_data,
+                                                             dbus_int32_t        size);
+dbus_bool_t      _dbus_credentials_include                  (DBusCredentials    *credentials,
+                                                             DBusCredentialType  type);
+dbus_pid_t       _dbus_credentials_get_unix_pid             (DBusCredentials    *credentials);
+dbus_uid_t       _dbus_credentials_get_unix_uid             (DBusCredentials    *credentials);
+const char*      _dbus_credentials_get_windows_sid          (DBusCredentials    *credentials);
+void *           _dbus_credentials_get_adt_audit_data       (DBusCredentials    *credentials);
+dbus_int32_t     _dbus_credentials_get_adt_audit_data_size  (DBusCredentials    *credentials);
+dbus_bool_t      _dbus_credentials_are_superset             (DBusCredentials    *credentials,
+                                                             DBusCredentials    *possible_subset);
+dbus_bool_t      _dbus_credentials_are_empty                (DBusCredentials    *credentials);
+dbus_bool_t      _dbus_credentials_are_anonymous            (DBusCredentials    *credentials);
+dbus_bool_t      _dbus_credentials_add_credentials          (DBusCredentials    *credentials,
+                                                             DBusCredentials    *other_credentials);
+/* must silently allow 'which' to not exist */
+dbus_bool_t      _dbus_credentials_add_credential           (DBusCredentials    *credentials,
+                                                             DBusCredentialType  which,
+                                                             DBusCredentials    *other_credentials);
+void             _dbus_credentials_clear                    (DBusCredentials    *credentials);
+DBusCredentials* _dbus_credentials_copy                     (DBusCredentials    *credentials);
+dbus_bool_t      _dbus_credentials_same_user                (DBusCredentials    *credentials,
+                                                             DBusCredentials    *other_credentials);
+
+
+DBUS_END_DECLS
+
+#endif /* DBUS_CREDENTIALS_H */
diff --git a/src/dbus/dbus-dataslot.c b/src/dbus/dbus-dataslot.c
new file mode 100644 (file)
index 0000000..a7ad2e4
--- /dev/null
@@ -0,0 +1,477 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-dataslot.c  storing data on objects
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-dataslot.h"
+#include "dbus-threads-internal.h"
+
+/**
+ * @defgroup DBusDataSlot Data slots
+ * @ingroup  DBusInternals
+ * @brief Storing data by ID
+ *
+ * Types and functions related to storing data by an
+ * allocated ID. This is used for dbus_connection_set_data(),
+ * dbus_server_set_data(), etc. 
+ * @{
+ */
+
+/**
+ * Initializes a data slot allocator object, used to assign
+ * integer IDs for data slots.
+ *
+ * @param allocator the allocator to initialize
+ */
+dbus_bool_t
+_dbus_data_slot_allocator_init (DBusDataSlotAllocator *allocator)
+{
+  allocator->allocated_slots = NULL;
+  allocator->n_allocated_slots = 0;
+  allocator->n_used_slots = 0;
+  allocator->lock_loc = NULL;
+  
+  return TRUE;
+}
+
+/**
+ * Allocates an integer ID to be used for storing data
+ * in a #DBusDataSlotList. If the value at *slot_id_p is
+ * not -1, this function just increments the refcount for
+ * the existing slot ID. If the value is -1, a new slot ID
+ * is allocated and stored at *slot_id_p.
+ * 
+ * @param allocator the allocator
+ * @param mutex_loc the location lock for this allocator
+ * @param slot_id_p address to fill with the slot ID
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_data_slot_allocator_alloc (DBusDataSlotAllocator *allocator,
+                                 DBusMutex             **mutex_loc,
+                                 dbus_int32_t          *slot_id_p)
+{
+  dbus_int32_t slot;
+
+  _dbus_mutex_lock (*mutex_loc);
+
+  if (allocator->n_allocated_slots == 0)
+    {
+      _dbus_assert (allocator->lock_loc == NULL);
+      allocator->lock_loc = mutex_loc;
+    }
+  else if (allocator->lock_loc != mutex_loc)
+    {
+      _dbus_warn_check_failed ("D-Bus threads were initialized after first using the D-Bus library. If your application does not directly initialize threads or use D-Bus, keep in mind that some library or plugin may have used D-Bus or initialized threads behind your back. You can often fix this problem by calling dbus_init_threads() or dbus_g_threads_init() early in your main() method, before D-Bus is used.\n");
+      _dbus_assert_not_reached ("exiting");
+    }
+
+  if (*slot_id_p >= 0)
+    {
+      slot = *slot_id_p;
+      
+      _dbus_assert (slot < allocator->n_allocated_slots);
+      _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
+      
+      allocator->allocated_slots[slot].refcount += 1;
+
+      goto out;
+    }
+
+  _dbus_assert (*slot_id_p < 0);
+  
+  if (allocator->n_used_slots < allocator->n_allocated_slots)
+    {
+      slot = 0;
+      while (slot < allocator->n_allocated_slots)
+        {
+          if (allocator->allocated_slots[slot].slot_id < 0)
+            {
+              allocator->allocated_slots[slot].slot_id = slot;
+              allocator->allocated_slots[slot].refcount = 1;
+              allocator->n_used_slots += 1;
+              break;
+            }
+          ++slot;
+        }
+
+      _dbus_assert (slot < allocator->n_allocated_slots);
+    }
+  else
+    {
+      DBusAllocatedSlot *tmp;
+      
+      slot = -1;
+      tmp = dbus_realloc (allocator->allocated_slots,
+                          sizeof (DBusAllocatedSlot) * (allocator->n_allocated_slots + 1));
+      if (tmp == NULL)
+        goto out;
+
+      allocator->allocated_slots = tmp;
+      slot = allocator->n_allocated_slots;
+      allocator->n_allocated_slots += 1;
+      allocator->n_used_slots += 1;
+      allocator->allocated_slots[slot].slot_id = slot;
+      allocator->allocated_slots[slot].refcount = 1;
+    }
+
+  _dbus_assert (slot >= 0);
+  _dbus_assert (slot < allocator->n_allocated_slots);
+  _dbus_assert (*slot_id_p < 0);
+  _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
+  _dbus_assert (allocator->allocated_slots[slot].refcount == 1);
+  
+  *slot_id_p = slot;
+  
+  _dbus_verbose ("Allocated slot %d on allocator %p total %d slots allocated %d used\n",
+                 slot, allocator, allocator->n_allocated_slots, allocator->n_used_slots);
+  
+ out:
+  _dbus_mutex_unlock (*(allocator->lock_loc));
+  return slot >= 0;
+}
+
+/**
+ * Deallocates an ID previously allocated with
+ * _dbus_data_slot_allocator_alloc().  Existing data stored on
+ * existing #DBusDataSlotList objects with this ID will be freed when the
+ * data list is finalized, but may not be retrieved (and may only be
+ * replaced if someone else reallocates the slot).
+ * The slot value is reset to -1 if this is the last unref.
+ *
+ * @param allocator the allocator
+ * @param slot_id_p address where we store the slot
+ */
+void
+_dbus_data_slot_allocator_free (DBusDataSlotAllocator *allocator,
+                                dbus_int32_t          *slot_id_p)
+{
+  _dbus_mutex_lock (*(allocator->lock_loc));
+  
+  _dbus_assert (*slot_id_p < allocator->n_allocated_slots);
+  _dbus_assert (allocator->allocated_slots[*slot_id_p].slot_id == *slot_id_p);
+  _dbus_assert (allocator->allocated_slots[*slot_id_p].refcount > 0);
+
+  allocator->allocated_slots[*slot_id_p].refcount -= 1;
+
+  if (allocator->allocated_slots[*slot_id_p].refcount > 0)
+    {
+      _dbus_mutex_unlock (*(allocator->lock_loc));
+      return;
+    }
+
+  /* refcount is 0, free the slot */
+  _dbus_verbose ("Freeing slot %d on allocator %p total %d allocated %d used\n",
+                 *slot_id_p, allocator, allocator->n_allocated_slots, allocator->n_used_slots);
+  
+  allocator->allocated_slots[*slot_id_p].slot_id = -1;
+  *slot_id_p = -1;
+  
+  allocator->n_used_slots -= 1;
+  
+  if (allocator->n_used_slots == 0)
+    {
+      DBusMutex **mutex_loc = allocator->lock_loc;
+      
+      dbus_free (allocator->allocated_slots);
+      allocator->allocated_slots = NULL;
+      allocator->n_allocated_slots = 0;
+      allocator->lock_loc = NULL;
+
+      _dbus_mutex_unlock (*mutex_loc);
+    }
+  else
+    {
+      _dbus_mutex_unlock (*(allocator->lock_loc));
+    }
+}
+
+/**
+ * Initializes a slot list.
+ * @param list the list to initialize.
+ */
+void
+_dbus_data_slot_list_init (DBusDataSlotList *list)
+{
+  list->slots = NULL;
+  list->n_slots = 0;
+}
+
+/**
+ * Stores a pointer in the data slot list, along with an optional
+ * function to be used for freeing the data when the data is set
+ * again, or when the slot list is finalized. The slot number must
+ * have been allocated with _dbus_data_slot_allocator_alloc() for the
+ * same allocator passed in here. The same allocator has to be used
+ * with the slot list every time.
+ *
+ * @param allocator the allocator to use
+ * @param list the data slot list
+ * @param slot the slot number
+ * @param data the data to store
+ * @param free_data_func finalizer function for the data
+ * @param old_free_func free function for any previously-existing data
+ * @param old_data previously-existing data, should be freed with old_free_func
+ * @returns #TRUE if there was enough memory to store the data
+ */
+dbus_bool_t
+_dbus_data_slot_list_set  (DBusDataSlotAllocator *allocator,
+                           DBusDataSlotList      *list,
+                           int                    slot,
+                           void                  *data,
+                           DBusFreeFunction       free_data_func,
+                           DBusFreeFunction      *old_free_func,
+                           void                 **old_data)
+{
+#ifndef DBUS_DISABLE_ASSERT
+  /* We need to take the allocator lock here, because the allocator could
+   * be e.g. realloc()ing allocated_slots. We avoid doing this if asserts
+   * are disabled, since then the asserts are empty.
+   */
+  _dbus_mutex_lock (*(allocator->lock_loc));
+  _dbus_assert (slot < allocator->n_allocated_slots);
+  _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
+  _dbus_mutex_unlock (*(allocator->lock_loc));
+#endif
+  
+  if (slot >= list->n_slots)
+    {
+      DBusDataSlot *tmp;
+      int i;
+      
+      tmp = dbus_realloc (list->slots,
+                          sizeof (DBusDataSlot) * (slot + 1));
+      if (tmp == NULL)
+        return FALSE;
+      
+      list->slots = tmp;
+      i = list->n_slots;
+      list->n_slots = slot + 1;
+      while (i < list->n_slots)
+        {
+          list->slots[i].data = NULL;
+          list->slots[i].free_data_func = NULL;
+          ++i;
+        }
+    }
+
+  _dbus_assert (slot < list->n_slots);
+
+  *old_data = list->slots[slot].data;
+  *old_free_func = list->slots[slot].free_data_func;
+
+  list->slots[slot].data = data;
+  list->slots[slot].free_data_func = free_data_func;
+
+  return TRUE;
+}
+
+/**
+ * Retrieves data previously set with _dbus_data_slot_list_set_data().
+ * The slot must still be allocated (must not have been freed).
+ *
+ * @param allocator the allocator slot was allocated from
+ * @param list the data slot list
+ * @param slot the slot to get data from
+ * @returns the data, or #NULL if not found
+ */
+void*
+_dbus_data_slot_list_get  (DBusDataSlotAllocator *allocator,
+                           DBusDataSlotList      *list,
+                           int                    slot)
+{
+#ifndef DBUS_DISABLE_ASSERT
+  /* We need to take the allocator lock here, because the allocator could
+   * be e.g. realloc()ing allocated_slots. We avoid doing this if asserts
+   * are disabled, since then the asserts are empty.
+   */
+  _dbus_mutex_lock (*(allocator->lock_loc));
+  _dbus_assert (slot >= 0);
+  _dbus_assert (slot < allocator->n_allocated_slots);
+  _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
+  _dbus_mutex_unlock (*(allocator->lock_loc));
+#endif
+
+  if (slot >= list->n_slots)
+    return NULL;
+  else
+    return list->slots[slot].data;
+}
+
+/**
+ * Frees all data slots contained in the list, calling
+ * application-provided free functions if they exist.
+ *
+ * @param list the list to clear
+ */
+void
+_dbus_data_slot_list_clear (DBusDataSlotList *list)
+{
+  int i;
+
+  i = 0;
+  while (i < list->n_slots)
+    {
+      if (list->slots[i].free_data_func)
+        (* list->slots[i].free_data_func) (list->slots[i].data);
+      list->slots[i].data = NULL;
+      list->slots[i].free_data_func = NULL;
+      ++i;
+    }
+}
+
+/**
+ * Frees the data slot list and all data slots contained
+ * in it, calling application-provided free functions
+ * if they exist.
+ *
+ * @param list the list to free
+ */
+void
+_dbus_data_slot_list_free (DBusDataSlotList *list)
+{
+  _dbus_data_slot_list_clear (list);
+  
+  dbus_free (list->slots);
+  list->slots = NULL;
+  list->n_slots = 0;
+}
+
+/** @} */
+
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-test.h"
+#include <stdio.h>
+
+static int free_counter;
+
+static void
+test_free_slot_data_func (void *data)
+{
+  int i = _DBUS_POINTER_TO_INT (data);
+
+  _dbus_assert (free_counter == i);
+  ++free_counter;
+}
+
+/**
+ * Test function for data slots
+ */
+dbus_bool_t
+_dbus_data_slot_test (void)
+{
+  DBusDataSlotAllocator allocator;
+  DBusDataSlotList list;
+  int i;
+  DBusFreeFunction old_free_func;
+  void *old_data;
+  DBusMutex *mutex;
+  
+  if (!_dbus_data_slot_allocator_init (&allocator))
+    _dbus_assert_not_reached ("no memory for allocator");
+
+  _dbus_data_slot_list_init (&list);
+
+  _dbus_mutex_new_at_location (&mutex);
+  if (mutex == NULL)
+    _dbus_assert_not_reached ("failed to alloc mutex");
+  
+#define N_SLOTS 100
+
+  i = 0;
+  while (i < N_SLOTS)
+    {
+      /* we don't really want apps to rely on this ordered
+       * allocation, but it simplifies things to rely on it
+       * here.
+       */
+      dbus_int32_t tmp = -1;
+      
+      _dbus_data_slot_allocator_alloc (&allocator, &mutex, &tmp);
+
+      if (tmp != i)
+        _dbus_assert_not_reached ("did not allocate slots in numeric order\n");
+
+      ++i;
+    }
+
+  i = 0;
+  while (i < N_SLOTS)
+    {
+      if (!_dbus_data_slot_list_set (&allocator, &list,
+                                     i,
+                                     _DBUS_INT_TO_POINTER (i), 
+                                     test_free_slot_data_func,
+                                     &old_free_func, &old_data))
+        _dbus_assert_not_reached ("no memory to set data");
+
+      _dbus_assert (old_free_func == NULL);
+      _dbus_assert (old_data == NULL);
+
+      _dbus_assert (_dbus_data_slot_list_get (&allocator, &list, i) ==
+                    _DBUS_INT_TO_POINTER (i));
+      
+      ++i;
+    }
+
+  free_counter = 0;
+  i = 0;
+  while (i < N_SLOTS)
+    {
+      if (!_dbus_data_slot_list_set (&allocator, &list,
+                                     i,
+                                     _DBUS_INT_TO_POINTER (i), 
+                                     test_free_slot_data_func,
+                                     &old_free_func, &old_data))
+        _dbus_assert_not_reached ("no memory to set data");
+
+      _dbus_assert (old_free_func == test_free_slot_data_func);
+      _dbus_assert (_DBUS_POINTER_TO_INT (old_data) == i);
+
+      (* old_free_func) (old_data);
+      _dbus_assert (i == (free_counter - 1));
+
+      _dbus_assert (_dbus_data_slot_list_get (&allocator, &list, i) ==
+                    _DBUS_INT_TO_POINTER (i));
+      
+      ++i;
+    }
+
+  free_counter = 0;
+  _dbus_data_slot_list_free (&list);
+
+  _dbus_assert (N_SLOTS == free_counter);
+
+  i = 0;
+  while (i < N_SLOTS)
+    {
+      dbus_int32_t tmp = i;
+      
+      _dbus_data_slot_allocator_free (&allocator, &tmp);
+      _dbus_assert (tmp == -1);
+      ++i;
+    }
+
+  _dbus_mutex_free_at_location (&mutex);
+  
+  return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-dataslot.h b/src/dbus/dbus-dataslot.h
new file mode 100644 (file)
index 0000000..e3c2099
--- /dev/null
@@ -0,0 +1,96 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-dataslot.h  storing data on objects
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_DATASLOT_H
+#define DBUS_DATASLOT_H
+
+#include <dbus/dbus-internals.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusDataSlotAllocator DBusDataSlotAllocator;
+typedef struct DBusDataSlotList DBusDataSlotList;
+
+/** Opaque typedef for DBusDataSlot */
+typedef struct DBusDataSlot DBusDataSlot;
+/** DBusDataSlot is used to store application data on the connection */
+struct DBusDataSlot
+{
+  void *data;                      /**< The application data */
+  DBusFreeFunction free_data_func; /**< Free the application data */
+};
+
+typedef struct DBusAllocatedSlot DBusAllocatedSlot;
+
+/** An allocated slot for storing data
+ */
+struct DBusAllocatedSlot
+{
+  dbus_int32_t slot_id;  /**< ID of this slot */
+  int          refcount; /**< Number of uses of the slot */
+};
+
+/**
+ * An allocator that tracks a set of slot IDs.
+ */
+struct DBusDataSlotAllocator
+{
+  DBusAllocatedSlot *allocated_slots; /**< Allocated slots */
+  int  n_allocated_slots; /**< number of slots malloc'd */
+  int  n_used_slots;      /**< number of slots used */
+  DBusMutex **lock_loc;   /**< location of thread lock */
+};
+
+/**
+ * Data structure that stores the actual user data set at a given
+ * slot.
+ */
+struct DBusDataSlotList
+{
+  DBusDataSlot *slots;   /**< Data slots */
+  int           n_slots; /**< Slots we have storage for in data_slots */
+};
+
+dbus_bool_t _dbus_data_slot_allocator_init  (DBusDataSlotAllocator  *allocator);
+dbus_bool_t _dbus_data_slot_allocator_alloc (DBusDataSlotAllocator  *allocator,
+                                             DBusMutex              **mutex_loc,
+                                             int                    *slot_id_p);
+void        _dbus_data_slot_allocator_free  (DBusDataSlotAllocator  *allocator,
+                                             int                    *slot_id_p);
+void        _dbus_data_slot_list_init       (DBusDataSlotList       *list);
+dbus_bool_t _dbus_data_slot_list_set        (DBusDataSlotAllocator  *allocator,
+                                             DBusDataSlotList       *list,
+                                             int                     slot,
+                                             void                   *data,
+                                             DBusFreeFunction        free_data_func,
+                                             DBusFreeFunction       *old_free_func,
+                                             void                  **old_data);
+void*       _dbus_data_slot_list_get        (DBusDataSlotAllocator  *allocator,
+                                             DBusDataSlotList       *list,
+                                             int                     slot);
+void        _dbus_data_slot_list_clear      (DBusDataSlotList       *list);
+void        _dbus_data_slot_list_free       (DBusDataSlotList       *list);
+
+
+DBUS_END_DECLS
+
+#endif /* DBUS_DATASLOT_H */
diff --git a/src/dbus/dbus-errors.c b/src/dbus/dbus-errors.c
new file mode 100644 (file)
index 0000000..6d14ff7
--- /dev/null
@@ -0,0 +1,417 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-errors.c Error reporting
+ *
+ * Copyright (C) 2002, 2004  Red Hat Inc.
+ * Copyright (C) 2003  CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-errors.h"
+#include "dbus-internals.h"
+#include "dbus-string.h"
+#include "dbus-protocol.h"
+#include <stdarg.h>
+#include <string.h>
+
+/**
+ * @defgroup DBusErrorInternals Error reporting internals
+ * @ingroup  DBusInternals
+ * @brief Error reporting internals
+ * @{
+ */
+
+/**
+ * @def DBUS_ERROR_INIT
+ *
+ * Expands to a suitable initializer for a DBusError on the stack.
+ * Declaring a DBusError with:
+ *
+ * @code
+ * DBusError error = DBUS_ERROR_INIT;
+ *
+ * do_things_with (&error);
+ * @endcode
+ *
+ * is a more concise form of:
+ *
+ * @code
+ * DBusError error;
+ *
+ * dbus_error_init (&error);
+ * do_things_with (&error);
+ * @endcode
+ */
+
+/**
+ * Internals of DBusError
+ */
+typedef struct
+{
+  char *name; /**< error name */
+  char *message; /**< error message */
+
+  unsigned int const_message : 1; /**< Message is not owned by DBusError */
+
+  unsigned int dummy2 : 1; /**< placeholder */
+  unsigned int dummy3 : 1; /**< placeholder */
+  unsigned int dummy4 : 1; /**< placeholder */
+  unsigned int dummy5 : 1; /**< placeholder */
+
+  void *padding1; /**< placeholder */
+  
+} DBusRealError;
+
+/**
+ * Returns a longer message describing an error name.
+ * If the error name is unknown, returns the name
+ * itself.
+ *
+ * @param error the error to describe
+ * @returns a constant string describing the error.
+ */
+static const char*
+message_from_error (const char *error)
+{
+  if (strcmp (error, DBUS_ERROR_FAILED) == 0)
+    return "Unknown error";
+  else if (strcmp (error, DBUS_ERROR_NO_MEMORY) == 0)
+    return "Not enough memory available";
+  else if (strcmp (error, DBUS_ERROR_IO_ERROR) == 0)
+    return "Error reading or writing data";
+  else if (strcmp (error, DBUS_ERROR_BAD_ADDRESS) == 0)
+    return "Could not parse address";
+  else if (strcmp (error, DBUS_ERROR_NOT_SUPPORTED) == 0)
+    return "Feature not supported";
+  else if (strcmp (error, DBUS_ERROR_LIMITS_EXCEEDED) == 0)
+    return "Resource limits exceeded";
+  else if (strcmp (error, DBUS_ERROR_ACCESS_DENIED) == 0)
+    return "Permission denied";
+  else if (strcmp (error, DBUS_ERROR_AUTH_FAILED) == 0)
+    return "Could not authenticate to server";
+  else if (strcmp (error, DBUS_ERROR_NO_SERVER) == 0)
+    return "No server available at address";
+  else if (strcmp (error, DBUS_ERROR_TIMEOUT) == 0)
+    return "Connection timed out";
+  else if (strcmp (error, DBUS_ERROR_NO_NETWORK) == 0)
+    return "Network unavailable";
+  else if (strcmp (error, DBUS_ERROR_ADDRESS_IN_USE) == 0)
+    return "Address already in use";
+  else if (strcmp (error, DBUS_ERROR_DISCONNECTED) == 0)
+    return "Disconnected.";
+  else if (strcmp (error, DBUS_ERROR_INVALID_ARGS) == 0)
+    return "Invalid arguments.";
+  else if (strcmp (error, DBUS_ERROR_NO_REPLY) == 0)
+    return "Did not get a reply message.";
+  else if (strcmp (error, DBUS_ERROR_FILE_NOT_FOUND) == 0)
+    return "File doesn't exist.";
+  else if (strcmp (error, DBUS_ERROR_OBJECT_PATH_IN_USE) == 0)
+    return "Object path already in use";
+  else
+    return error;
+}
+
+/** @} */ /* End of internals */
+
+/**
+ * @defgroup DBusErrors Error reporting
+ * @ingroup  DBus
+ * @brief Error reporting
+ *
+ * Types and functions related to reporting errors.
+ *
+ *
+ * In essence D-Bus error reporting works as follows:
+ *
+ * @code
+ * DBusError error;
+ * dbus_error_init (&error);
+ * dbus_some_function (arg1, arg2, &error);
+ * if (dbus_error_is_set (&error))
+ *   {
+ *     fprintf (stderr, "an error occurred: %s\n", error.message);
+ *     dbus_error_free (&error);
+ *   }
+ * @endcode
+ *
+ * By convention, all functions allow #NULL instead of a DBusError*,
+ * so callers who don't care about the error can ignore it.
+ * 
+ * There are some rules. An error passed to a D-Bus function must
+ * always be unset; you can't pass in an error that's already set.  If
+ * a function has a return code indicating whether an error occurred,
+ * and also a #DBusError parameter, then the error will always be set
+ * if and only if the return code indicates an error occurred. i.e.
+ * the return code and the error are never going to disagree.
+ *
+ * An error only needs to be freed if it's been set, not if
+ * it's merely been initialized.
+ *
+ * You can check the specific error that occurred using
+ * dbus_error_has_name().
+ * 
+ * Errors will not be set for programming errors, such as passing
+ * invalid arguments to the libdbus API. Instead, libdbus will print
+ * warnings, exit on a failed assertion, or even crash in those cases
+ * (in other words, incorrect use of the API results in undefined
+ * behavior, possibly accompanied by helpful debugging output if
+ * you're lucky).
+ * 
+ * @{
+ */
+
+/**
+ * Initializes a DBusError structure. Does not allocate any memory;
+ * the error only needs to be freed if it is set at some point.
+ *
+ * @param error the DBusError.
+ */
+void
+dbus_error_init (DBusError *error)
+{
+  DBusRealError *real;
+
+  _dbus_return_if_fail (error != NULL);
+
+  _dbus_assert (sizeof (DBusError) == sizeof (DBusRealError));
+
+  real = (DBusRealError *)error;
+  
+  real->name = NULL;  
+  real->message = NULL;
+
+  real->const_message = TRUE;
+}
+
+/**
+ * Frees an error that's been set (or just initialized),
+ * then reinitializes the error as in dbus_error_init().
+ *
+ * @param error memory where the error is stored.
+ */
+void
+dbus_error_free (DBusError *error)
+{
+  DBusRealError *real;
+
+  _dbus_return_if_fail (error != NULL);
+  
+  real = (DBusRealError *)error;
+
+  if (!real->const_message)
+    {
+      dbus_free (real->name);
+      dbus_free (real->message);
+    }
+
+  dbus_error_init (error);
+}
+
+/**
+ * Assigns an error name and message to a DBusError.  Does nothing if
+ * error is #NULL. The message may be #NULL, which means a default
+ * message will be deduced from the name. The default message will be
+ * totally useless, though, so using a #NULL message is not recommended.
+ *
+ * Because this function does not copy the error name or message, you
+ * must ensure the name and message are global data that won't be
+ * freed. You probably want dbus_set_error() instead, in most cases.
+ * 
+ * @param error the error.or #NULL
+ * @param name the error name (not copied!!!)
+ * @param message the error message (not copied!!!)
+ */
+void
+dbus_set_error_const (DBusError  *error,
+                     const char *name,
+                     const char *message)
+{
+  DBusRealError *real;
+
+  _dbus_return_if_error_is_set (error);
+  _dbus_return_if_fail (name != NULL);
+  
+  if (error == NULL)
+    return;
+
+  _dbus_assert (error->name == NULL);
+  _dbus_assert (error->message == NULL);
+
+  if (message == NULL)
+    message = message_from_error (name);
+  
+  real = (DBusRealError *)error;
+  
+  real->name = (char*) name;
+  real->message = (char *)message;
+  real->const_message = TRUE;
+}
+
+/**
+ * Moves an error src into dest, freeing src and
+ * overwriting dest. Both src and dest must be initialized.
+ * src is reinitialized to an empty error. dest may not
+ * contain an existing error. If the destination is
+ * #NULL, just frees and reinits the source error.
+ * 
+ * @param src the source error
+ * @param dest the destination error or #NULL
+ */
+void
+dbus_move_error (DBusError *src,
+                 DBusError *dest)
+{
+  _dbus_return_if_error_is_set (dest);
+
+  if (dest)
+    {
+      dbus_error_free (dest);
+      *dest = *src;
+      dbus_error_init (src);
+    }
+  else
+    dbus_error_free (src);
+}
+
+/**
+ * Checks whether the error is set and has the given
+ * name.
+ * @param error the error
+ * @param name the name
+ * @returns #TRUE if the given named error occurred
+ */
+dbus_bool_t
+dbus_error_has_name (const DBusError *error,
+                     const char      *name)
+{
+  _dbus_return_val_if_fail (error != NULL, FALSE);
+  _dbus_return_val_if_fail (name != NULL, FALSE);
+
+  _dbus_assert ((error->name != NULL && error->message != NULL) ||
+                (error->name == NULL && error->message == NULL));
+  
+  if (error->name != NULL)
+    {
+      DBusString str1, str2;
+      _dbus_string_init_const (&str1, error->name);
+      _dbus_string_init_const (&str2, name);
+      return _dbus_string_equal (&str1, &str2);
+    }
+  else
+    return FALSE;
+}
+
+/**
+ * Checks whether an error occurred (the error is set).
+ *
+ * @param error the error object
+ * @returns #TRUE if an error occurred
+ */
+dbus_bool_t
+dbus_error_is_set (const DBusError *error)
+{
+  _dbus_return_val_if_fail (error != NULL, FALSE);  
+  _dbus_assert ((error->name != NULL && error->message != NULL) ||
+                (error->name == NULL && error->message == NULL));
+  return error->name != NULL;
+}
+
+/**
+ * Assigns an error name and message to a DBusError.
+ * Does nothing if error is #NULL.
+ *
+ * The format may be #NULL, which means a (pretty much useless)
+ * default message will be deduced from the name. This is not a good
+ * idea, just go ahead and provide a useful error message. It won't
+ * hurt you.
+ *
+ * If no memory can be allocated for the error message, 
+ * an out-of-memory error message will be set instead.
+ *
+ * @param error the error.or #NULL
+ * @param name the error name
+ * @param format printf-style format string.
+ */
+void
+dbus_set_error (DBusError  *error,
+               const char *name,
+               const char *format,
+               ...)
+{
+  DBusRealError *real;
+  DBusString str;
+  va_list args;
+  
+  if (error == NULL)
+    return;
+
+  /* it's a bug to pile up errors */
+  _dbus_return_if_error_is_set (error);
+  _dbus_return_if_fail (name != NULL);
+  
+  _dbus_assert (error->name == NULL);
+  _dbus_assert (error->message == NULL);
+
+  if (!_dbus_string_init (&str))
+    goto nomem;
+  
+  if (format == NULL)
+    {
+      if (!_dbus_string_append (&str,
+                                message_from_error (name)))
+        {
+          _dbus_string_free (&str);
+          goto nomem;
+        }
+    }
+  else
+    {
+      va_start (args, format);
+      if (!_dbus_string_append_printf_valist (&str, format, args))
+        {
+          _dbus_string_free (&str);
+          va_end (args);
+          goto nomem;
+        }
+      va_end (args);
+    }
+
+  real = (DBusRealError *)error;
+
+  if (!_dbus_string_steal_data (&str, &real->message))
+    {
+      _dbus_string_free (&str);
+      goto nomem;
+    }
+  _dbus_string_free (&str);
+  
+  real->name = _dbus_strdup (name);
+  if (real->name == NULL)
+    {
+      dbus_free (real->message);
+      real->message = NULL;
+      goto nomem;
+    }
+  real->const_message = FALSE;
+
+  return;
+  
+ nomem:
+  _DBUS_SET_OOM (error);
+}
+
+/** @} */ /* End public API */
diff --git a/src/dbus/dbus-errors.h b/src/dbus/dbus-errors.h
new file mode 100644 (file)
index 0000000..0a480d8
--- /dev/null
@@ -0,0 +1,82 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-errors.h Error reporting
+ *
+ * Copyright (C) 2002  Red Hat Inc.
+ * Copyright (C) 2003  CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_ERROR_H
+#define DBUS_ERROR_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-types.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusErrors
+ * @{
+ */
+
+/** Mostly-opaque type representing an error that occurred */
+typedef struct DBusError DBusError;
+
+/**
+ * Object representing an exception.
+ */
+struct DBusError
+{
+  const char *name;    /**< public error name field */
+  const char *message; /**< public error message field */
+
+  unsigned int dummy1 : 1; /**< placeholder */
+  unsigned int dummy2 : 1; /**< placeholder */
+  unsigned int dummy3 : 1; /**< placeholder */
+  unsigned int dummy4 : 1; /**< placeholder */
+  unsigned int dummy5 : 1; /**< placeholder */
+
+  void *padding1; /**< placeholder */
+};
+
+#define DBUS_ERROR_INIT { NULL, NULL, TRUE, 0, 0, 0, 0, NULL }
+
+void        dbus_error_init      (DBusError       *error);
+void        dbus_error_free      (DBusError       *error);
+void        dbus_set_error       (DBusError       *error,
+                                  const char      *name,
+                                  const char      *message,
+                                  ...);
+void        dbus_set_error_const (DBusError       *error,
+                                  const char      *name,
+                                  const char      *message);
+void        dbus_move_error      (DBusError       *src,
+                                  DBusError       *dest);
+dbus_bool_t dbus_error_has_name  (const DBusError *error,
+                                  const char      *name);
+dbus_bool_t dbus_error_is_set    (const DBusError *error);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_ERROR_H */
diff --git a/src/dbus/dbus-hash.c b/src/dbus/dbus-hash.c
new file mode 100644 (file)
index 0000000..d0778bf
--- /dev/null
@@ -0,0 +1,2193 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-hash.c Generic hash table utility (internal to D-Bus implementation)
+ * 
+ * Copyright (C) 2002  Red Hat, Inc.
+ * Copyright (c) 1991-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ * 
+ * Hash table implementation based on generic/tclHash.c from the Tcl
+ * source code. The original Tcl license applies to portions of the
+ * code from tclHash.c; the Tcl license follows this standad D-Bus 
+ * license information.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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
+ *
+ */
+/* 
+ * The following copyright applies to code from the Tcl distribution.
+ *
+ * Copyright (c) 1991-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ *
+ * This software is copyrighted by the Regents of the University of
+ * California, Sun Microsystems, Inc., Scriptics Corporation, and
+ * other parties.  The following terms apply to all files associated
+ * with the software unless explicitly disclaimed in individual files.
+ * 
+ * The authors hereby grant permission to use, copy, modify,
+ * distribute, and license this software and its documentation for any
+ * purpose, provided that existing copyright notices are retained in
+ * all copies and that this notice is included verbatim in any
+ * distributions. No written agreement, license, or royalty fee is
+ * required for any of the authorized uses.  Modifications to this
+ * software may be copyrighted by their authors and need not follow
+ * the licensing terms described here, provided that the new terms are
+ * clearly indicated on the first page of each file where they apply.
+ * 
+ * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+ * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
+ * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
+ * NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
+ * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
+ * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ * 
+ * GOVERNMENT USE: If you are acquiring this software on behalf of the
+ * U.S. government, the Government shall have only "Restricted Rights"
+ * in the software and related documentation as defined in the Federal
+ * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
+ * are acquiring the software on behalf of the Department of Defense,
+ * the software shall be classified as "Commercial Computer Software"
+ * and the Government shall have only "Restricted Rights" as defined
+ * in Clause 252.227-7013 (c) (1) of DFARs.  Notwithstanding the
+ * foregoing, the authors grant the U.S. Government and others acting
+ * in its behalf permission to use and distribute the software in
+ * accordance with the terms specified in this license.
+ */
+
+#include "dbus-hash.h"
+#include "dbus-internals.h"
+#include "dbus-mempool.h"
+
+/**
+ * @defgroup DBusHashTable Hash table
+ * @ingroup  DBusInternals
+ * @brief DBusHashTable data structure
+ *
+ * Types and functions related to DBusHashTable.
+ */
+
+/**
+ * @defgroup DBusHashTableInternals Hash table implementation details
+ * @ingroup  DBusInternals
+ * @brief DBusHashTable implementation details
+ *
+ * The guts of DBusHashTable.
+ *
+ * @{
+ */
+
+/**
+ * When there are this many entries per bucket, on average, rebuild
+ * the hash table to make it larger.
+ */
+#define REBUILD_MULTIPLIER  3
+
+/**
+ * Takes a preliminary integer hash value and produces an index into a
+ * hash tables bucket list.  The idea is to make it so that
+ * preliminary values that are arbitrarily similar will end up in
+ * different buckets.  The hash function was taken from a
+ * random-number generator. (This is used to hash integers.)
+ *
+ * The down_shift drops off the high bits of the hash index, and
+ * decreases as we increase the number of hash buckets (to keep more
+ * range in the hash index). The mask also strips high bits and strips
+ * fewer high bits as the number of hash buckets increases.
+ * I don't understand two things: why is the initial downshift 28
+ * to keep 4 bits when the initial mask is 011 to keep 2 bits,
+ * and why do we have both a mask and a downshift?
+ * 
+ */
+#define RANDOM_INDEX(table, i) \
+    (((((long) (i))*1103515245) >> (table)->down_shift) & (table)->mask)
+
+/**
+ * Initial number of buckets in hash table (hash table statically
+ * allocates its buckets for this size and below).
+ * The initial mask has to be synced to this.
+ */
+#define DBUS_SMALL_HASH_TABLE 4
+
+/**
+ * Typedef for DBusHashEntry
+ */
+typedef struct DBusHashEntry DBusHashEntry;
+
+/**
+ * @brief Internal representation of a hash entry.
+ * 
+ * A single entry (key-value pair) in the hash table.
+ * Internal to hash table implementation.
+ */
+struct DBusHashEntry
+{
+  DBusHashEntry *next;    /**< Pointer to next entry in this
+                           * hash bucket, or #NULL for end of
+                           * chain.
+                           */
+  void *key;              /**< Hash key */
+  void *value;            /**< Hash value */
+};
+
+/**
+ * Function used to find and optionally create a hash entry.
+ */
+typedef DBusHashEntry* (* DBusFindEntryFunction) (DBusHashTable        *table,
+                                                  void                 *key,
+                                                  dbus_bool_t           create_if_not_found,
+                                                  DBusHashEntry      ***bucket,
+                                                  DBusPreallocatedHash *preallocated);
+
+/**
+ * @brief Internals of DBusHashTable.
+ * 
+ * Hash table internals. Hash tables are opaque objects, they must be
+ * used via accessor functions.
+ */
+struct DBusHashTable {
+  int refcount;                       /**< Reference count */
+  
+  DBusHashEntry **buckets;            /**< Pointer to bucket array.  Each
+                                       * element points to first entry in
+                                       * bucket's hash chain, or #NULL.
+                                       */
+  DBusHashEntry *static_buckets[DBUS_SMALL_HASH_TABLE];
+                                       /**< Bucket array used for small tables
+                                        * (to avoid mallocs and frees).
+                                        */
+  int n_buckets;                       /**< Total number of buckets allocated
+                                        * at **buckets.
+                                        */
+  int n_entries;                       /**< Total number of entries present
+                                        * in table.
+                                        */
+  int hi_rebuild_size;                 /**< Enlarge table when n_entries gets
+                                        * to be this large.
+                                        */
+  int lo_rebuild_size;                 /**< Shrink table when n_entries gets
+                                        * below this.
+                                        */
+  int down_shift;                      /**< Shift count used in hashing
+                                        * function.  Designed to use high-
+                                        * order bits of randomized keys.
+                                        */
+  int mask;                            /**< Mask value used in hashing
+                                        * function.
+                                        */
+  DBusHashType key_type;               /**< Type of keys used in this table */
+
+
+  DBusFindEntryFunction find_function; /**< Function for finding entries */
+
+  DBusFreeFunction free_key_function;   /**< Function to free keys */
+  DBusFreeFunction free_value_function; /**< Function to free values */
+
+  DBusMemPool *entry_pool;              /**< Memory pool for hash entries */
+};
+
+/** 
+ * @brief Internals of DBusHashIter.
+ */
+typedef struct
+{
+  DBusHashTable *table;     /**< Pointer to table containing entry. */
+  DBusHashEntry **bucket;   /**< Pointer to bucket that points to
+                             * first entry in this entry's chain:
+                             * used for deleting the entry.
+                             */
+  DBusHashEntry *entry;      /**< Current hash entry */
+  DBusHashEntry *next_entry; /**< Next entry to be iterated onto in current bucket */
+  int next_bucket;           /**< index of next bucket */
+  int n_entries_on_init;     /**< used to detect table resize since initialization */
+} DBusRealHashIter;
+
+static DBusHashEntry* find_direct_function      (DBusHashTable          *table,
+                                                 void                   *key,
+                                                 dbus_bool_t             create_if_not_found,
+                                                 DBusHashEntry        ***bucket,
+                                                 DBusPreallocatedHash   *preallocated);
+static DBusHashEntry* find_string_function      (DBusHashTable          *table,
+                                                 void                   *key,
+                                                 dbus_bool_t             create_if_not_found,
+                                                 DBusHashEntry        ***bucket,
+                                                 DBusPreallocatedHash   *preallocated);
+#ifdef DBUS_BUILD_TESTS
+static DBusHashEntry* find_two_strings_function (DBusHashTable          *table,
+                                                 void                   *key,
+                                                 dbus_bool_t             create_if_not_found,
+                                                 DBusHashEntry        ***bucket,
+                                                 DBusPreallocatedHash   *preallocated);
+#endif
+static unsigned int   string_hash               (const char             *str);
+#ifdef DBUS_BUILD_TESTS
+static unsigned int   two_strings_hash          (const char             *str);
+#endif
+static void           rebuild_table             (DBusHashTable          *table);
+static DBusHashEntry* alloc_entry               (DBusHashTable          *table);
+static void           remove_entry              (DBusHashTable          *table,
+                                                 DBusHashEntry         **bucket,
+                                                 DBusHashEntry          *entry);
+static void           free_entry                (DBusHashTable          *table,
+                                                 DBusHashEntry          *entry);
+static void           free_entry_data           (DBusHashTable          *table,
+                                                 DBusHashEntry          *entry);
+
+
+/** @} */
+
+/**
+ * @addtogroup DBusHashTable
+ * @{
+ */
+
+/**
+ * @typedef DBusHashIter
+ *
+ * Public opaque hash table iterator object.
+ */
+
+/**
+ * @typedef DBusHashTable
+ *
+ * Public opaque hash table object.
+ */
+
+/**
+ * @typedef DBusHashType
+ *
+ * Indicates the type of a key in the hash table.
+ */
+
+/**
+ * Constructs a new hash table. Should be freed with
+ * _dbus_hash_table_unref(). If memory cannot be
+ * allocated for the hash table, returns #NULL.
+ *
+ * @param type the type of hash key to use.
+ * @param key_free_function function to free hash keys.
+ * @param value_free_function function to free hash values.
+ * @returns a new DBusHashTable or #NULL if no memory.
+ */
+DBusHashTable*
+_dbus_hash_table_new (DBusHashType     type,
+                      DBusFreeFunction key_free_function,
+                      DBusFreeFunction value_free_function)
+{
+  DBusHashTable *table;
+  DBusMemPool *entry_pool;
+  
+  table = dbus_new0 (DBusHashTable, 1);
+  if (table == NULL)
+    return NULL;
+
+  entry_pool = _dbus_mem_pool_new (sizeof (DBusHashEntry), TRUE);
+  if (entry_pool == NULL)
+    {
+      dbus_free (table);
+      return NULL;
+    }
+  
+  table->refcount = 1;
+  table->entry_pool = entry_pool;
+  
+  _dbus_assert (DBUS_SMALL_HASH_TABLE == _DBUS_N_ELEMENTS (table->static_buckets));
+  
+  table->buckets = table->static_buckets;  
+  table->n_buckets = DBUS_SMALL_HASH_TABLE;
+  table->n_entries = 0;
+  table->hi_rebuild_size = DBUS_SMALL_HASH_TABLE * REBUILD_MULTIPLIER;
+  table->lo_rebuild_size = 0;
+  table->down_shift = 28;
+  table->mask = 3;
+  table->key_type = type;
+
+  _dbus_assert (table->mask < table->n_buckets);
+  
+  switch (table->key_type)
+    {
+    case DBUS_HASH_INT:
+    case DBUS_HASH_POINTER:
+    case DBUS_HASH_ULONG:
+      table->find_function = find_direct_function;
+      break;
+    case DBUS_HASH_STRING:
+      table->find_function = find_string_function;
+      break;
+    case DBUS_HASH_TWO_STRINGS:
+#ifdef DBUS_BUILD_TESTS
+      table->find_function = find_two_strings_function;
+#endif
+      break;
+    default:
+      _dbus_assert_not_reached ("Unknown hash table type");
+      break;
+    }
+
+  table->free_key_function = key_free_function;
+  table->free_value_function = value_free_function;
+
+  return table;
+}
+
+
+/**
+ * Increments the reference count for a hash table.
+ *
+ * @param table the hash table to add a reference to.
+ * @returns the hash table.
+ */
+DBusHashTable *
+_dbus_hash_table_ref (DBusHashTable *table)
+{
+  table->refcount += 1;
+  
+  return table;
+}
+
+/**
+ * Decrements the reference count for a hash table,
+ * freeing the hash table if the count reaches zero.
+ *
+ * @param table the hash table to remove a reference from.
+ */
+void
+_dbus_hash_table_unref (DBusHashTable *table)
+{
+  table->refcount -= 1;
+
+  if (table->refcount == 0)
+    {
+#if 0
+      DBusHashEntry *entry;
+      DBusHashEntry *next;
+      int i;
+
+      /* Free the entries in the table. */
+      for (i = 0; i < table->n_buckets; i++)
+        {
+          entry = table->buckets[i];
+          while (entry != NULL)
+            {
+              next = entry->next;
+
+              free_entry (table, entry);
+              
+              entry = next;
+            }
+        }
+#else
+      DBusHashEntry *entry;
+      int i;
+
+      /* Free the entries in the table. */
+      for (i = 0; i < table->n_buckets; i++)
+        {
+          entry = table->buckets[i];
+          while (entry != NULL)
+            {
+              free_entry_data (table, entry);
+              
+              entry = entry->next;
+            }
+        }
+      /* We can do this very quickly with memory pools ;-) */
+      _dbus_mem_pool_free (table->entry_pool);
+#endif
+      
+      /* Free the bucket array, if it was dynamically allocated. */
+      if (table->buckets != table->static_buckets)
+        dbus_free (table->buckets);
+
+      dbus_free (table);
+    }
+}
+
+/**
+ * Removed all entries from a hash table.
+ *
+ * @param table the hash table to remove all entries from.
+ */
+void
+_dbus_hash_table_remove_all (DBusHashTable *table)
+{
+  DBusHashIter iter;
+  _dbus_hash_iter_init (table, &iter);
+  while (_dbus_hash_iter_next (&iter))
+    {
+      _dbus_hash_iter_remove_entry(&iter);
+    }
+}
+
+static DBusHashEntry*
+alloc_entry (DBusHashTable *table)
+{
+  DBusHashEntry *entry;
+
+  entry = _dbus_mem_pool_alloc (table->entry_pool);
+  
+  return entry;
+}
+
+static void
+free_entry_data (DBusHashTable  *table,
+                DBusHashEntry  *entry)
+{
+  if (table->free_key_function)
+    (* table->free_key_function) (entry->key);
+  if (table->free_value_function)
+    (* table->free_value_function) (entry->value);
+}
+
+static void
+free_entry (DBusHashTable  *table,
+            DBusHashEntry  *entry)
+{
+  free_entry_data (table, entry);
+  _dbus_mem_pool_dealloc (table->entry_pool, entry);
+}
+
+static void
+remove_entry (DBusHashTable  *table,
+              DBusHashEntry **bucket,
+              DBusHashEntry  *entry)
+{
+  _dbus_assert (table != NULL);
+  _dbus_assert (bucket != NULL);
+  _dbus_assert (*bucket != NULL);  
+  _dbus_assert (entry != NULL);
+  
+  if (*bucket == entry)
+    *bucket = entry->next;
+  else
+    {
+      DBusHashEntry *prev;
+      prev = *bucket;
+
+      while (prev->next != entry)
+        prev = prev->next;      
+      
+      _dbus_assert (prev != NULL);
+
+      prev->next = entry->next;
+    }
+  
+  table->n_entries -= 1;
+  free_entry (table, entry);
+}
+
+/**
+ * Initializes a hash table iterator. To iterate over all entries in a
+ * hash table, use the following code (the printf assumes a hash
+ * from strings to strings obviously):
+ *
+ * @code
+ * DBusHashIter iter;
+ *
+ * _dbus_hash_iter_init (table, &iter);
+ * while (_dbus_hash_iter_next (&iter))
+ *   {
+ *      printf ("The first key is %s and value is %s\n",
+ *              _dbus_hash_iter_get_string_key (&iter),
+ *              _dbus_hash_iter_get_value (&iter));
+ *   }
+ * 
+ * 
+ * @endcode
+ *
+ * The iterator is initialized pointing "one before" the first hash
+ * entry. The first call to _dbus_hash_iter_next() moves it onto
+ * the first valid entry or returns #FALSE if the hash table is
+ * empty. Subsequent calls move to the next valid entry or return
+ * #FALSE if there are no more entries.
+ *
+ * Note that it is guaranteed to be safe to remove a hash entry during
+ * iteration, but it is not safe to add a hash entry.
+ * 
+ * @param table the hash table to iterate over.
+ * @param iter the iterator to initialize.
+ */
+void
+_dbus_hash_iter_init (DBusHashTable *table,
+                      DBusHashIter  *iter)
+{
+  DBusRealHashIter *real;
+  
+  _dbus_assert (sizeof (DBusHashIter) == sizeof (DBusRealHashIter));
+  
+  real = (DBusRealHashIter*) iter;
+
+  real->table = table;
+  real->bucket = NULL;
+  real->entry = NULL;
+  real->next_entry = NULL;
+  real->next_bucket = 0;
+  real->n_entries_on_init = table->n_entries;
+}
+
+/**
+ * Move the hash iterator forward one step, to the next hash entry.
+ * The documentation for _dbus_hash_iter_init() explains in more
+ * detail.
+ *
+ * @param iter the iterator to move forward.
+ * @returns #FALSE if there are no more entries to move to.
+ */
+dbus_bool_t
+_dbus_hash_iter_next (DBusHashIter  *iter)
+{
+  DBusRealHashIter *real;
+  
+  _dbus_assert (sizeof (DBusHashIter) == sizeof (DBusRealHashIter));
+  
+  real = (DBusRealHashIter*) iter;
+
+  /* if this assertion failed someone probably added hash entries
+   * during iteration, which is bad.
+   */
+  _dbus_assert (real->n_entries_on_init >= real->table->n_entries);
+  
+  /* Remember that real->entry may have been deleted */
+  
+  while (real->next_entry == NULL)
+    {
+      if (real->next_bucket >= real->table->n_buckets)
+        {
+          /* invalidate iter and return false */
+          real->entry = NULL;
+          real->table = NULL;
+          real->bucket = NULL;
+          return FALSE;
+        }
+
+      real->bucket = &(real->table->buckets[real->next_bucket]);
+      real->next_entry = *(real->bucket);
+      real->next_bucket += 1;
+    }
+
+  _dbus_assert (real->next_entry != NULL);
+  _dbus_assert (real->bucket != NULL);
+  
+  real->entry = real->next_entry;
+  real->next_entry = real->entry->next;
+  
+  return TRUE;
+}
+
+/**
+ * Removes the current entry from the hash table.
+ * If a key_free_function or value_free_function
+ * was provided to _dbus_hash_table_new(),
+ * frees the key and/or value for this entry.
+ *
+ * @param iter the hash table iterator.
+ */
+void
+_dbus_hash_iter_remove_entry (DBusHashIter *iter)
+{
+  DBusRealHashIter *real;
+
+  real = (DBusRealHashIter*) iter;
+
+  _dbus_assert (real->table != NULL);
+  _dbus_assert (real->entry != NULL);
+  _dbus_assert (real->bucket != NULL);
+  
+  remove_entry (real->table, real->bucket, real->entry);
+
+  real->entry = NULL; /* make it crash if you try to use this entry */
+}
+
+/**
+ * Gets the value of the current entry.
+ *
+ * @param iter the hash table iterator.
+ */
+void*
+_dbus_hash_iter_get_value (DBusHashIter *iter)
+{
+  DBusRealHashIter *real;
+
+  real = (DBusRealHashIter*) iter;
+
+  _dbus_assert (real->table != NULL);
+  _dbus_assert (real->entry != NULL);
+
+  return real->entry->value;
+}
+
+/**
+ * Sets the value of the current entry.
+ * If the hash table has a value_free_function
+ * it will be used to free the previous value.
+ * The hash table will own the passed-in value
+ * (it will not be copied).
+ *
+ * @param iter the hash table iterator.
+ * @param value the new value.
+ */
+void
+_dbus_hash_iter_set_value (DBusHashIter *iter,
+                           void         *value)
+{
+  DBusRealHashIter *real;
+
+  real = (DBusRealHashIter*) iter;
+
+  _dbus_assert (real->table != NULL);
+  _dbus_assert (real->entry != NULL);
+
+  if (real->table->free_value_function && value != real->entry->value)    
+    (* real->table->free_value_function) (real->entry->value);
+  
+  real->entry->value = value;
+}
+
+/**
+ * Gets the key for the current entry.
+ * Only works for hash tables of type #DBUS_HASH_INT.
+ *
+ * @param iter the hash table iterator.
+ */
+int
+_dbus_hash_iter_get_int_key (DBusHashIter *iter)
+{
+  DBusRealHashIter *real;
+
+  real = (DBusRealHashIter*) iter;
+
+  _dbus_assert (real->table != NULL);
+  _dbus_assert (real->entry != NULL);
+
+  return _DBUS_POINTER_TO_INT (real->entry->key);
+}
+
+/**
+ * Gets the key for the current entry.
+ * Only works for hash tables of type #DBUS_HASH_ULONG.
+ *
+ * @param iter the hash table iterator.
+ */
+unsigned long
+_dbus_hash_iter_get_ulong_key (DBusHashIter *iter)
+{
+  DBusRealHashIter *real;
+
+  real = (DBusRealHashIter*) iter;
+
+  _dbus_assert (real->table != NULL);
+  _dbus_assert (real->entry != NULL);
+
+  return (unsigned long) real->entry->key;
+}
+
+/**
+ * Gets the key for the current entry.
+ * Only works for hash tables of type #DBUS_HASH_STRING
+ * @param iter the hash table iterator.
+ */
+const char*
+_dbus_hash_iter_get_string_key (DBusHashIter *iter)
+{
+  DBusRealHashIter *real;
+
+  real = (DBusRealHashIter*) iter;
+
+  _dbus_assert (real->table != NULL);
+  _dbus_assert (real->entry != NULL);
+
+  return real->entry->key;
+}
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Gets the key for the current entry.
+ * Only works for hash tables of type #DBUS_HASH_TWO_STRINGS
+ * @param iter the hash table iterator.
+ */
+const char*
+_dbus_hash_iter_get_two_strings_key (DBusHashIter *iter)
+{
+  DBusRealHashIter *real;
+
+  real = (DBusRealHashIter*) iter;
+
+  _dbus_assert (real->table != NULL);
+  _dbus_assert (real->entry != NULL);
+
+  return real->entry->key;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * A low-level but efficient interface for manipulating the hash
+ * table.  It's efficient because you can get, set, and optionally
+ * create the hash entry while only running the hash function one
+ * time.
+ *
+ * Note that while calling _dbus_hash_iter_next() on the iterator
+ * filled in by this function may work, it's completely
+ * undefined which entries are after this iter and which
+ * are before it. So it would be silly to iterate using this
+ * iterator.
+ *
+ * If the hash entry is created, its value will be initialized
+ * to all bits zero.
+ *
+ * #FALSE may be returned due to memory allocation failure, or
+ * because create_if_not_found was #FALSE and the entry
+ * did not exist.
+ *
+ * If create_if_not_found is #TRUE and the entry is created, the hash
+ * table takes ownership of the key that's passed in.
+ *
+ * For a hash table of type #DBUS_HASH_INT, cast the int
+ * key to the key parameter using #_DBUS_INT_TO_POINTER().
+ * 
+ * @param table the hash table.
+ * @param key the hash key.
+ * @param create_if_not_found if #TRUE, create the entry if it didn't exist.
+ * @param iter the iterator to initialize.
+ * @returns #TRUE if the hash entry now exists (and the iterator is thus valid).
+ */
+dbus_bool_t
+_dbus_hash_iter_lookup (DBusHashTable *table,
+                        void          *key,
+                        dbus_bool_t    create_if_not_found,
+                        DBusHashIter  *iter)
+{
+  DBusRealHashIter *real;
+  DBusHashEntry *entry;
+  DBusHashEntry **bucket;
+  
+  _dbus_assert (sizeof (DBusHashIter) == sizeof (DBusRealHashIter));
+  
+  real = (DBusRealHashIter*) iter;
+
+  entry = (* table->find_function) (table, key, create_if_not_found, &bucket, NULL);
+
+  if (entry == NULL)
+    return FALSE;
+  
+  real->table = table;
+  real->bucket = bucket;
+  real->entry = entry;
+  real->next_entry = entry->next;
+  real->next_bucket = (bucket - table->buckets) + 1;
+  real->n_entries_on_init = table->n_entries; 
+
+  _dbus_assert (&(table->buckets[real->next_bucket-1]) == real->bucket);
+  
+  return TRUE;
+}
+
+static void
+add_allocated_entry (DBusHashTable   *table,
+                     DBusHashEntry   *entry,
+                     unsigned int     idx,
+                     void            *key,
+                     DBusHashEntry ***bucket)
+{
+  DBusHashEntry **b;  
+  
+  entry->key = key;
+  
+  b = &(table->buckets[idx]);
+  entry->next = *b;
+  *b = entry;
+
+  if (bucket)
+    *bucket = b;
+  
+  table->n_entries += 1;
+
+  /* note we ONLY rebuild when ADDING - because you can iterate over a
+   * table and remove entries safely.
+   */
+  if (table->n_entries >= table->hi_rebuild_size ||
+      table->n_entries < table->lo_rebuild_size)
+    rebuild_table (table);
+}
+
+static DBusHashEntry*
+add_entry (DBusHashTable        *table, 
+           unsigned int          idx,
+           void                 *key,
+           DBusHashEntry      ***bucket,
+           DBusPreallocatedHash *preallocated)
+{
+  DBusHashEntry  *entry;
+
+  if (preallocated == NULL)
+    {
+      entry = alloc_entry (table);
+      if (entry == NULL)
+        {
+          if (bucket)
+            *bucket = NULL;
+          return NULL;
+        }
+    }
+  else
+    {
+      entry = (DBusHashEntry*) preallocated;
+    }
+
+  add_allocated_entry (table, entry, idx, key, bucket);
+
+  return entry;
+}
+
+/* This is g_str_hash from GLib which was
+ * extensively discussed/tested/profiled
+ */
+static unsigned int
+string_hash (const char *str)
+{
+  const char *p = str;
+  unsigned int h = *p;
+
+  if (h)
+    for (p += 1; *p != '\0'; p++)
+      h = (h << 5) - h + *p;
+
+  return h;
+}
+
+#ifdef DBUS_BUILD_TESTS
+/* This hashes a memory block with two nul-terminated strings
+ * in it, used in dbus-object-registry.c at the moment.
+ */
+static unsigned int
+two_strings_hash (const char *str)
+{
+  const char *p = str;
+  unsigned int h = *p;
+
+  if (h)
+    for (p += 1; *p != '\0'; p++)
+      h = (h << 5) - h + *p;
+
+  for (p += 1; *p != '\0'; p++)
+    h = (h << 5) - h + *p;
+  
+  return h;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/** Key comparison function */
+typedef int (* KeyCompareFunc) (const void *key_a, const void *key_b);
+
+static DBusHashEntry*
+find_generic_function (DBusHashTable        *table,
+                       void                 *key,
+                       unsigned int          idx,
+                       KeyCompareFunc        compare_func,
+                       dbus_bool_t           create_if_not_found,
+                       DBusHashEntry      ***bucket,
+                       DBusPreallocatedHash *preallocated)
+{
+  DBusHashEntry *entry;
+
+  if (bucket)
+    *bucket = NULL;
+
+  /* Search all of the entries in this bucket. */
+  entry = table->buckets[idx];
+  while (entry != NULL)
+    {
+      if ((compare_func == NULL && key == entry->key) ||
+          (compare_func != NULL && (* compare_func) (key, entry->key) == 0))
+        {
+          if (bucket)
+            *bucket = &(table->buckets[idx]);
+
+          if (preallocated)
+            _dbus_hash_table_free_preallocated_entry (table, preallocated);
+          
+          return entry;
+        }
+      
+      entry = entry->next;
+    }
+
+  if (create_if_not_found)
+    entry = add_entry (table, idx, key, bucket, preallocated);
+  else if (preallocated)
+    _dbus_hash_table_free_preallocated_entry (table, preallocated);
+  
+  return entry;
+}
+
+static DBusHashEntry*
+find_string_function (DBusHashTable        *table,
+                      void                 *key,
+                      dbus_bool_t           create_if_not_found,
+                      DBusHashEntry      ***bucket,
+                      DBusPreallocatedHash *preallocated)
+{
+  unsigned int idx;
+  
+  idx = string_hash (key) & table->mask;
+
+  return find_generic_function (table, key, idx,
+                                (KeyCompareFunc) strcmp, create_if_not_found, bucket,
+                                preallocated);
+}
+
+#ifdef DBUS_BUILD_TESTS
+static int
+two_strings_cmp (const char *a,
+                 const char *b)
+{
+  size_t len_a;
+  size_t len_b;
+  int res;
+  
+  res = strcmp (a, b);
+  if (res != 0)
+    return res;
+
+  len_a = strlen (a);
+  len_b = strlen (b);
+
+  return strcmp (a + len_a + 1, b + len_b + 1);
+}
+#endif
+
+#ifdef DBUS_BUILD_TESTS
+static DBusHashEntry*
+find_two_strings_function (DBusHashTable        *table,
+                           void                 *key,
+                           dbus_bool_t           create_if_not_found,
+                           DBusHashEntry      ***bucket,
+                           DBusPreallocatedHash *preallocated)
+{
+  unsigned int idx;
+  
+  idx = two_strings_hash (key) & table->mask;
+
+  return find_generic_function (table, key, idx,
+                                (KeyCompareFunc) two_strings_cmp, create_if_not_found, bucket,
+                                preallocated);
+}
+#endif /* DBUS_BUILD_TESTS */
+
+static DBusHashEntry*
+find_direct_function (DBusHashTable        *table,
+                      void                 *key,
+                      dbus_bool_t           create_if_not_found,
+                      DBusHashEntry      ***bucket,
+                      DBusPreallocatedHash *preallocated)
+{
+  unsigned int idx;
+  
+  idx = RANDOM_INDEX (table, key) & table->mask;
+
+
+  return find_generic_function (table, key, idx,
+                                NULL, create_if_not_found, bucket,
+                                preallocated);
+}
+
+static void
+rebuild_table (DBusHashTable *table)
+{
+  int old_size;
+  int new_buckets;
+  DBusHashEntry **old_buckets;
+  DBusHashEntry **old_chain;
+  DBusHashEntry *entry;
+  dbus_bool_t growing;
+  
+  /*
+   * Allocate and initialize the new bucket array, and set up
+   * hashing constants for new array size.
+   */
+
+  growing = table->n_entries >= table->hi_rebuild_size;
+  
+  old_size = table->n_buckets;
+  old_buckets = table->buckets;
+
+  if (growing)
+    {
+      /* overflow paranoia */
+      if (table->n_buckets < _DBUS_INT_MAX / 4 &&
+          table->down_shift >= 0)
+        new_buckets = table->n_buckets * 4;
+      else
+        return; /* can't grow anymore */
+    }
+  else
+    {
+      new_buckets = table->n_buckets / 4;
+      if (new_buckets < DBUS_SMALL_HASH_TABLE)
+        return; /* don't bother shrinking this far */
+    }
+
+  table->buckets = dbus_new0 (DBusHashEntry*, new_buckets);
+  if (table->buckets == NULL)
+    {
+      /* out of memory, yay - just don't reallocate, the table will
+       * still work, albeit more slowly.
+       */
+      table->buckets = old_buckets;
+      return;
+    }
+
+  table->n_buckets = new_buckets;
+  
+  if (growing)
+    {
+      table->lo_rebuild_size = table->hi_rebuild_size;
+      table->hi_rebuild_size *= 4;
+      
+      table->down_shift -= 2;               /* keep 2 more high bits */
+      table->mask = (table->mask << 2) + 3; /* keep 2 more high bits */
+    }
+  else
+    {
+      table->hi_rebuild_size = table->lo_rebuild_size;
+      table->lo_rebuild_size /= 4;
+
+      table->down_shift += 2;         /* keep 2 fewer high bits */
+      table->mask = table->mask >> 2; /* keep 2 fewer high bits */
+    }
+
+#if 0
+  printf ("%s table to lo = %d hi = %d downshift = %d mask = 0x%x\n",
+          growing ? "GROW" : "SHRINK",
+          table->lo_rebuild_size,
+          table->hi_rebuild_size,
+          table->down_shift,
+          table->mask);
+#endif
+  
+  _dbus_assert (table->lo_rebuild_size >= 0);
+  _dbus_assert (table->hi_rebuild_size > table->lo_rebuild_size);
+  _dbus_assert (table->mask != 0);
+  /* the mask is essentially the max index */
+  _dbus_assert (table->mask < table->n_buckets);
+  
+  /*
+   * Rehash all of the existing entries into the new bucket array.
+   */
+
+  for (old_chain = old_buckets; old_size > 0; old_size--, old_chain++)
+    {
+      for (entry = *old_chain; entry != NULL; entry = *old_chain)
+        {
+          unsigned int idx;
+          DBusHashEntry **bucket;
+          
+          *old_chain = entry->next;
+          switch (table->key_type)
+            {
+            case DBUS_HASH_STRING:
+              idx = string_hash (entry->key) & table->mask;
+              break;
+            case DBUS_HASH_TWO_STRINGS:
+#ifdef DBUS_BUILD_TESTS
+              idx = two_strings_hash (entry->key) & table->mask;
+#else
+              idx = 0;
+              _dbus_assert_not_reached ("two-strings is not enabled");
+#endif
+              break;
+            case DBUS_HASH_INT:
+            case DBUS_HASH_ULONG:
+            case DBUS_HASH_POINTER:
+              idx = RANDOM_INDEX (table, entry->key);
+              break;
+            default:
+              idx = 0;
+              _dbus_assert_not_reached ("Unknown hash table type");
+              break;
+            }
+          
+          bucket = &(table->buckets[idx]);
+          entry->next = *bucket;
+          *bucket = entry;
+        }
+    }
+  
+  /* Free the old bucket array, if it was dynamically allocated. */
+
+  if (old_buckets != table->static_buckets)
+    dbus_free (old_buckets);
+}
+
+/**
+ * Looks up the value for a given string in a hash table
+ * of type #DBUS_HASH_STRING. Returns %NULL if the value
+ * is not present. (A not-present entry is indistinguishable
+ * from an entry with a value of %NULL.)
+ * @param table the hash table.
+ * @param key the string to look up.
+ * @returns the value of the hash entry.
+ */
+void*
+_dbus_hash_table_lookup_string (DBusHashTable *table,
+                                const char    *key)
+{
+  DBusHashEntry *entry;
+
+  _dbus_assert (table->key_type == DBUS_HASH_STRING);
+  
+  entry = (* table->find_function) (table, (char*) key, FALSE, NULL, NULL);
+
+  if (entry)
+    return entry->value;
+  else
+    return NULL;
+}
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Looks up the value for a given string in a hash table
+ * of type #DBUS_HASH_TWO_STRINGS. Returns %NULL if the value
+ * is not present. (A not-present entry is indistinguishable
+ * from an entry with a value of %NULL.)
+ * @param table the hash table.
+ * @param key the string to look up.
+ * @returns the value of the hash entry.
+ */
+void*
+_dbus_hash_table_lookup_two_strings (DBusHashTable *table,
+                                     const char    *key)
+{
+  DBusHashEntry *entry;
+
+  _dbus_assert (table->key_type == DBUS_HASH_TWO_STRINGS);
+  
+  entry = (* table->find_function) (table, (char*) key, FALSE, NULL, NULL);
+
+  if (entry)
+    return entry->value;
+  else
+    return NULL;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * Looks up the value for a given integer in a hash table
+ * of type #DBUS_HASH_INT. Returns %NULL if the value
+ * is not present. (A not-present entry is indistinguishable
+ * from an entry with a value of %NULL.)
+ * @param table the hash table.
+ * @param key the integer to look up.
+ * @returns the value of the hash entry.
+ */
+void*
+_dbus_hash_table_lookup_int (DBusHashTable *table,
+                             int            key)
+{
+  DBusHashEntry *entry;
+
+  _dbus_assert (table->key_type == DBUS_HASH_INT);
+  
+  entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), FALSE, NULL, NULL);
+
+  if (entry)
+    return entry->value;
+  else
+    return NULL;
+}
+
+#ifdef DBUS_BUILD_TESTS
+/* disabled since it's only used for testing */
+/**
+ * Looks up the value for a given integer in a hash table
+ * of type #DBUS_HASH_POINTER. Returns %NULL if the value
+ * is not present. (A not-present entry is indistinguishable
+ * from an entry with a value of %NULL.)
+ * @param table the hash table.
+ * @param key the integer to look up.
+ * @returns the value of the hash entry.
+ */
+void*
+_dbus_hash_table_lookup_pointer (DBusHashTable *table,
+                                 void          *key)
+{
+  DBusHashEntry *entry;
+
+  _dbus_assert (table->key_type == DBUS_HASH_POINTER);
+  
+  entry = (* table->find_function) (table, key, FALSE, NULL, NULL);
+
+  if (entry)
+    return entry->value;
+  else
+    return NULL;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * Looks up the value for a given integer in a hash table
+ * of type #DBUS_HASH_ULONG. Returns %NULL if the value
+ * is not present. (A not-present entry is indistinguishable
+ * from an entry with a value of %NULL.)
+ * @param table the hash table.
+ * @param key the integer to look up.
+ * @returns the value of the hash entry.
+ */
+void*
+_dbus_hash_table_lookup_ulong (DBusHashTable *table,
+                               unsigned long  key)
+{
+  DBusHashEntry *entry;
+
+  _dbus_assert (table->key_type == DBUS_HASH_ULONG);
+  
+  entry = (* table->find_function) (table, (void*) key, FALSE, NULL, NULL);
+
+  if (entry)
+    return entry->value;
+  else
+    return NULL;
+}
+
+/**
+ * Removes the hash entry for the given key. If no hash entry
+ * for the key exists, does nothing.
+ *
+ * @param table the hash table.
+ * @param key the hash key.
+ * @returns #TRUE if the entry existed
+ */
+dbus_bool_t
+_dbus_hash_table_remove_string (DBusHashTable *table,
+                                const char    *key)
+{
+  DBusHashEntry *entry;
+  DBusHashEntry **bucket;
+  
+  _dbus_assert (table->key_type == DBUS_HASH_STRING);
+  
+  entry = (* table->find_function) (table, (char*) key, FALSE, &bucket, NULL);
+
+  if (entry)
+    {
+      remove_entry (table, bucket, entry);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Removes the hash entry for the given key. If no hash entry
+ * for the key exists, does nothing.
+ *
+ * @param table the hash table.
+ * @param key the hash key.
+ * @returns #TRUE if the entry existed
+ */
+dbus_bool_t
+_dbus_hash_table_remove_two_strings (DBusHashTable *table,
+                                     const char    *key)
+{
+  DBusHashEntry *entry;
+  DBusHashEntry **bucket;
+  
+  _dbus_assert (table->key_type == DBUS_HASH_TWO_STRINGS);
+  
+  entry = (* table->find_function) (table, (char*) key, FALSE, &bucket, NULL);
+
+  if (entry)
+    {
+      remove_entry (table, bucket, entry);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * Removes the hash entry for the given key. If no hash entry
+ * for the key exists, does nothing.
+ *
+ * @param table the hash table.
+ * @param key the hash key.
+ * @returns #TRUE if the entry existed
+ */
+dbus_bool_t
+_dbus_hash_table_remove_int (DBusHashTable *table,
+                             int            key)
+{
+  DBusHashEntry *entry;
+  DBusHashEntry **bucket;
+  
+  _dbus_assert (table->key_type == DBUS_HASH_INT);
+  
+  entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), FALSE, &bucket, NULL);
+  
+  if (entry)
+    {
+      remove_entry (table, bucket, entry);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+#ifdef DBUS_BUILD_TESTS
+/* disabled since it's only used for testing */
+/**
+ * Removes the hash entry for the given key. If no hash entry
+ * for the key exists, does nothing.
+ *
+ * @param table the hash table.
+ * @param key the hash key.
+ * @returns #TRUE if the entry existed
+ */
+dbus_bool_t
+_dbus_hash_table_remove_pointer (DBusHashTable *table,
+                                 void          *key)
+{
+  DBusHashEntry *entry;
+  DBusHashEntry **bucket;
+  
+  _dbus_assert (table->key_type == DBUS_HASH_POINTER);
+  
+  entry = (* table->find_function) (table, key, FALSE, &bucket, NULL);
+  
+  if (entry)
+    {
+      remove_entry (table, bucket, entry);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * Removes the hash entry for the given key. If no hash entry
+ * for the key exists, does nothing.
+ *
+ * @param table the hash table.
+ * @param key the hash key.
+ * @returns #TRUE if the entry existed
+ */
+dbus_bool_t
+_dbus_hash_table_remove_ulong (DBusHashTable *table,
+                               unsigned long  key)
+{
+  DBusHashEntry *entry;
+  DBusHashEntry **bucket;
+  
+  _dbus_assert (table->key_type == DBUS_HASH_ULONG);
+  
+  entry = (* table->find_function) (table, (void*) key, FALSE, &bucket, NULL);
+  
+  if (entry)
+    {
+      remove_entry (table, bucket, entry);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+/**
+ * Creates a hash entry with the given key and value.
+ * The key and value are not copied; they are stored
+ * in the hash table by reference. If an entry with the
+ * given key already exists, the previous key and value
+ * are overwritten (and freed if the hash table has
+ * a key_free_function and/or value_free_function).
+ *
+ * Returns #FALSE if memory for the new hash entry
+ * can't be allocated.
+ * 
+ * @param table the hash table.
+ * @param key the hash entry key.
+ * @param value the hash entry value.
+ */
+dbus_bool_t
+_dbus_hash_table_insert_string (DBusHashTable *table,
+                                char          *key,
+                                void          *value)
+{
+  DBusPreallocatedHash *preallocated;
+
+  _dbus_assert (table->key_type == DBUS_HASH_STRING);
+
+  preallocated = _dbus_hash_table_preallocate_entry (table);
+  if (preallocated == NULL)
+    return FALSE;
+
+  _dbus_hash_table_insert_string_preallocated (table, preallocated,
+                                               key, value);
+  
+  return TRUE;
+}
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Creates a hash entry with the given key and value.
+ * The key and value are not copied; they are stored
+ * in the hash table by reference. If an entry with the
+ * given key already exists, the previous key and value
+ * are overwritten (and freed if the hash table has
+ * a key_free_function and/or value_free_function).
+ *
+ * Returns #FALSE if memory for the new hash entry
+ * can't be allocated.
+ * 
+ * @param table the hash table.
+ * @param key the hash entry key.
+ * @param value the hash entry value.
+ */
+dbus_bool_t
+_dbus_hash_table_insert_two_strings (DBusHashTable *table,
+                                     char          *key,
+                                     void          *value)
+{
+  DBusHashEntry *entry;
+  
+  _dbus_assert (table->key_type == DBUS_HASH_TWO_STRINGS);
+  
+  entry = (* table->find_function) (table, key, TRUE, NULL, NULL);
+
+  if (entry == NULL)
+    return FALSE; /* no memory */
+
+  if (table->free_key_function && entry->key != key)
+    (* table->free_key_function) (entry->key);
+  
+  if (table->free_value_function && entry->value != value)
+    (* table->free_value_function) (entry->value);
+  
+  entry->key = key;
+  entry->value = value;
+
+  return TRUE;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * Creates a hash entry with the given key and value.
+ * The key and value are not copied; they are stored
+ * in the hash table by reference. If an entry with the
+ * given key already exists, the previous key and value
+ * are overwritten (and freed if the hash table has
+ * a key_free_function and/or value_free_function).
+ *
+ * Returns #FALSE if memory for the new hash entry
+ * can't be allocated.
+ * 
+ * @param table the hash table.
+ * @param key the hash entry key.
+ * @param value the hash entry value.
+ */
+dbus_bool_t
+_dbus_hash_table_insert_int (DBusHashTable *table,
+                             int            key,
+                             void          *value)
+{
+  DBusHashEntry *entry;
+
+  _dbus_assert (table->key_type == DBUS_HASH_INT);
+  
+  entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), TRUE, NULL, NULL);
+
+  if (entry == NULL)
+    return FALSE; /* no memory */
+
+  if (table->free_key_function && entry->key != _DBUS_INT_TO_POINTER (key))
+    (* table->free_key_function) (entry->key);
+  
+  if (table->free_value_function && entry->value != value)
+    (* table->free_value_function) (entry->value);
+  
+  entry->key = _DBUS_INT_TO_POINTER (key);
+  entry->value = value;
+
+  return TRUE;
+}
+
+#ifdef DBUS_BUILD_TESTS
+/* disabled since it's only used for testing */
+/**
+ * Creates a hash entry with the given key and value.
+ * The key and value are not copied; they are stored
+ * in the hash table by reference. If an entry with the
+ * given key already exists, the previous key and value
+ * are overwritten (and freed if the hash table has
+ * a key_free_function and/or value_free_function).
+ *
+ * Returns #FALSE if memory for the new hash entry
+ * can't be allocated.
+ * 
+ * @param table the hash table.
+ * @param key the hash entry key.
+ * @param value the hash entry value.
+ */
+dbus_bool_t
+_dbus_hash_table_insert_pointer (DBusHashTable *table,
+                                 void          *key,
+                                 void          *value)
+{
+  DBusHashEntry *entry;
+
+  _dbus_assert (table->key_type == DBUS_HASH_POINTER);
+  
+  entry = (* table->find_function) (table, key, TRUE, NULL, NULL);
+
+  if (entry == NULL)
+    return FALSE; /* no memory */
+
+  if (table->free_key_function && entry->key != key)
+    (* table->free_key_function) (entry->key);
+  
+  if (table->free_value_function && entry->value != value)
+    (* table->free_value_function) (entry->value);
+  
+  entry->key = key;
+  entry->value = value;
+
+  return TRUE;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * Creates a hash entry with the given key and value.
+ * The key and value are not copied; they are stored
+ * in the hash table by reference. If an entry with the
+ * given key already exists, the previous key and value
+ * are overwritten (and freed if the hash table has
+ * a key_free_function and/or value_free_function).
+ *
+ * Returns #FALSE if memory for the new hash entry
+ * can't be allocated.
+ * 
+ * @param table the hash table.
+ * @param key the hash entry key.
+ * @param value the hash entry value.
+ */
+dbus_bool_t
+_dbus_hash_table_insert_ulong (DBusHashTable *table,
+                               unsigned long  key,
+                               void          *value)
+{
+  DBusHashEntry *entry;
+
+  _dbus_assert (table->key_type == DBUS_HASH_ULONG);
+  
+  entry = (* table->find_function) (table, (void*) key, TRUE, NULL, NULL);
+
+  if (entry == NULL)
+    return FALSE; /* no memory */
+
+  if (table->free_key_function && entry->key != (void*) key)
+    (* table->free_key_function) (entry->key);
+  
+  if (table->free_value_function && entry->value != value)
+    (* table->free_value_function) (entry->value);
+  
+  entry->key = (void*) key;
+  entry->value = value;
+
+  return TRUE;
+}
+
+/**
+ * Preallocate an opaque data blob that allows us to insert into the
+ * hash table at a later time without allocating any memory.
+ *
+ * @param table the hash table
+ * @returns the preallocated data, or #NULL if no memory
+ */
+DBusPreallocatedHash*
+_dbus_hash_table_preallocate_entry (DBusHashTable *table)
+{
+  DBusHashEntry *entry;
+  
+  entry = alloc_entry (table);
+
+  return (DBusPreallocatedHash*) entry;
+}
+
+/**
+ * Frees an opaque DBusPreallocatedHash that was *not* used
+ * in order to insert into the hash table.
+ *
+ * @param table the hash table
+ * @param preallocated the preallocated data
+ */
+void
+_dbus_hash_table_free_preallocated_entry (DBusHashTable        *table,
+                                          DBusPreallocatedHash *preallocated)
+{
+  DBusHashEntry *entry;
+
+  _dbus_assert (preallocated != NULL);
+  
+  entry = (DBusHashEntry*) preallocated;
+  
+  /* Don't use free_entry(), since this entry has no key/data */
+  _dbus_mem_pool_dealloc (table->entry_pool, entry);
+}
+
+/**
+ * Inserts a string-keyed entry into the hash table, using a
+ * preallocated data block from
+ * _dbus_hash_table_preallocate_entry(). This function cannot fail due
+ * to lack of memory. The DBusPreallocatedHash object is consumed and
+ * should not be reused or freed. Otherwise this function works
+ * just like _dbus_hash_table_insert_string().
+ *
+ * @param table the hash table
+ * @param preallocated the preallocated data
+ * @param key the hash key
+ * @param value the value 
+ */
+void
+_dbus_hash_table_insert_string_preallocated (DBusHashTable        *table,
+                                             DBusPreallocatedHash *preallocated,
+                                             char                 *key,
+                                             void                 *value)
+{
+  DBusHashEntry *entry;
+
+  _dbus_assert (table->key_type == DBUS_HASH_STRING);
+  _dbus_assert (preallocated != NULL);
+  
+  entry = (* table->find_function) (table, key, TRUE, NULL, preallocated);
+
+  _dbus_assert (entry != NULL);
+  
+  if (table->free_key_function && entry->key != key)
+    (* table->free_key_function) (entry->key);
+
+  if (table->free_value_function && entry->value != value)
+    (* table->free_value_function) (entry->value);
+      
+  entry->key = key;
+  entry->value = value;
+}
+
+/**
+ * Gets the number of hash entries in a hash table.
+ *
+ * @param table the hash table.
+ * @returns the number of entries in the table.
+ */
+int
+_dbus_hash_table_get_n_entries (DBusHashTable *table)
+{
+  return table->n_entries;
+}
+
+/** @} */
+
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-test.h"
+#include <stdio.h>
+
+/* If you're wondering why the hash table test takes
+ * forever to run, it's because we call this function
+ * in inner loops thus making things quadratic.
+ */
+static int
+count_entries (DBusHashTable *table)
+{
+  DBusHashIter iter;
+  int count;
+
+  count = 0;
+  _dbus_hash_iter_init (table, &iter);
+  while (_dbus_hash_iter_next (&iter))
+    ++count;
+
+  _dbus_assert (count == _dbus_hash_table_get_n_entries (table));
+  
+  return count;
+}
+
+/* Copy the foo\0bar\0 double string thing */
+static char*
+_dbus_strdup2 (const char *str)
+{
+  size_t len;
+  char *copy;
+  
+  if (str == NULL)
+    return NULL;
+  
+  len = strlen (str);
+  len += strlen ((str + len + 1));
+
+  copy = dbus_malloc (len + 2);
+  if (copy == NULL)
+    return NULL;
+
+  memcpy (copy, str, len + 2);
+  
+  return copy;
+}
+
+/**
+ * @ingroup DBusHashTableInternals
+ * Unit test for DBusHashTable
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_hash_test (void)
+{
+  int i;
+  DBusHashTable *table1;
+  DBusHashTable *table2;
+  DBusHashTable *table3;
+  DBusHashTable *table4;
+  DBusHashIter iter;
+#define N_HASH_KEYS 5000
+  char **keys;
+  dbus_bool_t ret = FALSE;
+
+  keys = dbus_new (char *, N_HASH_KEYS);
+  if (keys == NULL)
+    _dbus_assert_not_reached ("no memory");
+
+  for (i = 0; i < N_HASH_KEYS; i++)
+    {
+      keys[i] = dbus_malloc (128);
+
+      if (keys[i] == NULL)
+       _dbus_assert_not_reached ("no memory");
+    }
+
+  printf ("Computing test hash keys...\n");
+  i = 0;
+  while (i < N_HASH_KEYS)
+    {
+      int len;
+
+      /* all the hash keys are TWO_STRINGS, but
+       * then we can also use those as regular strings.
+       */
+      
+      len = sprintf (keys[i], "Hash key %d", i);
+      sprintf (keys[i] + len + 1, "Two string %d", i);
+      _dbus_assert (*(keys[i] + len) == '\0');
+      _dbus_assert (*(keys[i] + len + 1) != '\0');
+      ++i;
+    }
+  printf ("... done.\n");
+  
+  table1 = _dbus_hash_table_new (DBUS_HASH_STRING,
+                                 dbus_free, dbus_free);
+  if (table1 == NULL)
+    goto out;
+
+  table2 = _dbus_hash_table_new (DBUS_HASH_INT,
+                                 NULL, dbus_free);
+  if (table2 == NULL)
+    goto out;
+
+  table3 = _dbus_hash_table_new (DBUS_HASH_ULONG,
+                                 NULL, dbus_free);
+  if (table3 == NULL)
+    goto out;
+
+  table4 = _dbus_hash_table_new (DBUS_HASH_TWO_STRINGS,
+                                 dbus_free, dbus_free);
+  if (table4 == NULL)
+    goto out;
+
+  
+  /* Insert and remove a bunch of stuff, counting the table in between
+   * to be sure it's not broken and that iteration works
+   */
+  i = 0;
+  while (i < 3000)
+    {
+      void *value;
+      char *key;
+
+      key = _dbus_strdup (keys[i]);
+      if (key == NULL)
+        goto out;
+      value = _dbus_strdup ("Value!");
+      if (value == NULL)
+        goto out;
+      
+      if (!_dbus_hash_table_insert_string (table1,
+                                           key, value))
+        goto out;
+
+      value = _dbus_strdup (keys[i]);
+      if (value == NULL)
+        goto out;
+      
+      if (!_dbus_hash_table_insert_int (table2,
+                                        i, value))
+        goto out;
+
+      value = _dbus_strdup (keys[i]);
+      if (value == NULL)
+        goto out;
+      
+      if (!_dbus_hash_table_insert_ulong (table3,
+                                          i, value))
+        goto out;
+
+      key = _dbus_strdup2 (keys[i]);
+      if (key == NULL)
+        goto out;
+      value = _dbus_strdup ("Value!");
+      if (value == NULL)
+        goto out;
+      
+      if (!_dbus_hash_table_insert_two_strings (table4,
+                                                key, value))
+        goto out;
+      
+      _dbus_assert (count_entries (table1) == i + 1);
+      _dbus_assert (count_entries (table2) == i + 1);
+      _dbus_assert (count_entries (table3) == i + 1);
+      _dbus_assert (count_entries (table4) == i + 1);
+
+      value = _dbus_hash_table_lookup_string (table1, keys[i]);
+      _dbus_assert (value != NULL);
+      _dbus_assert (strcmp (value, "Value!") == 0);
+
+      value = _dbus_hash_table_lookup_int (table2, i);
+      _dbus_assert (value != NULL);
+      _dbus_assert (strcmp (value, keys[i]) == 0);
+
+      value = _dbus_hash_table_lookup_ulong (table3, i);
+      _dbus_assert (value != NULL);
+      _dbus_assert (strcmp (value, keys[i]) == 0);
+
+      value = _dbus_hash_table_lookup_two_strings (table4, keys[i]);
+      _dbus_assert (value != NULL);
+      _dbus_assert (strcmp (value, "Value!") == 0);
+      
+      ++i;
+    }
+
+  --i;
+  while (i >= 0)
+    {
+      _dbus_hash_table_remove_string (table1,
+                                      keys[i]);
+
+      _dbus_hash_table_remove_int (table2, i);
+
+      _dbus_hash_table_remove_ulong (table3, i); 
+
+      _dbus_hash_table_remove_two_strings (table4,
+                                           keys[i]);
+      
+      _dbus_assert (count_entries (table1) == i);
+      _dbus_assert (count_entries (table2) == i);
+      _dbus_assert (count_entries (table3) == i);
+      _dbus_assert (count_entries (table4) == i);
+
+      --i;
+    }
+
+  _dbus_hash_table_ref (table1);
+  _dbus_hash_table_ref (table2);
+  _dbus_hash_table_ref (table3);
+  _dbus_hash_table_ref (table4);
+  _dbus_hash_table_unref (table1);
+  _dbus_hash_table_unref (table2);
+  _dbus_hash_table_unref (table3);
+  _dbus_hash_table_unref (table4);
+  _dbus_hash_table_unref (table1);
+  _dbus_hash_table_unref (table2);
+  _dbus_hash_table_unref (table3);
+  _dbus_hash_table_unref (table4);
+  table3 = NULL;
+
+  /* Insert a bunch of stuff then check
+   * that iteration works correctly (finds the right
+   * values, iter_set_value works, etc.)
+   */
+  table1 = _dbus_hash_table_new (DBUS_HASH_STRING,
+                                 dbus_free, dbus_free);
+  if (table1 == NULL)
+    goto out;
+  
+  table2 = _dbus_hash_table_new (DBUS_HASH_INT,
+                                 NULL, dbus_free);
+  if (table2 == NULL)
+    goto out;
+  
+  i = 0;
+  while (i < 5000)
+    {
+      char *key;
+      void *value;      
+      
+      key = _dbus_strdup (keys[i]);
+      if (key == NULL)
+        goto out;
+      value = _dbus_strdup ("Value!");
+      if (value == NULL)
+        goto out;
+      
+      if (!_dbus_hash_table_insert_string (table1,
+                                           key, value))
+        goto out;
+
+      value = _dbus_strdup (keys[i]);
+      if (value == NULL)
+        goto out;
+      
+      if (!_dbus_hash_table_insert_int (table2,
+                                        i, value))
+        goto out;
+      
+      _dbus_assert (count_entries (table1) == i + 1);
+      _dbus_assert (count_entries (table2) == i + 1);
+      
+      ++i;
+    }
+
+  _dbus_hash_iter_init (table1, &iter);
+  while (_dbus_hash_iter_next (&iter))
+    {
+      const char *key;
+      void *value;
+
+      key = _dbus_hash_iter_get_string_key (&iter);
+      value = _dbus_hash_iter_get_value (&iter);
+
+      _dbus_assert (_dbus_hash_table_lookup_string (table1, key) == value);
+
+      value = _dbus_strdup ("Different value!");
+      if (value == NULL)
+        goto out;
+      
+      _dbus_hash_iter_set_value (&iter, value);
+
+      _dbus_assert (_dbus_hash_table_lookup_string (table1, key) == value);
+    }
+  
+  _dbus_hash_iter_init (table1, &iter);
+  while (_dbus_hash_iter_next (&iter))
+    {
+      _dbus_hash_iter_remove_entry (&iter);
+      _dbus_assert (count_entries (table1) == i - 1);
+      --i;
+    }
+
+  _dbus_hash_iter_init (table2, &iter);
+  while (_dbus_hash_iter_next (&iter))
+    {
+      int key;
+      void *value;
+
+      key = _dbus_hash_iter_get_int_key (&iter);
+      value = _dbus_hash_iter_get_value (&iter);
+
+      _dbus_assert (_dbus_hash_table_lookup_int (table2, key) == value);
+
+      value = _dbus_strdup ("Different value!");
+      if (value == NULL)
+        goto out;
+      
+      _dbus_hash_iter_set_value (&iter, value);
+
+      _dbus_assert (_dbus_hash_table_lookup_int (table2, key) == value);
+    }
+
+  i = count_entries (table2);
+  _dbus_hash_iter_init (table2, &iter);
+  while (_dbus_hash_iter_next (&iter))
+    {
+      _dbus_hash_iter_remove_entry (&iter);
+      _dbus_assert (count_entries (table2) + 1 == i);
+      --i;
+    }
+
+  /* add/remove interleaved, to check that we grow/shrink the table
+   * appropriately
+   */
+  i = 0;
+  while (i < 1000)
+    {
+      char *key;
+      void *value;
+            
+      key = _dbus_strdup (keys[i]);
+      if (key == NULL)
+        goto out;
+
+      value = _dbus_strdup ("Value!");
+      if (value == NULL)
+        goto out;
+      
+      if (!_dbus_hash_table_insert_string (table1,
+                                           key, value))
+        goto out;
+      
+      ++i;
+    }
+
+  --i;
+  while (i >= 0)
+    {
+      char *key;
+      void *value;      
+      
+      key = _dbus_strdup (keys[i]);
+      if (key == NULL)
+        goto out;
+      value = _dbus_strdup ("Value!");
+      if (value == NULL)
+        goto out;
+
+      if (!_dbus_hash_table_remove_string (table1, keys[i]))
+        goto out;
+      
+      if (!_dbus_hash_table_insert_string (table1,
+                                           key, value))
+        goto out;
+
+      if (!_dbus_hash_table_remove_string (table1, keys[i]))
+        goto out;
+      
+      _dbus_assert (_dbus_hash_table_get_n_entries (table1) == i);
+      
+      --i;
+    }
+
+  /* nuke these tables */
+  _dbus_hash_table_unref (table1);
+  _dbus_hash_table_unref (table2);
+
+
+  /* Now do a bunch of things again using _dbus_hash_iter_lookup() to
+   * be sure that interface works.
+   */
+  table1 = _dbus_hash_table_new (DBUS_HASH_STRING,
+                                 dbus_free, dbus_free);
+  if (table1 == NULL)
+    goto out;
+  
+  table2 = _dbus_hash_table_new (DBUS_HASH_INT,
+                                 NULL, dbus_free);
+  if (table2 == NULL)
+    goto out;
+  
+  i = 0;
+  while (i < 3000)
+    {
+      void *value;
+      char *key;
+
+      key = _dbus_strdup (keys[i]);
+      if (key == NULL)
+        goto out;
+      value = _dbus_strdup ("Value!");
+      if (value == NULL)
+        goto out;
+      
+      if (!_dbus_hash_iter_lookup (table1,
+                                   key, TRUE, &iter))
+        goto out;
+      _dbus_assert (_dbus_hash_iter_get_value (&iter) == NULL);
+      _dbus_hash_iter_set_value (&iter, value);
+
+      value = _dbus_strdup (keys[i]);
+      if (value == NULL)
+        goto out;
+
+      if (!_dbus_hash_iter_lookup (table2,
+                                   _DBUS_INT_TO_POINTER (i), TRUE, &iter))
+        goto out;
+      _dbus_assert (_dbus_hash_iter_get_value (&iter) == NULL);
+      _dbus_hash_iter_set_value (&iter, value); 
+      
+      _dbus_assert (count_entries (table1) == i + 1);
+      _dbus_assert (count_entries (table2) == i + 1);
+
+      if (!_dbus_hash_iter_lookup (table1, keys[i], FALSE, &iter))
+        goto out;
+      
+      value = _dbus_hash_iter_get_value (&iter);
+      _dbus_assert (value != NULL);
+      _dbus_assert (strcmp (value, "Value!") == 0);
+
+      /* Iterate just to be sure it works, though
+       * it's a stupid thing to do
+       */
+      while (_dbus_hash_iter_next (&iter))
+        ;
+      
+      if (!_dbus_hash_iter_lookup (table2, _DBUS_INT_TO_POINTER (i), FALSE, &iter))
+        goto out;
+
+      value = _dbus_hash_iter_get_value (&iter);
+      _dbus_assert (value != NULL);
+      _dbus_assert (strcmp (value, keys[i]) == 0);
+
+      /* Iterate just to be sure it works, though
+       * it's a stupid thing to do
+       */
+      while (_dbus_hash_iter_next (&iter))
+        ;
+      
+      ++i;
+    }
+
+  --i;
+  while (i >= 0)
+    {
+      if (!_dbus_hash_iter_lookup (table1, keys[i], FALSE, &iter))
+        _dbus_assert_not_reached ("hash entry should have existed");
+      _dbus_hash_iter_remove_entry (&iter);
+      
+      if (!_dbus_hash_iter_lookup (table2, _DBUS_INT_TO_POINTER (i), FALSE, &iter))
+        _dbus_assert_not_reached ("hash entry should have existed");
+      _dbus_hash_iter_remove_entry (&iter);
+
+      _dbus_assert (count_entries (table1) == i);
+      _dbus_assert (count_entries (table2) == i);
+
+      --i;
+    }
+
+  _dbus_hash_table_unref (table1);
+  _dbus_hash_table_unref (table2);
+
+  ret = TRUE;
+
+ out:
+  for (i = 0; i < N_HASH_KEYS; i++)
+    dbus_free (keys[i]);
+
+  dbus_free (keys);
+  
+  return ret;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-hash.h b/src/dbus/dbus-hash.h
new file mode 100644 (file)
index 0000000..661e86d
--- /dev/null
@@ -0,0 +1,142 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-hash.h Generic hash table utility (internal to D-Bus implementation)
+ * 
+ * Copyright (C) 2002  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_HASH_H
+#define DBUS_HASH_H
+
+#include <dbus/dbus-memory.h>
+#include <dbus/dbus-types.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusHashTable
+ * @{
+ */
+
+/** Hash iterator object. The iterator is on the stack, but its real
+ * fields are hidden privately.
+ */
+struct DBusHashIter
+{
+  void *dummy1; /**< Do not use. */
+  void *dummy2; /**< Do not use. */
+  void *dummy3; /**< Do not use. */
+  void *dummy4; /**< Do not use. */
+  int   dummy5; /**< Do not use. */
+  int   dummy6; /**< Do not use. */
+};
+
+typedef struct DBusHashTable DBusHashTable;
+typedef struct DBusHashIter  DBusHashIter;
+
+/* Allowing an arbitrary function as with GLib
+ * would be nicer for a public API, but for
+ * an internal API this saves typing, we can add
+ * more whenever we feel like it.
+ */
+typedef enum
+{
+  DBUS_HASH_STRING,        /**< Hash keys are strings. */
+  DBUS_HASH_TWO_STRINGS,   /**< Hash key is two strings in one memory block, i.e. foo\\0bar\\0 */
+  DBUS_HASH_INT,           /**< Hash keys are integers. */
+  DBUS_HASH_POINTER,       /**< Hash keys are pointers. */
+  DBUS_HASH_ULONG          /**< Hash keys are unsigned long. */
+} DBusHashType;
+
+DBusHashTable* _dbus_hash_table_new                (DBusHashType      type,
+                                                    DBusFreeFunction  key_free_function,
+                                                    DBusFreeFunction  value_free_function);
+DBusHashTable* _dbus_hash_table_ref                (DBusHashTable    *table);
+void           _dbus_hash_table_unref              (DBusHashTable    *table);
+void           _dbus_hash_table_remove_all         (DBusHashTable    *table);
+void           _dbus_hash_iter_init                (DBusHashTable    *table,
+                                                    DBusHashIter     *iter);
+dbus_bool_t    _dbus_hash_iter_next                (DBusHashIter     *iter);
+void           _dbus_hash_iter_remove_entry        (DBusHashIter     *iter);
+void*          _dbus_hash_iter_get_value           (DBusHashIter     *iter);
+void           _dbus_hash_iter_set_value           (DBusHashIter     *iter,
+                                                    void             *value);
+int            _dbus_hash_iter_get_int_key         (DBusHashIter     *iter);
+const char*    _dbus_hash_iter_get_string_key      (DBusHashIter     *iter);
+const char*    _dbus_hash_iter_get_two_strings_key (DBusHashIter     *iter);
+unsigned long  _dbus_hash_iter_get_ulong_key       (DBusHashIter     *iter);
+dbus_bool_t    _dbus_hash_iter_lookup              (DBusHashTable    *table,
+                                                    void             *key,
+                                                    dbus_bool_t       create_if_not_found,
+                                                    DBusHashIter     *iter);
+void*          _dbus_hash_table_lookup_string      (DBusHashTable    *table,
+                                                    const char       *key);
+void*          _dbus_hash_table_lookup_two_strings (DBusHashTable    *table,
+                                                    const char       *key);
+void*          _dbus_hash_table_lookup_int         (DBusHashTable    *table,
+                                                    int               key);
+void*          _dbus_hash_table_lookup_pointer     (DBusHashTable    *table,
+                                                    void             *key);
+void*          _dbus_hash_table_lookup_ulong       (DBusHashTable    *table,
+                                                    unsigned long     key);
+dbus_bool_t    _dbus_hash_table_remove_string      (DBusHashTable    *table,
+                                                    const char       *key);
+dbus_bool_t    _dbus_hash_table_remove_two_strings (DBusHashTable    *table,
+                                                    const char       *key);
+dbus_bool_t    _dbus_hash_table_remove_int         (DBusHashTable    *table,
+                                                    int               key);
+dbus_bool_t    _dbus_hash_table_remove_pointer     (DBusHashTable    *table,
+                                                    void             *key);
+dbus_bool_t    _dbus_hash_table_remove_ulong       (DBusHashTable    *table,
+                                                    unsigned long     key);
+dbus_bool_t    _dbus_hash_table_insert_string      (DBusHashTable    *table,
+                                                    char             *key,
+                                                    void             *value);
+dbus_bool_t    _dbus_hash_table_insert_two_strings (DBusHashTable    *table,
+                                                    char             *key,
+                                                    void             *value);
+dbus_bool_t    _dbus_hash_table_insert_int         (DBusHashTable    *table,
+                                                    int               key,
+                                                    void             *value);
+dbus_bool_t    _dbus_hash_table_insert_pointer     (DBusHashTable    *table,
+                                                    void             *key,
+                                                    void             *value);
+dbus_bool_t    _dbus_hash_table_insert_ulong       (DBusHashTable    *table,
+                                                    unsigned long     key,
+                                                    void             *value);
+int            _dbus_hash_table_get_n_entries      (DBusHashTable    *table);
+
+/* Preallocation */
+
+/** A preallocated hash entry */
+typedef struct DBusPreallocatedHash DBusPreallocatedHash;
+
+DBusPreallocatedHash *_dbus_hash_table_preallocate_entry          (DBusHashTable        *table);
+void                  _dbus_hash_table_free_preallocated_entry    (DBusHashTable        *table,
+                                                                   DBusPreallocatedHash *preallocated);
+void                  _dbus_hash_table_insert_string_preallocated (DBusHashTable        *table,
+                                                                   DBusPreallocatedHash *preallocated,
+                                                                   char                 *key,
+                                                                   void                 *value);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_HASH_H */
diff --git a/src/dbus/dbus-internals.c b/src/dbus/dbus-internals.c
new file mode 100644 (file)
index 0000000..f3ca7c5
--- /dev/null
@@ -0,0 +1,951 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-internals.c  random utility stuff (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-internals.h"
+#include "dbus-protocol.h"
+#include "dbus-marshal-basic.h"
+#include "dbus-test.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+
+/**
+ * @defgroup DBusInternals D-Bus secret internal implementation details
+ * @brief Documentation useful when developing or debugging D-Bus itself.
+ * 
+ */
+
+/**
+ * @defgroup DBusInternalsUtils Utilities and portability
+ * @ingroup DBusInternals
+ * @brief Utility functions (_dbus_assert(), _dbus_warn(), etc.)
+ * @{
+ */
+
+/**
+ * @def _dbus_assert
+ *
+ * Aborts with an error message if the condition is false.
+ * 
+ * @param condition condition which must be true.
+ */
+
+/**
+ * @def _dbus_assert_not_reached
+ *
+ * Aborts with an error message if called.
+ * The given explanation will be printed.
+ * 
+ * @param explanation explanation of what happened if the code was reached.
+ */
+
+/**
+ * @def _DBUS_N_ELEMENTS
+ *
+ * Computes the number of elements in a fixed-size array using
+ * sizeof().
+ *
+ * @param array the array to count elements in.
+ */
+
+/**
+ * @def _DBUS_POINTER_TO_INT
+ *
+ * Safely casts a void* to an integer; should only be used on void*
+ * that actually contain integers, for example one created with
+ * _DBUS_INT_TO_POINTER.  Only guaranteed to preserve 32 bits.
+ * (i.e. it's used to store 32-bit ints in pointers, but
+ * can't be used to store 64-bit pointers in ints.)
+ *
+ * @param pointer pointer to extract an integer from.
+ */
+/**
+ * @def _DBUS_INT_TO_POINTER
+ *
+ * Safely stuffs an integer into a pointer, to be extracted later with
+ * _DBUS_POINTER_TO_INT. Only guaranteed to preserve 32 bits.
+ *
+ * @param integer the integer to stuff into a pointer.
+ */
+/**
+ * @def _DBUS_ZERO
+ *
+ * Sets all bits in an object to zero.
+ *
+ * @param object the object to be zeroed.
+ */
+/**
+ * @def _DBUS_INT16_MIN
+ *
+ * Minimum value of type "int16"
+ */
+/**
+ * @def _DBUS_INT16_MAX
+ *
+ * Maximum value of type "int16"
+ */
+/**
+ * @def _DBUS_UINT16_MAX
+ *
+ * Maximum value of type "uint16"
+ */
+
+/**
+ * @def _DBUS_INT32_MIN
+ *
+ * Minimum value of type "int32"
+ */
+/**
+ * @def _DBUS_INT32_MAX
+ *
+ * Maximum value of type "int32"
+ */
+/**
+ * @def _DBUS_UINT32_MAX
+ *
+ * Maximum value of type "uint32"
+ */
+
+/**
+ * @def _DBUS_INT_MIN
+ *
+ * Minimum value of type "int"
+ */
+/**
+ * @def _DBUS_INT_MAX
+ *
+ * Maximum value of type "int"
+ */
+/**
+ * @def _DBUS_UINT_MAX
+ *
+ * Maximum value of type "uint"
+ */
+
+/**
+ * @typedef DBusForeachFunction
+ * 
+ * Used to iterate over each item in a collection, such as
+ * a DBusList.
+ */
+
+/**
+ * @def _DBUS_LOCK_NAME
+ *
+ * Expands to name of a global lock variable.
+ */
+
+/**
+ * @def _DBUS_DEFINE_GLOBAL_LOCK
+ *
+ * Defines a global lock variable with the given name.
+ * The lock must be added to the list to initialize
+ * in dbus_threads_init().
+ */
+
+/**
+ * @def _DBUS_DECLARE_GLOBAL_LOCK
+ *
+ * Expands to declaration of a global lock defined
+ * with _DBUS_DEFINE_GLOBAL_LOCK.
+ * The lock must be added to the list to initialize
+ * in dbus_threads_init().
+ */
+
+/**
+ * @def _DBUS_LOCK
+ *
+ * Locks a global lock
+ */
+
+/**
+ * @def _DBUS_UNLOCK
+ *
+ * Unlocks a global lock
+ */
+
+/**
+ * Fixed "out of memory" error message, just to avoid
+ * making up a different string every time and wasting
+ * space.
+ */
+const char _dbus_no_memory_message[] = "Not enough memory";
+
+static dbus_bool_t warn_initted = FALSE;
+static dbus_bool_t fatal_warnings = FALSE;
+static dbus_bool_t fatal_warnings_on_check_failed = TRUE;
+
+static void
+init_warnings(void)
+{
+  if (!warn_initted)
+    {
+      const char *s;
+      s = _dbus_getenv ("DBUS_FATAL_WARNINGS");
+      if (s && *s)
+        {
+          if (*s == '0')
+            {
+              fatal_warnings = FALSE;
+              fatal_warnings_on_check_failed = FALSE;
+            }
+          else if (*s == '1')
+            {
+              fatal_warnings = TRUE;
+              fatal_warnings_on_check_failed = TRUE;
+            }
+          else
+            {
+              fprintf(stderr, "DBUS_FATAL_WARNINGS should be set to 0 or 1 if set, not '%s'",
+                      s);
+            }
+        }
+
+      warn_initted = TRUE;
+    }
+}
+
+/**
+ * Prints a warning message to stderr. Can optionally be made to exit
+ * fatally by setting DBUS_FATAL_WARNINGS, but this is rarely
+ * used. This function should be considered pretty much equivalent to
+ * fprintf(stderr). _dbus_warn_check_failed() on the other hand is
+ * suitable for use when a programming mistake has been made.
+ *
+ * @param format printf-style format string.
+ */
+void
+_dbus_warn (const char *format,
+            ...)
+{
+  va_list args;
+
+  if (!warn_initted)
+    init_warnings ();
+  
+  va_start (args, format);
+  vfprintf (stderr, format, args);
+  va_end (args);
+
+  if (fatal_warnings)
+    {
+      fflush (stderr);
+      _dbus_abort ();
+    }
+}
+
+/**
+ * Prints a "critical" warning to stderr when an assertion fails;
+ * differs from _dbus_warn primarily in that it prefixes the pid and
+ * defaults to fatal. This should be used only when a programming
+ * error has been detected. (NOT for unavoidable errors that an app
+ * might handle - those should be returned as DBusError.) Calling this
+ * means "there is a bug"
+ */
+void
+_dbus_warn_check_failed(const char *format,
+                        ...)
+{
+  va_list args;
+  
+  if (!warn_initted)
+    init_warnings ();
+
+  fprintf (stderr, "process %lu: ", _dbus_pid_for_log ());
+  
+  va_start (args, format);
+  vfprintf (stderr, format, args);
+  va_end (args);
+
+  if (fatal_warnings_on_check_failed)
+    {
+      fflush (stderr);
+      _dbus_abort ();
+    }
+}
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+
+static dbus_bool_t verbose_initted = FALSE;
+static dbus_bool_t verbose = TRUE;
+
+/** Whether to show the current thread in verbose messages */
+#define PTHREAD_IN_VERBOSE 0
+#if PTHREAD_IN_VERBOSE
+#include <pthread.h>
+#endif
+
+#ifdef DBUS_WIN
+#define inline
+#endif
+
+static inline void
+_dbus_verbose_init (void)
+{
+  if (!verbose_initted)
+    {
+      const char *p = _dbus_getenv ("DBUS_VERBOSE"); 
+      verbose = p != NULL && *p == '1';
+      verbose_initted = TRUE;
+    }
+}
+
+/**
+ * Implementation of dbus_is_verbose() macro if built with verbose logging
+ * enabled.
+ * @returns whether verbose logging is active.
+ */
+dbus_bool_t
+_dbus_is_verbose_real (void)
+{
+  _dbus_verbose_init ();
+  return verbose;
+}
+
+/**
+ * Prints a warning message to stderr
+ * if the user has enabled verbose mode.
+ * This is the real function implementation,
+ * use _dbus_verbose() macro in code.
+ *
+ * @param format printf-style format string.
+ */
+void
+_dbus_verbose_real (const char *format,
+                    ...)
+{
+  va_list args;
+  static dbus_bool_t need_pid = TRUE;
+  int len;
+  
+  /* things are written a bit oddly here so that
+   * in the non-verbose case we just have the one
+   * conditional and return immediately.
+   */
+  if (!_dbus_is_verbose_real())
+    return;
+
+  /* Print out pid before the line */
+  if (need_pid)
+    {
+#if PTHREAD_IN_VERBOSE
+      fprintf (stderr, "%lu: 0x%lx: ", _dbus_pid_for_log (), pthread_self ());
+#else
+      fprintf (stderr, "%lu: ", _dbus_pid_for_log ());
+#endif
+    }
+      
+
+  /* Only print pid again if the next line is a new line */
+  len = strlen (format);
+  if (format[len-1] == '\n')
+    need_pid = TRUE;
+  else
+    need_pid = FALSE;
+  
+  va_start (args, format);
+  vfprintf (stderr, format, args);
+  va_end (args);
+
+  fflush (stderr);
+}
+
+/**
+ * Reinitializes the verbose logging code, used
+ * as a hack in dbus-spawn.c so that a child
+ * process re-reads its pid
+ *
+ */
+void
+_dbus_verbose_reset_real (void)
+{
+  verbose_initted = FALSE;
+}
+
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
+
+/**
+ * Duplicates a string. Result must be freed with
+ * dbus_free(). Returns #NULL if memory allocation fails.
+ * If the string to be duplicated is #NULL, returns #NULL.
+ * 
+ * @param str string to duplicate.
+ * @returns newly-allocated copy.
+ */
+char*
+_dbus_strdup (const char *str)
+{
+  size_t len;
+  char *copy;
+  
+  if (str == NULL)
+    return NULL;
+  
+  len = strlen (str);
+
+  copy = dbus_malloc (len + 1);
+  if (copy == NULL)
+    return NULL;
+
+  memcpy (copy, str, len + 1);
+  
+  return copy;
+}
+
+/**
+ * Duplicates a block of memory. Returns
+ * #NULL on failure.
+ *
+ * @param mem memory to copy
+ * @param n_bytes number of bytes to copy
+ * @returns the copy
+ */
+void*
+_dbus_memdup (const void  *mem,
+              size_t       n_bytes)
+{
+  void *copy;
+
+  copy = dbus_malloc (n_bytes);
+  if (copy == NULL)
+    return NULL;
+
+  memcpy (copy, mem, n_bytes);
+  
+  return copy;
+}
+
+/**
+ * Duplicates a string array. Result may be freed with
+ * dbus_free_string_array(). Returns #NULL if memory allocation fails.
+ * If the array to be duplicated is #NULL, returns #NULL.
+ * 
+ * @param array array to duplicate.
+ * @returns newly-allocated copy.
+ */
+char**
+_dbus_dup_string_array (const char **array)
+{
+  int len;
+  int i;
+  char **copy;
+  
+  if (array == NULL)
+    return NULL;
+
+  for (len = 0; array[len] != NULL; ++len)
+    ;
+
+  copy = dbus_new0 (char*, len + 1);
+  if (copy == NULL)
+    return NULL;
+
+  i = 0;
+  while (i < len)
+    {
+      copy[i] = _dbus_strdup (array[i]);
+      if (copy[i] == NULL)
+        {
+          dbus_free_string_array (copy);
+          return NULL;
+        }
+
+      ++i;
+    }
+
+  return copy;
+}
+
+/**
+ * Checks whether a string array contains the given string.
+ * 
+ * @param array array to search.
+ * @param str string to look for
+ * @returns #TRUE if array contains string
+ */
+dbus_bool_t
+_dbus_string_array_contains (const char **array,
+                             const char  *str)
+{
+  int i;
+
+  i = 0;
+  while (array[i] != NULL)
+    {
+      if (strcmp (array[i], str) == 0)
+        return TRUE;
+      ++i;
+    }
+
+  return FALSE;
+}
+
+/**
+ * Generates a new UUID. If you change how this is done,
+ * there's some text about it in the spec that should also change.
+ *
+ * @param uuid the uuid to initialize
+ */
+void
+_dbus_generate_uuid (DBusGUID *uuid)
+{
+  long now;
+
+  _dbus_get_current_time (&now, NULL);
+
+  uuid->as_uint32s[DBUS_UUID_LENGTH_WORDS - 1] = DBUS_UINT32_TO_BE (now);
+  
+  _dbus_generate_random_bytes_buffer (uuid->as_bytes, DBUS_UUID_LENGTH_BYTES - 4);
+}
+
+/**
+ * Hex-encode a UUID.
+ *
+ * @param uuid the uuid
+ * @param encoded string to append hex uuid to
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_uuid_encode (const DBusGUID *uuid,
+                   DBusString     *encoded)
+{
+  DBusString binary;
+  _dbus_string_init_const_len (&binary, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
+  return _dbus_string_hex_encode (&binary, 0, encoded, _dbus_string_get_length (encoded));
+}
+
+static dbus_bool_t
+_dbus_read_uuid_file_without_creating (const DBusString *filename,
+                                       DBusGUID         *uuid,
+                                       DBusError        *error)
+{
+  DBusString contents;
+  DBusString decoded;
+  int end;
+  
+  if (!_dbus_string_init (&contents))
+    {
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+
+  if (!_dbus_string_init (&decoded))
+    {
+      _dbus_string_free (&contents);
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+  
+  if (!_dbus_file_get_contents (&contents, filename, error))
+    goto error;
+
+  _dbus_string_chop_white (&contents);
+
+  if (_dbus_string_get_length (&contents) != DBUS_UUID_LENGTH_HEX)
+    {
+      dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
+                      "UUID file '%s' should contain a hex string of length %d, not length %d, with no other text",
+                      _dbus_string_get_const_data (filename),
+                      DBUS_UUID_LENGTH_HEX,
+                      _dbus_string_get_length (&contents));
+      goto error;
+    }
+
+  if (!_dbus_string_hex_decode (&contents, 0, &end, &decoded, 0))
+    {
+      _DBUS_SET_OOM (error);
+      goto error;
+    }
+
+  if (end == 0)
+    {
+      dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
+                      "UUID file '%s' contains invalid hex data",
+                      _dbus_string_get_const_data (filename));
+      goto error;
+    }
+
+  if (_dbus_string_get_length (&decoded) != DBUS_UUID_LENGTH_BYTES)
+    {
+      dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
+                      "UUID file '%s' contains %d bytes of hex-encoded data instead of %d",
+                      _dbus_string_get_const_data (filename),
+                      _dbus_string_get_length (&decoded),
+                      DBUS_UUID_LENGTH_BYTES);
+      goto error;
+    }
+
+  _dbus_string_copy_to_buffer (&decoded, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
+
+  _dbus_string_free (&decoded);
+  _dbus_string_free (&contents);
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  return TRUE;
+  
+ error:
+  _DBUS_ASSERT_ERROR_IS_SET (error);
+  _dbus_string_free (&contents);
+  _dbus_string_free (&decoded);
+  return FALSE;
+}
+
+static dbus_bool_t
+_dbus_create_uuid_file_exclusively (const DBusString *filename,
+                                    DBusGUID         *uuid,
+                                    DBusError        *error)
+{
+  DBusString encoded;
+
+  if (!_dbus_string_init (&encoded))
+    {
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+
+  _dbus_generate_uuid (uuid);
+  
+  if (!_dbus_uuid_encode (uuid, &encoded))
+    {
+      _DBUS_SET_OOM (error);
+      goto error;
+    }
+  
+  /* FIXME this is racy; we need a save_file_exclusively
+   * function. But in practice this should be fine for now.
+   *
+   * - first be sure we can create the file and it
+   *   doesn't exist by creating it empty with O_EXCL
+   * - then create it by creating a temporary file and
+   *   overwriting atomically with rename()
+   */
+  if (!_dbus_create_file_exclusively (filename, error))
+    goto error;
+
+  if (!_dbus_string_append_byte (&encoded, '\n'))
+    {
+      _DBUS_SET_OOM (error);
+      goto error;
+    }
+  
+  if (!_dbus_string_save_to_file (&encoded, filename, error))
+    goto error;
+
+  if (!_dbus_make_file_world_readable (filename, error))
+    goto error;
+
+  _dbus_string_free (&encoded);
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  return TRUE;
+  
+ error:
+  _DBUS_ASSERT_ERROR_IS_SET (error);
+  _dbus_string_free (&encoded);
+  return FALSE;        
+}
+
+/**
+ * Reads (and optionally writes) a uuid to a file. Initializes the uuid
+ * unless an error is returned.
+ *
+ * @param filename the name of the file
+ * @param uuid uuid to be initialized with the loaded uuid
+ * @param create_if_not_found #TRUE to create a new uuid and save it if the file doesn't exist
+ * @param error the error return
+ * @returns #FALSE if the error is set
+ */
+dbus_bool_t
+_dbus_read_uuid_file (const DBusString *filename,
+                      DBusGUID         *uuid,
+                      dbus_bool_t       create_if_not_found,
+                      DBusError        *error)
+{
+  DBusError read_error = DBUS_ERROR_INIT;
+
+  if (_dbus_read_uuid_file_without_creating (filename, uuid, &read_error))
+    return TRUE;
+
+  if (!create_if_not_found)
+    {
+      dbus_move_error (&read_error, error);
+      return FALSE;
+    }
+
+  /* If the file exists and contains junk, we want to keep that error
+   * message instead of overwriting it with a "file exists" error
+   * message when we try to write
+   */
+  if (dbus_error_has_name (&read_error, DBUS_ERROR_INVALID_FILE_CONTENT))
+    {
+      dbus_move_error (&read_error, error);
+      return FALSE;
+    }
+  else
+    {
+      dbus_error_free (&read_error);
+      return _dbus_create_uuid_file_exclusively (filename, uuid, error);
+    }
+}
+
+_DBUS_DEFINE_GLOBAL_LOCK (machine_uuid);
+static int machine_uuid_initialized_generation = 0;
+static DBusGUID machine_uuid;
+
+/**
+ * Gets the hex-encoded UUID of the machine this function is
+ * executed on. This UUID is guaranteed to be the same for a given
+ * machine at least until it next reboots, though it also
+ * makes some effort to be the same forever, it may change if the
+ * machine is reconfigured or its hardware is modified.
+ * 
+ * @param uuid_str string to append hex-encoded machine uuid to
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_get_local_machine_uuid_encoded (DBusString *uuid_str)
+{
+  dbus_bool_t ok;
+  
+  _DBUS_LOCK (machine_uuid);
+  if (machine_uuid_initialized_generation != _dbus_current_generation)
+    {
+      DBusError error = DBUS_ERROR_INIT;
+
+      if (!_dbus_read_local_machine_uuid (&machine_uuid, FALSE,
+                                          &error))
+        {          
+#ifndef DBUS_BUILD_TESTS
+          /* For the test suite, we may not be installed so just continue silently
+           * here. But in a production build, we want to be nice and loud about
+           * this.
+           */
+          _dbus_warn_check_failed ("D-Bus library appears to be incorrectly set up; failed to read machine uuid: %s\n"
+                                   "See the manual page for dbus-uuidgen to correct this issue.\n",
+                                   error.message);
+#endif
+          
+          dbus_error_free (&error);
+          
+          _dbus_generate_uuid (&machine_uuid);
+        }
+    }
+
+  ok = _dbus_uuid_encode (&machine_uuid, uuid_str);
+
+  _DBUS_UNLOCK (machine_uuid);
+
+  return ok;
+}
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Returns a string describing the given name.
+ *
+ * @param header_field the field to describe
+ * @returns a constant string describing the field
+ */
+const char *
+_dbus_header_field_to_string (int header_field)
+{
+  switch (header_field)
+    {
+    case DBUS_HEADER_FIELD_INVALID:
+      return "invalid";
+    case DBUS_HEADER_FIELD_PATH:
+      return "path";
+    case DBUS_HEADER_FIELD_INTERFACE:
+      return "interface";
+    case DBUS_HEADER_FIELD_MEMBER:
+      return "member";
+    case DBUS_HEADER_FIELD_ERROR_NAME:
+      return "error-name";
+    case DBUS_HEADER_FIELD_REPLY_SERIAL:
+      return "reply-serial";
+    case DBUS_HEADER_FIELD_DESTINATION:
+      return "destination";
+    case DBUS_HEADER_FIELD_SENDER:
+      return "sender";
+    case DBUS_HEADER_FIELD_SIGNATURE:
+      return "signature";
+    default:
+      return "unknown";
+    }
+}
+#endif /* DBUS_BUILD_TESTS */
+
+#ifndef DBUS_DISABLE_CHECKS
+/** String used in _dbus_return_if_fail macro */
+const char _dbus_return_if_fail_warning_format[] =
+"arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n"
+"This is normally a bug in some application using the D-Bus library.\n";
+#endif
+
+#ifndef DBUS_DISABLE_ASSERT
+/**
+ * Internals of _dbus_assert(); it's a function
+ * rather than a macro with the inline code so
+ * that the assertion failure blocks don't show up
+ * in test suite coverage, and to shrink code size.
+ *
+ * @param condition TRUE if assertion succeeded
+ * @param condition_text condition as a string
+ * @param file file the assertion is in
+ * @param line line the assertion is in
+ * @param func function the assertion is in
+ */
+void
+_dbus_real_assert (dbus_bool_t  condition,
+                   const char  *condition_text,
+                   const char  *file,
+                   int          line,
+                   const char  *func)
+{
+  if (_DBUS_UNLIKELY (!condition))
+    {
+      _dbus_warn ("%lu: assertion failed \"%s\" file \"%s\" line %d function %s\n",
+                  _dbus_pid_for_log (), condition_text, file, line, func);
+      _dbus_abort ();
+    }
+}
+
+/**
+ * Internals of _dbus_assert_not_reached(); it's a function
+ * rather than a macro with the inline code so
+ * that the assertion failure blocks don't show up
+ * in test suite coverage, and to shrink code size.
+ *
+ * @param explanation what was reached that shouldn't have been
+ * @param file file the assertion is in
+ * @param line line the assertion is in
+ */
+void
+_dbus_real_assert_not_reached (const char *explanation,
+                               const char *file,
+                               int         line)
+{
+  _dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n",
+              file, line, _dbus_pid_for_log (), explanation);
+  _dbus_abort ();
+}
+#endif /* DBUS_DISABLE_ASSERT */
+  
+#ifdef DBUS_BUILD_TESTS
+static dbus_bool_t
+run_failing_each_malloc (int                    n_mallocs,
+                         const char            *description,
+                         DBusTestMemoryFunction func,
+                         void                  *data)
+{
+  n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */
+  
+  while (n_mallocs >= 0)
+    {      
+      _dbus_set_fail_alloc_counter (n_mallocs);
+
+      _dbus_verbose ("\n===\n%s: (will fail malloc %d with %d failures)\n===\n",
+                     description, n_mallocs,
+                     _dbus_get_fail_alloc_failures ());
+
+      if (!(* func) (data))
+        return FALSE;
+      
+      n_mallocs -= 1;
+    }
+
+  _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
+
+  return TRUE;
+}                        
+
+/**
+ * Tests how well the given function responds to out-of-memory
+ * situations. Calls the function repeatedly, failing a different
+ * call to malloc() each time. If the function ever returns #FALSE,
+ * the test fails. The function should return #TRUE whenever something
+ * valid (such as returning an error, or succeeding) occurs, and #FALSE
+ * if it gets confused in some way.
+ *
+ * @param description description of the test used in verbose output
+ * @param func function to call
+ * @param data data to pass to function
+ * @returns #TRUE if the function never returns FALSE
+ */
+dbus_bool_t
+_dbus_test_oom_handling (const char             *description,
+                         DBusTestMemoryFunction  func,
+                         void                   *data)
+{
+  int approx_mallocs;
+  const char *setting;
+  int max_failures_to_try;
+  int i;
+
+  /* Run once to see about how many mallocs are involved */
+  
+  _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
+
+  _dbus_verbose ("Running once to count mallocs\n");
+  
+  if (!(* func) (data))
+    return FALSE;
+  
+  approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();
+
+  _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n",
+                 description, approx_mallocs);
+
+  setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES");
+  if (setting != NULL)
+    {
+      DBusString str;
+      long v;
+      _dbus_string_init_const (&str, setting);
+      v = 4;
+      if (!_dbus_string_parse_int (&str, 0, &v, NULL))
+        _dbus_warn ("couldn't parse '%s' as integer\n", setting);
+      max_failures_to_try = v;
+    }
+  else
+    {
+      max_failures_to_try = 4;
+    }
+
+  i = setting ? max_failures_to_try - 1 : 1;
+  while (i < max_failures_to_try)
+    {
+      _dbus_set_fail_alloc_failures (i);
+      if (!run_failing_each_malloc (approx_mallocs, description, func, data))
+        return FALSE;
+      ++i;
+    }
+  
+  _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n",
+                 description);
+
+  return TRUE;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/** @} */
diff --git a/src/dbus/dbus-internals.h b/src/dbus/dbus-internals.h
new file mode 100644 (file)
index 0000000..3e5f989
--- /dev/null
@@ -0,0 +1,346 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-internals.h  random utility stuff (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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
+ *
+ */
+#ifdef DBUS_INSIDE_DBUS_H
+#error "You can't include dbus-internals.h in the public header dbus.h"
+#endif
+
+#ifndef DBUS_INTERNALS_H
+#define DBUS_INTERNALS_H
+
+#include <config.h>
+
+#include <dbus/dbus-memory.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-sysdeps.h>
+#include <dbus/dbus-threads-internal.h>
+
+DBUS_BEGIN_DECLS
+
+#define DBUS_SESSION_BUS_DEFAULT_ADDRESS       "autolaunch:"
+
+void _dbus_warn               (const char *format,
+                               ...) _DBUS_GNUC_PRINTF (1, 2);
+
+void _dbus_warn_check_failed  (const char *format,
+                               ...) _DBUS_GNUC_PRINTF (1, 2);
+
+
+#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+#define _DBUS_FUNCTION_NAME __func__
+#elif defined(__GNUC__) || defined(_MSC_VER)
+#define _DBUS_FUNCTION_NAME __FUNCTION__
+#else
+#define _DBUS_FUNCTION_NAME "unknown function"
+#endif
+
+/*
+ * (code from GLib)
+ * 
+ * The _DBUS_LIKELY and _DBUS_UNLIKELY macros let the programmer give hints to 
+ * the compiler about the expected result of an expression. Some compilers
+ * can use this information for optimizations.
+ *
+ * The _DBUS_BOOLEAN_EXPR macro is intended to trigger a gcc warning when
+ * putting assignments in the macro arg
+ */
+#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
+#define _DBUS_BOOLEAN_EXPR(expr)                \
+ __extension__ ({                               \
+   int _dbus_boolean_var_;                      \
+   if (expr)                                    \
+      _dbus_boolean_var_ = 1;                   \
+   else                                         \
+      _dbus_boolean_var_ = 0;                   \
+   _dbus_boolean_var_;                          \
+})
+#define _DBUS_LIKELY(expr) (__builtin_expect (_DBUS_BOOLEAN_EXPR(expr), 1))
+#define _DBUS_UNLIKELY(expr) (__builtin_expect (_DBUS_BOOLEAN_EXPR(expr), 0))
+#else
+#define _DBUS_LIKELY(expr) (expr)
+#define _DBUS_UNLIKELY(expr) (expr)
+#endif
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+
+void _dbus_verbose_real       (const char *format,
+                               ...) _DBUS_GNUC_PRINTF (1, 2);
+void _dbus_verbose_reset_real (void);
+dbus_bool_t _dbus_is_verbose_real (void);
+
+#  define _dbus_verbose _dbus_verbose_real
+#  define _dbus_verbose_reset _dbus_verbose_reset_real
+#  define _dbus_is_verbose _dbus_is_verbose_real
+#else
+#  ifdef HAVE_ISO_VARARGS
+#    define _dbus_verbose(...)
+#  elif defined (HAVE_GNUC_VARARGS)
+#    define _dbus_verbose(format...)
+#  else
+static void _dbus_verbose(const char * x,...) {;}
+#  endif
+#  define _dbus_verbose_reset()
+#  define _dbus_is_verbose() FALSE 
+#endif /* !DBUS_ENABLE_VERBOSE_MODE */
+
+const char* _dbus_strerror (int error_number);
+
+#ifdef DBUS_DISABLE_ASSERT
+#define _dbus_assert(condition)
+#else
+void _dbus_real_assert (dbus_bool_t  condition,
+                        const char  *condition_text,
+                        const char  *file,
+                        int          line,
+                        const char  *func);
+#define _dbus_assert(condition)                                         \
+  _dbus_real_assert ((condition) != 0, #condition, __FILE__, __LINE__, _DBUS_FUNCTION_NAME)
+#endif /* !DBUS_DISABLE_ASSERT */
+
+#ifdef DBUS_DISABLE_ASSERT
+#define _dbus_assert_not_reached(explanation)
+#else
+void _dbus_real_assert_not_reached (const char *explanation,
+                                    const char *file,
+                                    int         line) _DBUS_GNUC_NORETURN;
+#define _dbus_assert_not_reached(explanation)                                   \
+  _dbus_real_assert_not_reached (explanation, __FILE__, __LINE__)
+#endif /* !DBUS_DISABLE_ASSERT */
+
+#ifdef DBUS_DISABLE_CHECKS
+#define _dbus_return_if_fail(condition)
+#define _dbus_return_val_if_fail(condition, val)
+#else
+extern const char _dbus_return_if_fail_warning_format[];
+
+#define _dbus_return_if_fail(condition) do {                                       \
+   _dbus_assert ((*(const char*)_DBUS_FUNCTION_NAME) != '_');                      \
+  if (!(condition)) {                                                              \
+    _dbus_warn_check_failed (_dbus_return_if_fail_warning_format,                  \
+                             _DBUS_FUNCTION_NAME, #condition, __FILE__, __LINE__); \
+    return;                                                                        \
+  } } while (0)
+
+#define _dbus_return_val_if_fail(condition, val) do {                                   \
+   _dbus_assert ((*(const char*)_DBUS_FUNCTION_NAME) != '_');                           \
+  if (!(condition)) {                                                                   \
+    _dbus_warn_check_failed (_dbus_return_if_fail_warning_format,                       \
+                             _DBUS_FUNCTION_NAME, #condition, __FILE__, __LINE__);      \
+    return (val);                                                                       \
+  } } while (0)
+
+#endif /* !DBUS_DISABLE_ASSERT */
+
+#define _DBUS_N_ELEMENTS(array) ((int) (sizeof ((array)) / sizeof ((array)[0])))
+
+#define _DBUS_POINTER_TO_INT(pointer) ((long)(pointer))
+#define _DBUS_INT_TO_POINTER(integer) ((void*)((long)(integer)))
+
+#define _DBUS_ZERO(object) (memset (&(object), '\0', sizeof ((object))))
+
+#define _DBUS_STRUCT_OFFSET(struct_type, member)       \
+    ((long) ((unsigned char*) &((struct_type*) 0)->member))
+
+#ifdef DBUS_DISABLE_CHECKS
+/* this is an assert and not an error, but in the typical --disable-checks case (you're trying
+ * to really minimize code size), disabling these assertions makes sense.
+ */
+#define _DBUS_ASSERT_ERROR_IS_SET(error)
+#define _DBUS_ASSERT_ERROR_IS_CLEAR(error)
+#else
+#define _DBUS_ASSERT_ERROR_IS_SET(error)   _dbus_assert ((error) == NULL || dbus_error_is_set ((error)))
+#define _DBUS_ASSERT_ERROR_IS_CLEAR(error) _dbus_assert ((error) == NULL || !dbus_error_is_set ((error)))
+#endif
+
+#define _dbus_return_if_error_is_set(error) _dbus_return_if_fail ((error) == NULL || !dbus_error_is_set ((error)))
+#define _dbus_return_val_if_error_is_set(error, val) _dbus_return_val_if_fail ((error) == NULL || !dbus_error_is_set ((error)), (val))
+
+/* This alignment thing is from ORBit2 */
+/* Align a value upward to a boundary, expressed as a number of bytes.
+ * E.g. align to an 8-byte boundary with argument of 8.
+ */
+
+/*
+ *   (this + boundary - 1)
+ *          &
+ *    ~(boundary - 1)
+ */
+
+#define _DBUS_ALIGN_VALUE(this, boundary) \
+  (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
+
+#define _DBUS_ALIGN_ADDRESS(this, boundary) \
+  ((void*)_DBUS_ALIGN_VALUE(this, boundary))
+
+
+char*       _dbus_strdup                (const char  *str);
+void*       _dbus_memdup                (const void  *mem,
+                                         size_t       n_bytes);
+dbus_bool_t _dbus_string_array_contains (const char **array,
+                                         const char  *str);
+char**      _dbus_dup_string_array      (const char **array);
+
+#define _DBUS_INT16_MIN         ((dbus_int16_t) 0x8000)
+#define _DBUS_INT16_MAX         ((dbus_int16_t) 0x7fff)
+#define _DBUS_UINT16_MAX ((dbus_uint16_t)0xffff)
+#define _DBUS_INT32_MIN         ((dbus_int32_t) 0x80000000)
+#define _DBUS_INT32_MAX         ((dbus_int32_t) 0x7fffffff)
+#define _DBUS_UINT32_MAX ((dbus_uint32_t)0xffffffff)
+/* using 32-bit here is sort of bogus */
+#define _DBUS_INT_MIN   _DBUS_INT32_MIN
+#define _DBUS_INT_MAX   _DBUS_INT32_MAX
+#define _DBUS_UINT_MAX  _DBUS_UINT32_MAX
+#ifdef DBUS_HAVE_INT64
+#define _DBUS_INT64_MAX         DBUS_INT64_CONSTANT  (0x7fffffffffffffff)
+#define _DBUS_UINT64_MAX DBUS_UINT64_CONSTANT (0xffffffffffffffff)
+#endif
+#define _DBUS_ONE_KILOBYTE 1024
+#define _DBUS_ONE_MEGABYTE 1024 * _DBUS_ONE_KILOBYTE
+#define _DBUS_ONE_HOUR_IN_MILLISECONDS (1000 * 60 * 60)
+#define _DBUS_USEC_PER_SECOND          (1000000)
+
+#undef MAX
+#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
+
+#undef MIN
+#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
+
+#undef ABS
+#define ABS(a)    (((a) < 0) ? -(a) : (a))
+
+#define _DBUS_ISASCII(c) ((c) != '\0' && (((c) & ~0x7f) == 0))
+
+typedef void (* DBusForeachFunction) (void *element,
+                                      void *data);
+
+dbus_bool_t _dbus_set_fd_nonblocking (int             fd,
+                                      DBusError      *error);
+
+void _dbus_verbose_bytes           (const unsigned char *data,
+                                    int                  len,
+                                    int                  offset);
+void _dbus_verbose_bytes_of_string (const DBusString    *str,
+                                    int                  start,
+                                    int                  len);
+
+const char* _dbus_header_field_to_string (int header_field);
+
+extern const char _dbus_no_memory_message[];
+#define _DBUS_SET_OOM(error) dbus_set_error_const ((error), DBUS_ERROR_NO_MEMORY, _dbus_no_memory_message)
+
+#ifdef DBUS_BUILD_TESTS
+/* Memory debugging */
+void        _dbus_set_fail_alloc_counter        (int  until_next_fail);
+int         _dbus_get_fail_alloc_counter        (void);
+void        _dbus_set_fail_alloc_failures       (int  failures_per_failure);
+int         _dbus_get_fail_alloc_failures       (void);
+dbus_bool_t _dbus_decrement_fail_alloc_counter  (void);
+dbus_bool_t _dbus_disable_mem_pools             (void);
+int         _dbus_get_malloc_blocks_outstanding (void);
+
+typedef dbus_bool_t (* DBusTestMemoryFunction)  (void *data);
+dbus_bool_t _dbus_test_oom_handling (const char             *description,
+                                     DBusTestMemoryFunction  func,
+                                     void                   *data);
+#else
+#define _dbus_set_fail_alloc_counter(n)
+#define _dbus_get_fail_alloc_counter _DBUS_INT_MAX
+
+/* These are constant expressions so that blocks
+ * they protect should be optimized away
+ */
+#define _dbus_decrement_fail_alloc_counter() (FALSE)
+#define _dbus_disable_mem_pools()            (FALSE)
+#define _dbus_get_malloc_blocks_outstanding  (0)
+#endif /* !DBUS_BUILD_TESTS */
+
+typedef void (* DBusShutdownFunction) (void *data);
+dbus_bool_t _dbus_register_shutdown_func (DBusShutdownFunction  function,
+                                          void                 *data);
+
+extern int _dbus_current_generation;
+
+/* Thread initializers */
+#define _DBUS_LOCK_NAME(name)           _dbus_lock_##name
+#define _DBUS_DECLARE_GLOBAL_LOCK(name) extern DBusMutex  *_dbus_lock_##name
+#define _DBUS_DEFINE_GLOBAL_LOCK(name)  DBusMutex         *_dbus_lock_##name  
+#define _DBUS_LOCK(name)                _dbus_mutex_lock   (_dbus_lock_##name)
+#define _DBUS_UNLOCK(name)              _dbus_mutex_unlock (_dbus_lock_##name)
+
+/* 1-5 */
+_DBUS_DECLARE_GLOBAL_LOCK (list);
+_DBUS_DECLARE_GLOBAL_LOCK (connection_slots);
+_DBUS_DECLARE_GLOBAL_LOCK (pending_call_slots);
+_DBUS_DECLARE_GLOBAL_LOCK (server_slots);
+_DBUS_DECLARE_GLOBAL_LOCK (message_slots);
+/* 5-10 */
+_DBUS_DECLARE_GLOBAL_LOCK (atomic);
+_DBUS_DECLARE_GLOBAL_LOCK (bus);
+_DBUS_DECLARE_GLOBAL_LOCK (bus_datas);
+_DBUS_DECLARE_GLOBAL_LOCK (shutdown_funcs);
+_DBUS_DECLARE_GLOBAL_LOCK (system_users);
+/* 10-15 */
+_DBUS_DECLARE_GLOBAL_LOCK (message_cache);
+_DBUS_DECLARE_GLOBAL_LOCK (shared_connections);
+_DBUS_DECLARE_GLOBAL_LOCK (win_fds);
+_DBUS_DECLARE_GLOBAL_LOCK (sid_atom_cache);
+_DBUS_DECLARE_GLOBAL_LOCK (machine_uuid);
+#define _DBUS_N_GLOBAL_LOCKS (15)
+
+dbus_bool_t _dbus_threads_init_debug (void);
+
+dbus_bool_t   _dbus_address_append_escaped (DBusString       *escaped,
+                                            const DBusString *unescaped);
+
+void          _dbus_set_bad_address        (DBusError         *error,
+                                            const char        *address_problem_type,
+                                            const char        *address_problem_field,
+                                            const char        *address_problem_other);
+
+#define DBUS_UUID_LENGTH_BYTES 16
+#define DBUS_UUID_LENGTH_WORDS (DBUS_UUID_LENGTH_BYTES / 4)
+#define DBUS_UUID_LENGTH_HEX   (DBUS_UUID_LENGTH_BYTES * 2)
+
+/**
+ * A globally unique ID ; we have one for each DBusServer, and also one for each
+ * machine with libdbus installed on it.
+ */
+union DBusGUID
+{
+  dbus_uint32_t as_uint32s[DBUS_UUID_LENGTH_WORDS];     /**< guid as four uint32 values */
+  char as_bytes[DBUS_UUID_LENGTH_BYTES];                /**< guid as 16 single-byte values */
+};
+
+void        _dbus_generate_uuid  (DBusGUID         *uuid);
+dbus_bool_t _dbus_uuid_encode    (const DBusGUID   *uuid,
+                                  DBusString       *encoded);
+dbus_bool_t _dbus_read_uuid_file (const DBusString *filename,
+                                  DBusGUID         *uuid,
+                                  dbus_bool_t       create_if_not_found,
+                                  DBusError        *error);
+
+dbus_bool_t _dbus_get_local_machine_uuid_encoded (DBusString *uuid_str);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_INTERNALS_H */
diff --git a/src/dbus/dbus-keyring.c b/src/dbus/dbus-keyring.c
new file mode 100644 (file)
index 0000000..8cc4048
--- /dev/null
@@ -0,0 +1,1154 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-keyring.c Store secret cookies in your homedir
+ *
+ * Copyright (C) 2003, 2004  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-keyring.h"
+#include "dbus-protocol.h"
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-sysdeps.h>
+
+/**
+ * @defgroup DBusKeyring keyring class
+ * @ingroup  DBusInternals
+ * @brief DBusKeyring data structure
+ *
+ * Types and functions related to DBusKeyring. DBusKeyring is intended
+ * to manage cookies used to authenticate clients to servers.  This is
+ * essentially the "verify that client can read the user's homedir"
+ * authentication mechanism.  Both client and server must have access
+ * to the homedir.
+ *
+ * The secret keys are not kept in locked memory, and are written to a
+ * file in the user's homedir. However they are transient (only used
+ * by a single server instance for a fixed period of time, then
+ * discarded). Also, the keys are not sent over the wire.
+ *
+ * @todo there's a memory leak on some codepath in here, I saw it once
+ * when running make check - probably some specific initial cookies
+ * present in the cookie file, then depending on what we do with them.
+ */
+
+/**
+ * @defgroup DBusKeyringInternals DBusKeyring implementation details
+ * @ingroup  DBusInternals
+ * @brief DBusKeyring implementation details
+ *
+ * The guts of DBusKeyring.
+ *
+ * @{
+ */
+
+/** The maximum age of a key before we create a new key to use in
+ * challenges.  This isn't super-reliably enforced, since system
+ * clocks can change or be wrong, but we make a best effort to only
+ * use keys for a short time.
+ */
+#define NEW_KEY_TIMEOUT_SECONDS     (60*5)
+/**
+ * The time after which we drop a key from the secrets file.
+ * The EXPIRE_KEYS_TIMEOUT_SECONDS - NEW_KEY_TIMEOUT_SECONDS is the minimum
+ * time window a client has to complete authentication.
+ */
+#define EXPIRE_KEYS_TIMEOUT_SECONDS (NEW_KEY_TIMEOUT_SECONDS + (60*2))
+/**
+ * The maximum amount of time a key can be in the future.
+ */
+#define MAX_TIME_TRAVEL_SECONDS (60*5)
+
+/**
+ * Maximum number of keys in the keyring before
+ * we just ignore the rest
+ */
+#ifdef DBUS_BUILD_TESTS
+#define MAX_KEYS_IN_FILE 10
+#else
+#define MAX_KEYS_IN_FILE 256
+#endif
+
+/**
+ * A single key from the cookie file
+ */
+typedef struct
+{
+  dbus_int32_t id; /**< identifier used to refer to the key */
+
+  long creation_time; /**< when the key was generated,
+                       *   as unix timestamp. signed long
+                       *   matches struct timeval.
+                       */
+  
+  DBusString secret; /**< the actual key */
+
+} DBusKey;
+
+/**
+ * @brief Internals of DBusKeyring.
+ * 
+ * DBusKeyring internals. DBusKeyring is an opaque object, it must be
+ * used via accessor functions.
+ */
+struct DBusKeyring
+{
+  int refcount;             /**< Reference count */
+  DBusString directory;     /**< Directory the below two items are inside */
+  DBusString filename;      /**< Keyring filename */
+  DBusString filename_lock; /**< Name of lockfile */
+  DBusKey *keys; /**< Keys loaded from the file */
+  int n_keys;    /**< Number of keys */
+  DBusCredentials *credentials; /**< Credentials containing user the keyring is for */
+};
+
+static DBusKeyring*
+_dbus_keyring_new (void)
+{
+  DBusKeyring *keyring;
+
+  keyring = dbus_new0 (DBusKeyring, 1);
+  if (keyring == NULL)
+    goto out_0;
+  
+  if (!_dbus_string_init (&keyring->directory))
+    goto out_1;
+
+  if (!_dbus_string_init (&keyring->filename))
+    goto out_2;
+
+  if (!_dbus_string_init (&keyring->filename_lock))
+    goto out_3;
+  
+  keyring->refcount = 1;
+  keyring->keys = NULL;
+  keyring->n_keys = 0;
+
+  return keyring;
+
+  /*  out_4: */
+  _dbus_string_free (&keyring->filename_lock);
+ out_3:
+  _dbus_string_free (&keyring->filename);
+ out_2:
+  _dbus_string_free (&keyring->directory);
+ out_1:
+  dbus_free (keyring);
+ out_0:
+  return NULL;
+}
+
+static void
+free_keys (DBusKey *keys,
+           int      n_keys)
+{
+  int i;
+
+  /* should be safe for args NULL, 0 */
+  
+  i = 0;
+  while (i < n_keys)
+    {
+      _dbus_string_free (&keys[i].secret);
+      ++i;
+    }
+
+  dbus_free (keys);
+}
+
+/* Our locking scheme is highly unreliable.  However, there is
+ * unfortunately no reliable locking scheme in user home directories;
+ * between bugs in Linux NFS, people using Tru64 or other total crap
+ * NFS, AFS, random-file-system-of-the-week, and so forth, fcntl() in
+ * homedirs simply generates tons of bug reports. This has been
+ * learned through hard experience with GConf, unfortunately.
+ *
+ * This bad hack might work better for the kind of lock we have here,
+ * which we don't expect to hold for any length of time.  Crashing
+ * while we hold it should be unlikely, and timing out such that we
+ * delete a stale lock should also be unlikely except when the
+ * filesystem is running really slowly.  Stuff might break in corner
+ * cases but as long as it's not a security-level breakage it should
+ * be OK.
+ */
+
+/** Maximum number of timeouts waiting for lock before we decide it's stale */
+#define MAX_LOCK_TIMEOUTS 32
+/** Length of each timeout while waiting for a lock */
+#define LOCK_TIMEOUT_MILLISECONDS 250
+
+static dbus_bool_t
+_dbus_keyring_lock (DBusKeyring *keyring)
+{
+  int n_timeouts;
+  
+  n_timeouts = 0;
+  while (n_timeouts < MAX_LOCK_TIMEOUTS)
+    {
+      DBusError error = DBUS_ERROR_INIT;
+
+      if (_dbus_create_file_exclusively (&keyring->filename_lock,
+                                         &error))
+        break;
+
+      _dbus_verbose ("Did not get lock file, sleeping %d milliseconds (%s)\n",
+                     LOCK_TIMEOUT_MILLISECONDS, error.message);
+      dbus_error_free (&error);
+
+      _dbus_sleep_milliseconds (LOCK_TIMEOUT_MILLISECONDS);
+      
+      ++n_timeouts;
+    }
+
+  if (n_timeouts == MAX_LOCK_TIMEOUTS)
+    {
+      DBusError error = DBUS_ERROR_INIT;
+
+      _dbus_verbose ("Lock file timed out %d times, assuming stale\n",
+                     n_timeouts);
+
+      if (!_dbus_delete_file (&keyring->filename_lock, &error))
+        {
+          _dbus_verbose ("Couldn't delete old lock file: %s\n",
+                         error.message);
+          dbus_error_free (&error);
+          return FALSE;
+        }
+
+      if (!_dbus_create_file_exclusively (&keyring->filename_lock,
+                                          &error))
+        {
+          _dbus_verbose ("Couldn't create lock file after deleting stale one: %s\n",
+                         error.message);
+          dbus_error_free (&error);
+          return FALSE;
+        }
+    }
+  
+  return TRUE;
+}
+
+static void
+_dbus_keyring_unlock (DBusKeyring *keyring)
+{
+  DBusError error = DBUS_ERROR_INIT;
+
+  if (!_dbus_delete_file (&keyring->filename_lock, &error))
+    {
+      _dbus_warn ("Failed to delete lock file: %s\n",
+                  error.message);
+      dbus_error_free (&error);
+    }
+}
+
+static DBusKey*
+find_key_by_id (DBusKey *keys,
+                int      n_keys,
+                int      id)
+{
+  int i;
+
+  i = 0;
+  while (i < n_keys)
+    {
+      if (keys[i].id == id)
+        return &keys[i];
+      
+      ++i;
+    }
+
+  return NULL;
+}
+
+static dbus_bool_t
+add_new_key (DBusKey  **keys_p,
+             int       *n_keys_p,
+             DBusError *error)
+{
+  DBusKey *new;
+  DBusString bytes;
+  int id;
+  long timestamp;
+  const unsigned char *s;
+  dbus_bool_t retval;
+  DBusKey *keys;
+  int n_keys;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  if (!_dbus_string_init (&bytes))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      return FALSE;
+    }
+
+  keys = *keys_p;
+  n_keys = *n_keys_p;
+  retval = FALSE;
+      
+  /* Generate an integer ID and then the actual key. */
+ retry:
+      
+  if (!_dbus_generate_random_bytes (&bytes, 4))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto out;
+    }
+
+  s = (const unsigned char*) _dbus_string_get_const_data (&bytes);
+      
+  id = s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24);
+  if (id < 0)
+    id = - id;
+  _dbus_assert (id >= 0);
+
+  if (find_key_by_id (keys, n_keys, id) != NULL)
+    {
+      _dbus_string_set_length (&bytes, 0);
+      _dbus_verbose ("Key ID %d already existed, trying another one\n",
+                     id);
+      goto retry;
+    }
+
+  _dbus_verbose ("Creating key with ID %d\n", id);
+      
+#define KEY_LENGTH_BYTES 24
+  _dbus_string_set_length (&bytes, 0);
+  if (!_dbus_generate_random_bytes (&bytes, KEY_LENGTH_BYTES))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto out;
+    }
+
+  new = dbus_realloc (keys, sizeof (DBusKey) * (n_keys + 1));
+  if (new == NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto out;
+    }
+
+  keys = new;
+  *keys_p = keys; /* otherwise *keys_p ends up invalid */
+  n_keys += 1;
+
+  if (!_dbus_string_init (&keys[n_keys-1].secret))
+    {
+      n_keys -= 1; /* we don't want to free the one we didn't init */
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto out;
+    }
+
+  _dbus_get_current_time (&timestamp, NULL);
+      
+  keys[n_keys-1].id = id;
+  keys[n_keys-1].creation_time = timestamp;
+  if (!_dbus_string_move (&bytes, 0,
+                          &keys[n_keys-1].secret,
+                          0))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      _dbus_string_free (&keys[n_keys-1].secret);
+      n_keys -= 1;
+      goto out;
+    }
+  
+  retval = TRUE;
+  
+ out:
+  *n_keys_p = n_keys;
+  
+  _dbus_string_free (&bytes);
+  return retval;
+}
+
+/**
+ * Reloads the keyring file, optionally adds one new key to the file,
+ * removes all expired keys from the file iff a key was added, then
+ * resaves the file.  Stores the keys from the file in keyring->keys.
+ * Note that the file is only resaved (written to) if a key is added,
+ * this means that only servers ever write to the file and need to
+ * lock it, which avoids a lot of lock contention at login time and
+ * such.
+ *
+ * @param keyring the keyring
+ * @param add_new #TRUE to add a new key to the file, expire keys, and resave
+ * @param error return location for errors
+ * @returns #FALSE on failure
+ */
+static dbus_bool_t
+_dbus_keyring_reload (DBusKeyring *keyring,
+                      dbus_bool_t  add_new,
+                      DBusError   *error)
+{
+  DBusString contents;
+  DBusString line;
+  dbus_bool_t retval;
+  dbus_bool_t have_lock;
+  DBusKey *keys;
+  int n_keys;
+  int i;
+  long now;
+  DBusError tmp_error;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  if (!_dbus_check_dir_is_private_to_user (&keyring->directory, error))
+    return FALSE;
+    
+  if (!_dbus_string_init (&contents))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      return FALSE;
+    }
+
+  if (!_dbus_string_init (&line))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      _dbus_string_free (&contents);
+      return FALSE;
+    }
+   
+  keys = NULL;
+  n_keys = 0;
+  retval = FALSE;
+  have_lock = FALSE;
+
+  _dbus_get_current_time (&now, NULL);
+  
+  if (add_new)
+    {
+      if (!_dbus_keyring_lock (keyring))
+        {
+          dbus_set_error (error, DBUS_ERROR_FAILED,
+                          "Could not lock keyring file to add to it");
+          goto out;
+        }
+
+      have_lock = TRUE;
+    }
+
+  dbus_error_init (&tmp_error);
+  if (!_dbus_file_get_contents (&contents, 
+                                &keyring->filename,
+                                &tmp_error))
+    {
+      _dbus_verbose ("Failed to load keyring file: %s\n",
+                     tmp_error.message);
+      /* continue with empty keyring file, so we recreate it */
+      dbus_error_free (&tmp_error);
+    }
+
+  if (!_dbus_string_validate_ascii (&contents, 0,
+                                    _dbus_string_get_length (&contents)))
+    {
+      _dbus_warn ("Secret keyring file contains non-ASCII! Ignoring existing contents\n");
+      _dbus_string_set_length (&contents, 0);
+    }
+
+  /* FIXME this is badly inefficient for large keyring files
+   * (not that large keyring files exist outside of test suites)
+   */
+  while (_dbus_string_pop_line (&contents, &line))
+    {
+      int next;
+      long val;
+      int id;
+      long timestamp;
+      int len;
+      int end;
+      DBusKey *new;
+
+      /* Don't load more than the max. */
+      if (n_keys >= (add_new ? MAX_KEYS_IN_FILE - 1 : MAX_KEYS_IN_FILE))
+        break;
+      
+      next = 0;
+      if (!_dbus_string_parse_int (&line, 0, &val, &next))
+        {
+          _dbus_verbose ("could not parse secret key ID at start of line\n");
+          continue;
+        }
+
+      if (val > _DBUS_INT32_MAX || val < 0)
+        {
+          _dbus_verbose ("invalid secret key ID at start of line\n");
+          continue;
+        }
+      
+      id = val;
+
+      _dbus_string_skip_blank (&line, next, &next);
+      
+      if (!_dbus_string_parse_int (&line, next, &timestamp, &next))
+        {
+          _dbus_verbose ("could not parse secret key timestamp\n");
+          continue;
+        }
+
+      if (timestamp < 0 ||
+          (now + MAX_TIME_TRAVEL_SECONDS) < timestamp ||
+          (now - EXPIRE_KEYS_TIMEOUT_SECONDS) > timestamp)
+        {
+          _dbus_verbose ("dropping/ignoring %ld-seconds old key with timestamp %ld as current time is %ld\n",
+                         now - timestamp, timestamp, now);
+          continue;
+        }
+      
+      _dbus_string_skip_blank (&line, next, &next);
+
+      len = _dbus_string_get_length (&line);
+
+      if ((len - next) == 0)
+        {
+          _dbus_verbose ("no secret key after ID and timestamp\n");
+          continue;
+        }
+      
+      /* We have all three parts */
+      new = dbus_realloc (keys, sizeof (DBusKey) * (n_keys + 1));
+      if (new == NULL)
+        {
+          dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+          goto out;
+        }
+
+      keys = new;
+      n_keys += 1;
+
+      if (!_dbus_string_init (&keys[n_keys-1].secret))
+        {
+          n_keys -= 1; /* we don't want to free the one we didn't init */
+          dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+          goto out;
+        }
+      
+      keys[n_keys-1].id = id;
+      keys[n_keys-1].creation_time = timestamp;
+      if (!_dbus_string_hex_decode (&line, next, &end,
+                                    &keys[n_keys-1].secret, 0))
+       {
+         dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+         goto out;
+       }
+
+      if (_dbus_string_get_length (&line) != end)
+       {
+         _dbus_verbose ("invalid hex encoding in keyring file\n");
+         _dbus_string_free (&keys[n_keys - 1].secret);
+         n_keys -= 1;
+         continue;
+       }
+    }
+
+  _dbus_verbose ("Successfully loaded %d existing keys\n",
+                 n_keys);
+
+  if (add_new)
+    {
+      if (!add_new_key (&keys, &n_keys, error))
+        {
+          _dbus_verbose ("Failed to generate new key: %s\n",
+                         error ? error->message : "(unknown)");
+          goto out;
+        }
+
+      _dbus_string_set_length (&contents, 0);
+
+      i = 0;
+      while (i < n_keys)
+        {
+          if (!_dbus_string_append_int (&contents,
+                                        keys[i].id))
+            goto nomem;
+
+          if (!_dbus_string_append_byte (&contents, ' '))
+            goto nomem;
+
+          if (!_dbus_string_append_int (&contents,
+                                        keys[i].creation_time))
+            goto nomem;
+
+          if (!_dbus_string_append_byte (&contents, ' '))
+            goto nomem;
+
+          if (!_dbus_string_hex_encode (&keys[i].secret, 0,
+                                        &contents,
+                                        _dbus_string_get_length (&contents)))
+            goto nomem;
+
+          if (!_dbus_string_append_byte (&contents, '\n'))
+            goto nomem;          
+          
+          ++i;
+          continue;
+
+        nomem:
+          dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+          goto out;
+        }
+      
+      if (!_dbus_string_save_to_file (&contents, &keyring->filename,
+                                      error))
+        goto out;
+    }
+
+  if (keyring->keys)
+    free_keys (keyring->keys, keyring->n_keys);
+  keyring->keys = keys;
+  keyring->n_keys = n_keys;
+  keys = NULL;
+  n_keys = 0;
+  
+  retval = TRUE;  
+  
+ out:
+  if (have_lock)
+    _dbus_keyring_unlock (keyring);
+  
+  if (! ((retval == TRUE && (error == NULL || error->name == NULL)) ||
+         (retval == FALSE && (error == NULL || error->name != NULL))))
+    {
+      if (error && error->name)
+        _dbus_verbose ("error is %s: %s\n", error->name, error->message);
+      _dbus_warn ("returning %d but error pointer %p name %s\n",
+                  retval, error, error->name ? error->name : "(none)");
+      _dbus_assert_not_reached ("didn't handle errors properly");
+    }
+  
+  if (keys != NULL)
+    {
+      i = 0;
+      while (i < n_keys)
+        {
+          _dbus_string_zero (&keys[i].secret);
+          _dbus_string_free (&keys[i].secret);
+          ++i;
+        }
+
+      dbus_free (keys);
+    }
+  
+  _dbus_string_free (&contents);
+  _dbus_string_free (&line);
+
+  return retval;
+}
+
+/** @} */ /* end of internals */
+
+/**
+ * @addtogroup DBusKeyring
+ *
+ * @{
+ */
+
+/**
+ * Increments reference count of the keyring
+ *
+ * @param keyring the keyring
+ * @returns the keyring
+ */
+DBusKeyring *
+_dbus_keyring_ref (DBusKeyring *keyring)
+{
+  keyring->refcount += 1;
+
+  return keyring;
+}
+
+/**
+ * Decrements refcount and finalizes if it reaches
+ * zero.
+ *
+ * @param keyring the keyring
+ */
+void
+_dbus_keyring_unref (DBusKeyring *keyring)
+{
+  keyring->refcount -= 1;
+
+  if (keyring->refcount == 0)
+    {
+      if (keyring->credentials)
+        _dbus_credentials_unref (keyring->credentials);
+
+      _dbus_string_free (&keyring->filename);
+      _dbus_string_free (&keyring->filename_lock);
+      _dbus_string_free (&keyring->directory);
+      free_keys (keyring->keys, keyring->n_keys);
+      dbus_free (keyring);      
+    }
+}
+
+/**
+ * Creates a new keyring that lives in the ~/.dbus-keyrings directory
+ * of the given user credentials. If the credentials are #NULL or
+ * empty, uses those of the current process.
+ *
+ * @param username username to get keyring for, or #NULL
+ * @param context which keyring to get
+ * @param error return location for errors
+ * @returns the keyring or #NULL on error
+ */
+DBusKeyring*
+_dbus_keyring_new_for_credentials (DBusCredentials  *credentials,
+                                   const DBusString *context,
+                                   DBusError        *error)
+{
+  DBusString ringdir;
+  DBusKeyring *keyring;
+  dbus_bool_t error_set;
+  DBusError tmp_error;
+  DBusCredentials *our_credentials;
+  
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  keyring = NULL;
+  error_set = FALSE;
+  our_credentials = NULL;
+  
+  if (!_dbus_string_init (&ringdir))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      return NULL;
+    }
+
+  if (credentials != NULL)
+    {
+      our_credentials = _dbus_credentials_copy (credentials);
+    }
+  else
+    {
+      our_credentials = _dbus_credentials_new_from_current_process ();
+    }
+  
+  if (our_credentials == NULL)
+    goto failed;
+
+  if (_dbus_credentials_are_anonymous (our_credentials))
+    {
+      if (!_dbus_credentials_add_from_current_process (our_credentials))
+        goto failed;
+    }
+  
+  if (!_dbus_append_keyring_directory_for_credentials (&ringdir,
+                                                       our_credentials))
+    goto failed;
+  
+  keyring = _dbus_keyring_new ();
+  if (keyring == NULL)
+    goto failed;
+
+  _dbus_assert (keyring->credentials == NULL);
+  keyring->credentials = our_credentials;
+  our_credentials = NULL; /* so we don't unref it again later */
+  
+  /* should have been validated already, but paranoia check here */
+  if (!_dbus_keyring_validate_context (context))
+    {
+      error_set = TRUE;
+      dbus_set_error_const (error,
+                            DBUS_ERROR_FAILED,
+                            "Invalid context in keyring creation");
+      goto failed;
+    }
+
+  /* Save keyring dir in the keyring object */
+  if (!_dbus_string_copy (&ringdir, 0,
+                          &keyring->directory, 0))
+    goto failed;  
+
+  /* Create keyring->filename based on keyring dir and context */
+  if (!_dbus_string_copy (&keyring->directory, 0,
+                          &keyring->filename, 0))
+    goto failed;
+
+  if (!_dbus_concat_dir_and_file (&keyring->filename,
+                                  context))
+    goto failed;
+
+  /* Create lockfile name */
+  if (!_dbus_string_copy (&keyring->filename, 0,
+                          &keyring->filename_lock, 0))
+    goto failed;
+
+  if (!_dbus_string_append (&keyring->filename_lock, ".lock"))
+    goto failed;
+
+  /* Reload keyring */
+  dbus_error_init (&tmp_error);
+  if (!_dbus_keyring_reload (keyring, FALSE, &tmp_error))
+    {
+      _dbus_verbose ("didn't load an existing keyring: %s\n",
+                     tmp_error.message);
+      dbus_error_free (&tmp_error);
+    }
+  
+  /* We don't fail fatally if we can't create the directory,
+   * but the keyring will probably always be empty
+   * unless someone else manages to create it
+   */
+  dbus_error_init (&tmp_error);
+  if (!_dbus_create_directory (&keyring->directory,
+                               &tmp_error))
+    {
+      _dbus_verbose ("Creating keyring directory: %s\n",
+                     tmp_error.message);
+      dbus_error_free (&tmp_error);
+    }
+
+  _dbus_string_free (&ringdir);
+  
+  return keyring;
+  
+ failed:
+  if (!error_set)
+    dbus_set_error_const (error,
+                          DBUS_ERROR_NO_MEMORY,
+                          NULL);
+  if (our_credentials)
+    _dbus_credentials_unref (our_credentials);
+  if (keyring)
+    _dbus_keyring_unref (keyring);
+  _dbus_string_free (&ringdir);
+  return NULL;
+
+}
+
+/**
+ * Checks whether the context is a valid context.
+ * Contexts that might cause confusion when used
+ * in filenames are not allowed (contexts can't
+ * start with a dot or contain dir separators).
+ *
+ * @todo this is the most inefficient implementation
+ * imaginable.
+ *
+ * @param context the context
+ * @returns #TRUE if valid
+ */
+dbus_bool_t
+_dbus_keyring_validate_context (const DBusString *context)
+{
+  if (_dbus_string_get_length (context) == 0)
+    {
+      _dbus_verbose ("context is zero-length\n");
+      return FALSE;
+    }
+
+  if (!_dbus_string_validate_ascii (context, 0,
+                                    _dbus_string_get_length (context)))
+    {
+      _dbus_verbose ("context not valid ascii\n");
+      return FALSE;
+    }
+  
+  /* no directory separators */  
+  if (_dbus_string_find (context, 0, "/", NULL))
+    {
+      _dbus_verbose ("context contains a slash\n");
+      return FALSE;
+    }
+
+  if (_dbus_string_find (context, 0, "\\", NULL))
+    {
+      _dbus_verbose ("context contains a backslash\n");
+      return FALSE;
+    }
+
+  /* prevent attempts to use dotfiles or ".." or ".lock"
+   * all of which might allow some kind of attack
+   */
+  if (_dbus_string_find (context, 0, ".", NULL))
+    {
+      _dbus_verbose ("context contains a dot\n");
+      return FALSE;
+    }
+
+  /* no spaces/tabs, those are used for separators in the protocol */
+  if (_dbus_string_find_blank (context, 0, NULL))
+    {
+      _dbus_verbose ("context contains a blank\n");
+      return FALSE;
+    }
+
+  if (_dbus_string_find (context, 0, "\n", NULL))
+    {
+      _dbus_verbose ("context contains a newline\n");
+      return FALSE;
+    }
+
+  if (_dbus_string_find (context, 0, "\r", NULL))
+    {
+      _dbus_verbose ("context contains a carriage return\n");
+      return FALSE;
+    }
+  
+  return TRUE;
+}
+
+static DBusKey*
+find_recent_key (DBusKeyring *keyring)
+{
+  int i;
+  long tv_sec, tv_usec;
+
+  _dbus_get_current_time (&tv_sec, &tv_usec);
+  
+  i = 0;
+  while (i < keyring->n_keys)
+    {
+      DBusKey *key = &keyring->keys[i];
+
+      _dbus_verbose ("Key %d is %ld seconds old\n",
+                     i, tv_sec - key->creation_time);
+      
+      if ((tv_sec - NEW_KEY_TIMEOUT_SECONDS) < key->creation_time)
+        return key;
+      
+      ++i;
+    }
+
+  return NULL;
+}
+
+/**
+ * Gets a recent key to use for authentication.
+ * If no recent key exists, creates one. Returns
+ * the key ID. If a key can't be written to the keyring
+ * file so no recent key can be created, returns -1.
+ * All valid keys are > 0.
+ *
+ * @param keyring the keyring
+ * @param error error on failure
+ * @returns key ID to use for auth, or -1 on failure
+ */
+int
+_dbus_keyring_get_best_key (DBusKeyring  *keyring,
+                            DBusError    *error)
+{
+  DBusKey *key;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  key = find_recent_key (keyring);
+  if (key)
+    return key->id;
+
+  /* All our keys are too old, or we've never loaded the
+   * keyring. Create a new one.
+   */
+  if (!_dbus_keyring_reload (keyring, TRUE,
+                             error))
+    return -1;
+
+  key = find_recent_key (keyring);
+  if (key)
+    return key->id;
+  else
+    {
+      dbus_set_error_const (error,
+                            DBUS_ERROR_FAILED,
+                            "No recent-enough key found in keyring, and unable to create a new key");
+      return -1;
+    }
+}
+
+/**
+ * Checks whether the keyring is for the same user as the given credentials.
+ *
+ * @param keyring the keyring
+ * @param credentials the credentials to check
+ *
+ * @returns #TRUE if the keyring belongs to the given user
+ */
+dbus_bool_t
+_dbus_keyring_is_for_credentials (DBusKeyring           *keyring,
+                                  DBusCredentials       *credentials)
+{
+  return _dbus_credentials_same_user (keyring->credentials,
+                                      credentials);
+}
+
+/**
+ * Gets the hex-encoded secret key for the given ID.
+ * Returns #FALSE if not enough memory. Returns #TRUE
+ * but empty key on any other error such as unknown
+ * key ID.
+ *
+ * @param keyring the keyring
+ * @param key_id the key ID
+ * @param hex_key string to append hex-encoded key to
+ * @returns #TRUE if we had enough memory
+ */
+dbus_bool_t
+_dbus_keyring_get_hex_key (DBusKeyring       *keyring,
+                           int                key_id,
+                           DBusString        *hex_key)
+{
+  DBusKey *key;
+
+  key = find_key_by_id (keyring->keys,
+                        keyring->n_keys,
+                        key_id);
+  if (key == NULL)
+    return TRUE; /* had enough memory, so TRUE */
+
+  return _dbus_string_hex_encode (&key->secret, 0,
+                                  hex_key,
+                                  _dbus_string_get_length (hex_key));
+}
+
+/** @} */ /* end of exposed API */
+
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-test.h"
+#include <stdio.h>
+
+dbus_bool_t
+_dbus_keyring_test (void)
+{
+  DBusString context;
+  DBusKeyring *ring1;
+  DBusKeyring *ring2;
+  int id;
+  DBusError error;
+  int i;
+
+  ring1 = NULL;
+  ring2 = NULL;
+  
+  /* Context validation */
+  
+  _dbus_string_init_const (&context, "foo");
+  _dbus_assert (_dbus_keyring_validate_context (&context));
+  _dbus_string_init_const (&context, "org_freedesktop_blah");
+  _dbus_assert (_dbus_keyring_validate_context (&context));
+  
+  _dbus_string_init_const (&context, "");
+  _dbus_assert (!_dbus_keyring_validate_context (&context));
+  _dbus_string_init_const (&context, ".foo");
+  _dbus_assert (!_dbus_keyring_validate_context (&context));
+  _dbus_string_init_const (&context, "bar.foo");
+  _dbus_assert (!_dbus_keyring_validate_context (&context));
+  _dbus_string_init_const (&context, "bar/foo");
+  _dbus_assert (!_dbus_keyring_validate_context (&context));
+  _dbus_string_init_const (&context, "bar\\foo");
+  _dbus_assert (!_dbus_keyring_validate_context (&context));
+  _dbus_string_init_const (&context, "foo\xfa\xf0");
+  _dbus_assert (!_dbus_keyring_validate_context (&context));
+  _dbus_string_init_const (&context, "foo\x80");
+  _dbus_assert (!_dbus_keyring_validate_context (&context));
+  _dbus_string_init_const (&context, "foo\x7f");
+  _dbus_assert (_dbus_keyring_validate_context (&context));
+  _dbus_string_init_const (&context, "foo bar");
+  _dbus_assert (!_dbus_keyring_validate_context (&context));
+  
+  if (!_dbus_string_init (&context))
+    _dbus_assert_not_reached ("no memory");
+  if (!_dbus_string_append_byte (&context, '\0'))
+    _dbus_assert_not_reached ("no memory");
+  _dbus_assert (!_dbus_keyring_validate_context (&context));
+  _dbus_string_free (&context);
+
+  /* Now verify that if we create a key in keyring 1,
+   * it is properly loaded in keyring 2
+   */
+
+  _dbus_string_init_const (&context, "org_freedesktop_dbus_testsuite");
+  dbus_error_init (&error);
+  ring1 = _dbus_keyring_new_for_credentials (NULL, &context,
+                                             &error);
+  _dbus_assert (ring1 != NULL);
+  _dbus_assert (error.name == NULL);
+
+  id = _dbus_keyring_get_best_key (ring1, &error);
+  if (id < 0)
+    {
+      fprintf (stderr, "Could not load keyring: %s\n", error.message);
+      dbus_error_free (&error);
+      goto failure;
+    }
+
+  ring2 = _dbus_keyring_new_for_credentials (NULL, &context, &error);
+  _dbus_assert (ring2 != NULL);
+  _dbus_assert (error.name == NULL);
+  
+  if (ring1->n_keys != ring2->n_keys)
+    {
+      fprintf (stderr, "Different number of keys in keyrings\n");
+      goto failure;
+    }
+
+  /* We guarantee we load and save keeping keys in a fixed
+   * order
+   */
+  i = 0;
+  while (i < ring1->n_keys)
+    {
+      if (ring1->keys[i].id != ring2->keys[i].id)
+        {
+          fprintf (stderr, "Keyring 1 has first key ID %d and keyring 2 has %d\n",
+                   ring1->keys[i].id, ring2->keys[i].id);
+          goto failure;
+        }      
+
+      if (ring1->keys[i].creation_time != ring2->keys[i].creation_time)
+        {
+          fprintf (stderr, "Keyring 1 has first key time %ld and keyring 2 has %ld\n",
+                   ring1->keys[i].creation_time, ring2->keys[i].creation_time);
+          goto failure;
+        }
+
+      if (!_dbus_string_equal (&ring1->keys[i].secret,
+                               &ring2->keys[i].secret))
+        {
+          fprintf (stderr, "Keyrings 1 and 2 have different secrets for same ID/timestamp\n");
+          goto failure;
+        }
+      
+      ++i;
+    }
+
+  printf (" %d keys in test\n", ring1->n_keys);
+
+  /* Test ref/unref */
+  _dbus_keyring_ref (ring1);
+  _dbus_keyring_ref (ring2);
+  _dbus_keyring_unref (ring1);
+  _dbus_keyring_unref (ring2);
+
+
+  /* really unref */
+  _dbus_keyring_unref (ring1);
+  _dbus_keyring_unref (ring2);
+  
+  return TRUE;
+
+ failure:
+  if (ring1)
+    _dbus_keyring_unref (ring1);
+  if (ring2)
+    _dbus_keyring_unref (ring2);
+
+  return FALSE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
+     
diff --git a/src/dbus/dbus-keyring.h b/src/dbus/dbus-keyring.h
new file mode 100644 (file)
index 0000000..ed7b3cb
--- /dev/null
@@ -0,0 +1,52 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-keyring.h Store secret cookies in your homedir
+ *
+ * Copyright (C) 2003  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_KEYRING_H
+#define DBUS_KEYRING_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-credentials.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusKeyring DBusKeyring;
+
+DBusKeyring* _dbus_keyring_new_for_credentials (DBusCredentials  *credentials,
+                                                const DBusString *context,
+                                                DBusError        *error);
+DBusKeyring* _dbus_keyring_ref                 (DBusKeyring      *keyring);
+void         _dbus_keyring_unref               (DBusKeyring      *keyring);
+dbus_bool_t  _dbus_keyring_validate_context    (const DBusString *context);
+int          _dbus_keyring_get_best_key        (DBusKeyring      *keyring,
+                                                DBusError        *error);
+dbus_bool_t  _dbus_keyring_is_for_credentials  (DBusKeyring      *keyring,
+                                                DBusCredentials  *credentials);
+dbus_bool_t  _dbus_keyring_get_hex_key         (DBusKeyring      *keyring,
+                                                int               key_id,
+                                                DBusString       *hex_key);
+
+
+DBUS_END_DECLS
+
+#endif /* DBUS_KEYRING_H */
diff --git a/src/dbus/dbus-list.c b/src/dbus/dbus-list.c
new file mode 100644 (file)
index 0000000..d314e95
--- /dev/null
@@ -0,0 +1,1405 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-list.c Generic linked list utility (internal to D-Bus implementation)
+ * 
+ * Copyright (C) 2002  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-internals.h"
+#include "dbus-list.h"
+#include "dbus-mempool.h"
+#include "dbus-threads-internal.h"
+
+/**
+ * @defgroup DBusList Linked list
+ * @ingroup  DBusInternals
+ * @brief DBusList data structure
+ *
+ * Types and functions related to DBusList.
+ */
+
+static DBusMemPool *list_pool;
+_DBUS_DEFINE_GLOBAL_LOCK (list);
+
+/**
+ * @defgroup DBusListInternals Linked list implementation details
+ * @ingroup  DBusInternals
+ * @brief DBusList implementation details
+ *
+ * The guts of DBusList.
+ *
+ * @{
+ */
+
+/* the mem pool is probably a speed hit, with the thread
+ * lock, though it does still save memory - unknown.
+ */
+static DBusList*
+alloc_link (void *data)
+{
+  DBusList *link;
+
+  _DBUS_LOCK (list);
+
+  if (list_pool == NULL)
+    {      
+      list_pool = _dbus_mem_pool_new (sizeof (DBusList), TRUE);
+
+      if (list_pool == NULL)
+        {
+          _DBUS_UNLOCK (list);
+          return NULL;
+        }
+
+      link = _dbus_mem_pool_alloc (list_pool);
+      if (link == NULL)
+        {
+          _dbus_mem_pool_free (list_pool);
+          list_pool = NULL;
+          _DBUS_UNLOCK (list);
+          return NULL;
+        }
+    }
+  else
+    {
+      link = _dbus_mem_pool_alloc (list_pool);
+    }
+
+  if (link)
+    link->data = data;
+  
+  _DBUS_UNLOCK (list);
+
+  return link;
+}
+
+static void
+free_link (DBusList *link)
+{  
+  _DBUS_LOCK (list);
+  if (_dbus_mem_pool_dealloc (list_pool, link))
+    {
+      _dbus_mem_pool_free (list_pool);
+      list_pool = NULL;
+    }
+  
+  _DBUS_UNLOCK (list);
+}
+
+static void
+link_before (DBusList **list,
+             DBusList  *before_this_link,
+             DBusList  *link)
+{
+  if (*list == NULL)
+    {
+      link->prev = link;
+      link->next = link;
+      *list = link;
+    }
+  else
+    {      
+      link->next = before_this_link;
+      link->prev = before_this_link->prev;
+      before_this_link->prev = link;
+      link->prev->next = link;
+      
+      if (before_this_link == *list)
+        *list = link;
+    }
+}
+
+static void
+link_after (DBusList **list,
+            DBusList  *after_this_link,
+            DBusList  *link)
+{
+  if (*list == NULL)
+    {
+      link->prev = link;
+      link->next = link;
+      *list = link;
+    }
+  else
+    {
+      link->prev = after_this_link;
+      link->next = after_this_link->next;
+      after_this_link->next = link;
+      link->next->prev = link;
+    }
+}
+
+/** @} */
+
+/**
+ * @addtogroup DBusList
+ * @{
+ */
+
+/**
+ * @struct DBusList
+ *
+ * A node in a linked list.
+ *
+ * DBusList is a circular list; that is, the tail of the list
+ * points back to the head of the list. The empty list is
+ * represented by a #NULL pointer.
+ */
+
+/**
+ * @def _dbus_list_get_next_link
+ *
+ * Gets the next link in the list, or #NULL if
+ * there are no more links. Used for iteration.
+ *
+ * @code
+ * DBusList *link;
+ * link = _dbus_list_get_first_link (&list);
+ * while (link != NULL)
+ *   {
+ *     printf ("value is %p\n", link->data);
+ *     link = _dbus_list_get_next_link (&link);
+ *   }
+ * @endcode
+ *
+ * @param list address of the list head.
+ * @param link current link.
+ * @returns the next link, or %NULL if none.
+ * 
+ */
+
+/**
+ * @def _dbus_list_get_prev_link
+ *
+ * Gets the previous link in the list, or #NULL if
+ * there are no more links. Used for iteration.
+ *
+ * @code
+ * DBusList *link;
+ * link = _dbus_list_get_last_link (&list);
+ * while (link != NULL)
+ *   {
+ *     printf ("value is %p\n", link->data);
+ *     link = _dbus_list_get_prev_link (&link);
+ *   }
+ * @endcode
+ *
+ * @param list address of the list head.
+ * @param link current link.
+ * @returns the previous link, or %NULL if none.
+ * 
+ */
+
+/**
+ * Allocates a linked list node. Useful for preallocating
+ * nodes and using _dbus_list_append_link() to avoid
+ * allocations.
+ * 
+ * @param data the value to store in the link.
+ * @returns a newly allocated link.
+ */
+DBusList*
+_dbus_list_alloc_link (void *data)
+{
+  return alloc_link (data);
+}
+
+/**
+ * Frees a linked list node allocated with _dbus_list_alloc_link.
+ * Does not free the data in the node.
+ *
+ * @param link the list node
+ */
+void
+_dbus_list_free_link (DBusList *link)
+{
+  free_link (link);
+}
+
+
+/**
+ * Appends a value to the list. May return #FALSE
+ * if insufficient memory exists to add a list link.
+ * This is a constant-time operation.
+ *
+ * @param list address of the list head.
+ * @param data the value to append.
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_list_append (DBusList **list,
+                   void      *data)
+{
+  if (!_dbus_list_prepend (list, data))
+    return FALSE;
+
+  /* Now cycle the list forward one so the prepended node is the tail */
+  *list = (*list)->next;
+
+  return TRUE;
+}
+
+/**
+ * Prepends a value to the list. May return #FALSE
+ * if insufficient memory exists to add a list link.
+ * This is a constant-time operation.
+ *
+ * @param list address of the list head.
+ * @param data the value to prepend.
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_list_prepend (DBusList **list,
+                    void      *data)
+{
+  DBusList *link;
+
+  link = alloc_link (data);
+  if (link == NULL)
+    return FALSE;
+
+  link_before (list, *list, link);
+
+  return TRUE;
+}
+
+/**
+ * Appends a link to the list.
+ * Cannot fail due to out of memory.
+ * This is a constant-time operation.
+ *
+ * @param list address of the list head.
+ * @param link the link to append.
+ */
+void
+_dbus_list_append_link (DBusList **list,
+                       DBusList *link)
+{
+  _dbus_list_prepend_link (list, link);
+
+  /* Now cycle the list forward one so the prepended node is the tail */
+  *list = (*list)->next;
+}
+
+/**
+ * Prepends a link to the list. 
+ * Cannot fail due to out of memory.
+ * This is a constant-time operation.
+ *
+ * @param list address of the list head.
+ * @param link the link to prepend.
+ */
+void
+_dbus_list_prepend_link (DBusList **list,
+                        DBusList *link)
+{
+  link_before (list, *list, link);
+}
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Inserts data into the list before the given existing link.
+ * 
+ * @param list the list to modify
+ * @param before_this_link existing link to insert before, or #NULL to append
+ * @param data the value to insert
+ * @returns #TRUE on success, #FALSE if memory allocation fails
+ */
+dbus_bool_t
+_dbus_list_insert_before (DBusList **list,
+                          DBusList  *before_this_link,
+                          void      *data)
+{
+  DBusList *link;
+  
+  if (before_this_link == NULL)
+    return _dbus_list_append (list, data);
+  else
+    {
+      link = alloc_link (data);
+      if (link == NULL)
+        return FALSE;
+  
+      link_before (list, before_this_link, link);
+    }
+  
+  return TRUE;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * Inserts data into the list after the given existing link.
+ * 
+ * @param list the list to modify
+ * @param after_this_link existing link to insert after, or #NULL to prepend
+ * @param data the value to insert
+ * @returns #TRUE on success, #FALSE if memory allocation fails
+ */
+dbus_bool_t
+_dbus_list_insert_after (DBusList **list,
+                         DBusList  *after_this_link,
+                         void      *data)
+{
+  DBusList *link;  
+
+  if (after_this_link == NULL)
+    return _dbus_list_prepend (list, data);
+  else
+    {
+      link = alloc_link (data);
+      if (link == NULL)
+        return FALSE;
+  
+      link_after (list, after_this_link, link);
+    }
+  
+  return TRUE;
+}
+
+/**
+ * Inserts a link into the list before the given existing link.
+ * 
+ * @param list the list to modify
+ * @param before_this_link existing link to insert before, or #NULL to append
+ * @param link the link to insert
+ */
+void
+_dbus_list_insert_before_link (DBusList **list,
+                               DBusList  *before_this_link,
+                               DBusList  *link)
+{
+  if (before_this_link == NULL)
+    _dbus_list_append_link (list, link);
+  else
+    link_before (list, before_this_link, link);
+}
+
+/**
+ * Inserts a link into the list after the given existing link.
+ * 
+ * @param list the list to modify
+ * @param after_this_link existing link to insert after, or #NULL to prepend
+ * @param link the link to insert
+ */
+void
+_dbus_list_insert_after_link (DBusList **list,
+                              DBusList  *after_this_link,
+                              DBusList  *link)
+{
+  if (after_this_link == NULL)
+    _dbus_list_prepend_link (list, link);
+  else  
+    link_after (list, after_this_link, link);
+}
+
+/**
+ * Removes a value from the list. Only removes the
+ * first value equal to the given data pointer,
+ * even if multiple values exist which match.
+ * This is a linear-time operation.
+ *
+ * @param list address of the list head.
+ * @param data the value to remove.
+ * @returns #TRUE if a value was found to remove.
+ */
+dbus_bool_t
+_dbus_list_remove (DBusList **list,
+                   void      *data)
+{
+  DBusList *link;
+
+  link = *list;
+  while (link != NULL)
+    {
+      if (link->data == data)
+        {
+          _dbus_list_remove_link (list, link);
+          return TRUE;
+        }
+      
+      link = _dbus_list_get_next_link (list, link);
+    }
+
+  return FALSE;
+}
+
+/**
+ * Removes a value from the list. Only removes the
+ * last value equal to the given data pointer,
+ * even if multiple values exist which match.
+ * This is a linear-time operation.
+ *
+ * @param list address of the list head.
+ * @param data the value to remove.
+ * @returns #TRUE if a value was found to remove.
+ */
+dbus_bool_t
+_dbus_list_remove_last (DBusList **list,
+                        void      *data)
+{
+  DBusList *link;
+
+  link = _dbus_list_find_last (list, data);
+  if (link)
+    {
+      _dbus_list_remove_link (list, link);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+/**
+ * Finds a value in the list. Returns the last link
+ * with value equal to the given data pointer.
+ * This is a linear-time operation.
+ * Returns #NULL if no value found that matches.
+ *
+ * @param list address of the list head.
+ * @param data the value to find.
+ * @returns the link if found
+ */
+DBusList*
+_dbus_list_find_last (DBusList **list,
+                      void      *data)
+{
+  DBusList *link;
+
+  link = _dbus_list_get_last_link (list);
+
+  while (link != NULL)
+    {
+      if (link->data == data)
+        return link;
+      
+      link = _dbus_list_get_prev_link (list, link);
+    }
+
+  return NULL;
+}
+
+/**
+ * Removes the given link from the list, but doesn't
+ * free it. _dbus_list_remove_link() both removes the
+ * link and also frees it.
+ *
+ * @param list the list
+ * @param link the link in the list
+ */
+void
+_dbus_list_unlink (DBusList **list,
+                   DBusList  *link)
+{
+  if (link->next == link)
+    {
+      /* one-element list */
+      *list = NULL;
+    }
+  else
+    {      
+      link->prev->next = link->next;
+      link->next->prev = link->prev;
+      
+      if (*list == link)
+        *list = link->next;
+    }
+
+  link->next = NULL;
+  link->prev = NULL;
+}
+
+/**
+ * Removes a link from the list. This is a constant-time operation.
+ *
+ * @param list address of the list head.
+ * @param link the list link to remove.
+ */
+void
+_dbus_list_remove_link (DBusList **list,
+                        DBusList  *link)
+{
+  _dbus_list_unlink (list, link);
+  free_link (link);
+}
+
+/**
+ * Frees all links in the list and sets the list head to #NULL. Does
+ * not free the data in each link, for obvious reasons. This is a
+ * linear-time operation.
+ *
+ * @param list address of the list head.
+ */
+void
+_dbus_list_clear (DBusList **list)
+{
+  DBusList *link;
+
+  link = *list;
+  while (link != NULL)
+    {
+      DBusList *next = _dbus_list_get_next_link (list, link);
+      
+      free_link (link);
+      
+      link = next;
+    }
+
+  *list = NULL;
+}
+
+/**
+ * Gets the first link in the list.
+ * This is a constant-time operation.
+ *
+ * @param list address of the list head.
+ * @returns the first link, or #NULL for an empty list.
+ */
+DBusList*
+_dbus_list_get_first_link (DBusList **list)
+{
+  return *list;
+}
+
+/**
+ * Gets the last link in the list.
+ * This is a constant-time operation.
+ *
+ * @param list address of the list head.
+ * @returns the last link, or #NULL for an empty list.
+ */
+DBusList*
+_dbus_list_get_last_link (DBusList **list)
+{
+  if (*list == NULL)
+    return NULL;
+  else
+    return (*list)->prev;
+}
+
+/**
+ * Gets the last data in the list.
+ * This is a constant-time operation.
+ *
+ * @param list address of the list head.
+ * @returns the last data in the list, or #NULL for an empty list.
+ */
+void*
+_dbus_list_get_last (DBusList **list)
+{
+  if (*list == NULL)
+    return NULL;
+  else
+    return (*list)->prev->data;
+}
+
+/**
+ * Gets the first data in the list.
+ * This is a constant-time operation.
+ *
+ * @param list address of the list head.
+ * @returns the first data in the list, or #NULL for an empty list.
+ */
+void*
+_dbus_list_get_first (DBusList **list)
+{
+  if (*list == NULL)
+    return NULL;
+  else
+    return (*list)->data;
+}
+
+/**
+ * Removes the first link in the list and returns it.  This is a
+ * constant-time operation.
+ *
+ * @param list address of the list head.
+ * @returns the first link in the list, or #NULL for an empty list.
+ */
+DBusList*
+_dbus_list_pop_first_link (DBusList **list)
+{
+  DBusList *link;
+  
+  link = _dbus_list_get_first_link (list);
+  if (link == NULL)
+    return NULL;
+
+  _dbus_list_unlink (list, link);
+
+  return link;
+}
+
+/**
+ * Removes the first value in the list and returns it.  This is a
+ * constant-time operation.
+ *
+ * @param list address of the list head.
+ * @returns the first data in the list, or #NULL for an empty list.
+ */
+void*
+_dbus_list_pop_first (DBusList **list)
+{
+  DBusList *link;
+  void *data;
+  
+  link = _dbus_list_get_first_link (list);
+  if (link == NULL)
+    return NULL;
+  
+  data = link->data;
+  _dbus_list_remove_link (list, link);
+
+  return data;
+}
+
+/**
+ * Removes the last value in the list and returns it.  This is a
+ * constant-time operation.
+ *
+ * @param list address of the list head.
+ * @returns the last data in the list, or #NULL for an empty list.
+ */
+void*
+_dbus_list_pop_last (DBusList **list)
+{
+  DBusList *link;
+  void *data;
+  
+  link = _dbus_list_get_last_link (list);
+  if (link == NULL)
+    return NULL;
+  
+  data = link->data;
+  _dbus_list_remove_link (list, link);
+
+  return data;
+}
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Removes the last link in the list and returns it.  This is a
+ * constant-time operation.
+ *
+ * @param list address of the list head.
+ * @returns the last link in the list, or #NULL for an empty list.
+ */
+DBusList*
+_dbus_list_pop_last_link (DBusList **list)
+{
+  DBusList *link;
+  
+  link = _dbus_list_get_last_link (list);
+  if (link == NULL)
+    return NULL;
+
+  _dbus_list_unlink (list, link);
+
+  return link;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * Copies a list. This is a linear-time operation.  If there isn't
+ * enough memory to copy the entire list, the destination list will be
+ * set to #NULL.
+ *
+ * @param list address of the head of the list to copy.
+ * @param dest address where the copied list should be placed.
+ * @returns #TRUE on success, #FALSE if not enough memory.
+ */
+dbus_bool_t
+_dbus_list_copy (DBusList **list,
+                 DBusList **dest)
+{
+  DBusList *link;
+
+  _dbus_assert (list != dest);
+
+  *dest = NULL;
+  
+  link = *list;
+  while (link != NULL)
+    {
+      if (!_dbus_list_append (dest, link->data))
+        {
+          /* free what we have so far */
+          _dbus_list_clear (dest);
+          return FALSE;
+        }
+      
+      link = _dbus_list_get_next_link (list, link);
+    }
+
+  return TRUE;
+}
+
+/**
+ * Gets the length of a list. This is a linear-time
+ * operation.
+ *
+ * @param list address of the head of the list
+ * @returns number of elements in the list.
+ */
+int
+_dbus_list_get_length (DBusList **list)
+{
+  DBusList *link;
+  int length;
+
+  length = 0;
+  
+  link = *list;
+  while (link != NULL)
+    {
+      ++length;
+      
+      link = _dbus_list_get_next_link (list, link);
+    }
+
+  return length;
+}
+
+/**
+ * Calls the given function for each element in the list.  The
+ * function is passed the list element as its first argument, and the
+ * given data as its second argument.
+ *
+ * @param list address of the head of the list.
+ * @param function function to call for each element.
+ * @param data extra data for the function.
+ * 
+ */
+void
+_dbus_list_foreach (DBusList          **list,
+                    DBusForeachFunction function,
+                    void               *data)
+{
+  DBusList *link;
+
+  link = *list;
+  while (link != NULL)
+    {
+      DBusList *next = _dbus_list_get_next_link (list, link);
+      
+      (* function) (link->data, data);
+      
+      link = next;
+    }
+}
+
+/**
+ * Check whether length is exactly one.
+ *
+ * @param list the list
+ * @returns #TRUE if length is exactly one
+ */
+dbus_bool_t
+_dbus_list_length_is_one (DBusList **list)
+{
+  return (*list != NULL &&
+          (*list)->next == *list);
+}
+
+/** @} */
+
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-test.h"
+#include <stdio.h>
+
+static void
+verify_list (DBusList **list)
+{
+  DBusList *link;
+  int length;
+  
+  link = *list;
+
+  if (link == NULL)
+    return;
+
+  if (link->next == link)
+    {
+      _dbus_assert (link->prev == link);
+      _dbus_assert (*list == link);
+      return;
+    }
+
+  length = 0;
+  do
+    {
+      length += 1;
+      _dbus_assert (link->prev->next == link);
+      _dbus_assert (link->next->prev == link);
+      link = link->next;
+    }
+  while (link != *list);
+
+  _dbus_assert (length == _dbus_list_get_length (list));
+
+  if (length == 1)
+    _dbus_assert (_dbus_list_length_is_one (list));
+  else
+    _dbus_assert (!_dbus_list_length_is_one (list));
+}
+
+static dbus_bool_t
+is_ascending_sequence (DBusList **list)
+{
+  DBusList *link;
+  int prev;
+
+  prev = _DBUS_INT_MIN;
+  
+  link = _dbus_list_get_first_link (list);
+  while (link != NULL)
+    {
+      int v = _DBUS_POINTER_TO_INT (link->data);
+      
+      if (v <= prev)
+        return FALSE;
+
+      prev = v;
+      
+      link = _dbus_list_get_next_link (list, link);
+    }
+
+  return TRUE;
+}
+
+static dbus_bool_t
+is_descending_sequence (DBusList **list)
+{
+  DBusList *link;
+  int prev;
+
+  prev = _DBUS_INT_MAX;
+  
+  link = _dbus_list_get_first_link (list);
+  while (link != NULL)
+    {
+      int v = _DBUS_POINTER_TO_INT (link->data);
+
+      if (v >= prev)
+        return FALSE;
+
+      prev = v;
+      
+      link = _dbus_list_get_next_link (list, link);
+    }
+
+  return TRUE;
+}
+
+static dbus_bool_t
+all_even_values (DBusList **list)
+{
+  DBusList *link;
+  
+  link = _dbus_list_get_first_link (list);
+  while (link != NULL)
+    {
+      int v = _DBUS_POINTER_TO_INT (link->data);
+
+      if ((v % 2) != 0)
+        return FALSE;
+      
+      link = _dbus_list_get_next_link (list, link);
+    }
+
+  return TRUE;
+}
+
+static dbus_bool_t
+all_odd_values (DBusList **list)
+{
+  DBusList *link;
+  
+  link = _dbus_list_get_first_link (list);
+  while (link != NULL)
+    {
+      int v = _DBUS_POINTER_TO_INT (link->data);
+
+      if ((v % 2) == 0)
+        return FALSE;
+      
+      link = _dbus_list_get_next_link (list, link);
+    }
+
+  return TRUE;
+}
+
+static dbus_bool_t
+lists_equal (DBusList **list1,
+             DBusList **list2)
+{
+  DBusList *link1;
+  DBusList *link2;
+  
+  link1 = _dbus_list_get_first_link (list1);
+  link2 = _dbus_list_get_first_link (list2);
+  while (link1 && link2)
+    {
+      if (link1->data != link2->data)
+        return FALSE;
+      
+      link1 = _dbus_list_get_next_link (list1, link1);
+      link2 = _dbus_list_get_next_link (list2, link2);
+    }
+
+  if (link1 || link2)
+    return FALSE;
+
+  return TRUE;
+}
+
+/**
+ * @ingroup DBusListInternals
+ * Unit test for DBusList
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_list_test (void)
+{
+  DBusList *list1;
+  DBusList *list2;
+  DBusList *link1;
+  DBusList *link2;
+  DBusList *copy1;
+  DBusList *copy2;
+  int i;
+  
+  list1 = NULL;
+  list2 = NULL;
+
+  /* Test append and prepend */
+  
+  i = 0;
+  while (i < 10)
+    {
+      if (!_dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i)))
+        _dbus_assert_not_reached ("could not allocate for append");
+      
+      if (!_dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i)))
+        _dbus_assert_not_reached ("count not allocate for prepend");
+      ++i;
+
+      verify_list (&list1);
+      verify_list (&list2);
+      
+      _dbus_assert (_dbus_list_get_length (&list1) == i);
+      _dbus_assert (_dbus_list_get_length (&list2) == i);
+    }
+
+  _dbus_assert (is_ascending_sequence (&list1));
+  _dbus_assert (is_descending_sequence (&list2));
+
+  /* Test list clear */
+  _dbus_list_clear (&list1);
+  _dbus_list_clear (&list2);
+
+  verify_list (&list1);
+  verify_list (&list2);
+
+  /* Test get_first, get_last, pop_first, pop_last */
+  
+  i = 0;
+  while (i < 10)
+    {
+      _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
+      _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
+      ++i;
+    }
+
+  --i;
+  while (i >= 0)
+    {
+      void *got_data1;
+      void *got_data2;
+      
+      void *data1;
+      void *data2;
+
+      got_data1 = _dbus_list_get_last (&list1);
+      got_data2 = _dbus_list_get_first (&list2);
+      
+      data1 = _dbus_list_pop_last (&list1);
+      data2 = _dbus_list_pop_first (&list2);
+
+      _dbus_assert (got_data1 == data1);
+      _dbus_assert (got_data2 == data2);
+      
+      _dbus_assert (_DBUS_POINTER_TO_INT (data1) == i);
+      _dbus_assert (_DBUS_POINTER_TO_INT (data2) == i);
+
+      verify_list (&list1);
+      verify_list (&list2);
+
+      _dbus_assert (is_ascending_sequence (&list1));
+      _dbus_assert (is_descending_sequence (&list2));
+      
+      --i;
+    }
+
+  _dbus_assert (list1 == NULL);
+  _dbus_assert (list2 == NULL);
+
+  /* Test get_first_link, get_last_link, pop_first_link, pop_last_link */
+  
+  i = 0;
+  while (i < 10)
+    {
+      _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
+      _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
+      ++i;
+    }
+
+  --i;
+  while (i >= 0)
+    {
+      DBusList *got_link1;
+      DBusList *got_link2;
+
+      DBusList *link1;
+      DBusList *link2;
+      
+      void *data1;
+      void *data2;
+      
+      got_link1 = _dbus_list_get_last_link (&list1);
+      got_link2 = _dbus_list_get_first_link (&list2);
+      
+      link1 = _dbus_list_pop_last_link (&list1);
+      link2 = _dbus_list_pop_first_link (&list2);
+
+      _dbus_assert (got_link1 == link1);
+      _dbus_assert (got_link2 == link2);
+
+      data1 = link1->data;
+      data2 = link2->data;
+
+      _dbus_list_free_link (link1);
+      _dbus_list_free_link (link2);
+      
+      _dbus_assert (_DBUS_POINTER_TO_INT (data1) == i);
+      _dbus_assert (_DBUS_POINTER_TO_INT (data2) == i);
+
+      verify_list (&list1);
+      verify_list (&list2);
+
+      _dbus_assert (is_ascending_sequence (&list1));
+      _dbus_assert (is_descending_sequence (&list2));
+      
+      --i;
+    }
+
+  _dbus_assert (list1 == NULL);
+  _dbus_assert (list2 == NULL);
+  
+  /* Test iteration */
+  
+  i = 0;
+  while (i < 10)
+    {
+      _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
+      _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
+      ++i;
+
+      verify_list (&list1);
+      verify_list (&list2);
+      
+      _dbus_assert (_dbus_list_get_length (&list1) == i);
+      _dbus_assert (_dbus_list_get_length (&list2) == i);
+    }
+
+  _dbus_assert (is_ascending_sequence (&list1));
+  _dbus_assert (is_descending_sequence (&list2));
+
+  --i;
+  link2 = _dbus_list_get_first_link (&list2);
+  while (link2 != NULL)
+    {
+      verify_list (&link2); /* pretend this link is the head */
+      
+      _dbus_assert (_DBUS_POINTER_TO_INT (link2->data) == i);
+      
+      link2 = _dbus_list_get_next_link (&list2, link2);
+      --i;
+    }
+
+  i = 0;
+  link1 = _dbus_list_get_first_link (&list1);
+  while (link1 != NULL)
+    {
+      verify_list (&link1); /* pretend this link is the head */
+      
+      _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);
+      
+      link1 = _dbus_list_get_next_link (&list1, link1);
+      ++i;
+    }
+
+  --i;
+  link1 = _dbus_list_get_last_link (&list1);
+  while (link1 != NULL)
+    {
+      verify_list (&link1); /* pretend this link is the head */
+
+      _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);
+      
+      link1 = _dbus_list_get_prev_link (&list1, link1);
+      --i;
+    }
+
+  _dbus_list_clear (&list1);
+  _dbus_list_clear (&list2);
+
+  /* Test remove */
+  
+  i = 0;
+  while (i < 10)
+    {
+      _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
+      _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
+      ++i;
+    }
+
+  --i;
+  while (i >= 0)
+    {
+      if ((i % 2) == 0)
+        {
+          if (!_dbus_list_remove (&list1, _DBUS_INT_TO_POINTER (i)))
+            _dbus_assert_not_reached ("element should have been in list");
+          if (!_dbus_list_remove (&list2, _DBUS_INT_TO_POINTER (i)))
+            _dbus_assert_not_reached ("element should have been in list");
+
+          verify_list (&list1);
+          verify_list (&list2);
+        }
+      --i;
+    }
+
+  _dbus_assert (all_odd_values (&list1));
+  _dbus_assert (all_odd_values (&list2));
+
+  _dbus_list_clear (&list1);
+  _dbus_list_clear (&list2);
+
+  /* test removing the other half of the elements */
+  
+  i = 0;
+  while (i < 10)
+    {
+      _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
+      _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
+      ++i;
+    }
+
+  --i;
+  while (i >= 0)
+    {
+      if ((i % 2) != 0)
+        {
+          if (!_dbus_list_remove (&list1, _DBUS_INT_TO_POINTER (i)))
+            _dbus_assert_not_reached ("element should have been in list");
+          if (!_dbus_list_remove (&list2, _DBUS_INT_TO_POINTER (i)))
+            _dbus_assert_not_reached ("element should have been in list");
+
+          verify_list (&list1);
+          verify_list (&list2);
+        }
+      --i;
+    }
+
+  _dbus_assert (all_even_values (&list1));
+  _dbus_assert (all_even_values (&list2));
+
+  /* clear list using remove_link */
+  while (list1 != NULL)
+    {
+      _dbus_list_remove_link (&list1, list1);
+      verify_list (&list1);
+    }
+  while (list2 != NULL)
+    {
+      _dbus_list_remove_link (&list2, list2);
+      verify_list (&list2);
+    }
+
+  /* Test remove link more generally */
+  i = 0;
+  while (i < 10)
+    {
+      _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
+      _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
+      ++i;
+    }
+
+  --i;
+  link2 = _dbus_list_get_first_link (&list2);
+  while (link2 != NULL)
+    {
+      DBusList *next = _dbus_list_get_next_link (&list2, link2);
+      
+      _dbus_assert (_DBUS_POINTER_TO_INT (link2->data) == i);
+
+      if ((i % 2) == 0)
+        _dbus_list_remove_link (&list2, link2);
+
+      verify_list (&list2);
+      
+      link2 = next;
+      --i;
+    }
+
+  _dbus_assert (all_odd_values (&list2));  
+  _dbus_list_clear (&list2);
+  
+  i = 0;
+  link1 = _dbus_list_get_first_link (&list1);
+  while (link1 != NULL)
+    {
+      DBusList *next = _dbus_list_get_next_link (&list1, link1);
+
+      _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);
+
+      if ((i % 2) != 0)
+        _dbus_list_remove_link (&list1, link1);
+
+      verify_list (&list1);
+      
+      link1 = next;
+      ++i;
+    }
+
+  _dbus_assert (all_even_values (&list1));
+  _dbus_list_clear (&list1);
+
+  /* Test copying a list */
+  i = 0;
+  while (i < 10)
+    {
+      _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
+      _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
+      ++i;
+    }
+
+  /* bad pointers, because they are allowed in the copy dest */
+  copy1 = _DBUS_INT_TO_POINTER (0x342234);
+  copy2 = _DBUS_INT_TO_POINTER (23);
+  
+  _dbus_list_copy (&list1, &copy1);
+  verify_list (&list1);
+  verify_list (&copy1);
+  _dbus_assert (lists_equal (&list1, &copy1));
+  
+  _dbus_list_copy (&list2, &copy2);
+  verify_list (&list2);
+  verify_list (&copy2);
+  _dbus_assert (lists_equal (&list2, &copy2));
+
+  /* Now test copying empty lists */
+  _dbus_list_clear (&list1);
+  _dbus_list_clear (&list2);
+  _dbus_list_clear (&copy1);
+  _dbus_list_clear (&copy2);
+  
+  /* bad pointers, because they are allowed in the copy dest */
+  copy1 = _DBUS_INT_TO_POINTER (0x342234);
+  copy2 = _DBUS_INT_TO_POINTER (23);
+  
+  _dbus_list_copy (&list1, &copy1);
+  verify_list (&list1);
+  verify_list (&copy1);
+  _dbus_assert (lists_equal (&list1, &copy1));
+  
+  _dbus_list_copy (&list2, &copy2);
+  verify_list (&list2);
+  verify_list (&copy2);
+  _dbus_assert (lists_equal (&list2, &copy2));
+
+  _dbus_list_clear (&list1);
+  _dbus_list_clear (&list2);
+  
+  /* insert_before on empty list */
+  _dbus_list_insert_before (&list1, NULL,
+                            _DBUS_INT_TO_POINTER (0));
+  verify_list (&list1);
+
+  /* inserting before first element */
+  _dbus_list_insert_before (&list1, list1,
+                            _DBUS_INT_TO_POINTER (2));
+  verify_list (&list1);
+  _dbus_assert (is_descending_sequence (&list1));
+
+  /* inserting in the middle */
+  _dbus_list_insert_before (&list1, list1->next,
+                            _DBUS_INT_TO_POINTER (1));
+  verify_list (&list1);
+  _dbus_assert (is_descending_sequence (&list1));  
+
+  /* using insert_before to append */
+  _dbus_list_insert_before (&list1, NULL,
+                            _DBUS_INT_TO_POINTER (-1));
+  verify_list (&list1);
+  _dbus_assert (is_descending_sequence (&list1));
+  
+  _dbus_list_clear (&list1);
+
+  /* insert_after on empty list */
+  _dbus_list_insert_after (&list1, NULL,
+                           _DBUS_INT_TO_POINTER (0));
+  verify_list (&list1);
+
+  /* inserting after first element */
+  _dbus_list_insert_after (&list1, list1,
+                           _DBUS_INT_TO_POINTER (1));
+  verify_list (&list1);
+  _dbus_assert (is_ascending_sequence (&list1));
+
+  /* inserting at the end */
+  _dbus_list_insert_after (&list1, list1->next,
+                           _DBUS_INT_TO_POINTER (2));
+  verify_list (&list1);
+  _dbus_assert (is_ascending_sequence (&list1));
+
+  /* using insert_after to prepend */
+  _dbus_list_insert_after (&list1, NULL,
+                           _DBUS_INT_TO_POINTER (-1));
+  verify_list (&list1);
+  _dbus_assert (is_ascending_sequence (&list1));
+  
+  _dbus_list_clear (&list1);
+
+  /* using remove_last */
+  _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (2));
+  _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (1));
+  _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (3));
+
+  _dbus_list_remove_last (&list1, _DBUS_INT_TO_POINTER (2));
+  
+  verify_list (&list1);
+  _dbus_assert (is_ascending_sequence (&list1));
+  
+  _dbus_list_clear (&list1);
+  
+  return TRUE;
+}
+
+#endif
diff --git a/src/dbus/dbus-list.h b/src/dbus/dbus-list.h
new file mode 100644 (file)
index 0000000..69ce265
--- /dev/null
@@ -0,0 +1,98 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-list.h Generic linked list utility (internal to D-Bus implementation)
+ * 
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_LIST_H
+#define DBUS_LIST_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-memory.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-sysdeps.h>
+
+DBUS_BEGIN_DECLS
+
+struct DBusList
+{
+  DBusList *prev; /**< Previous list node. */
+  DBusList *next; /**< Next list node. */
+  void     *data; /**< Data stored at this element. */
+};
+dbus_bool_t _dbus_list_append             (DBusList **list,
+                                           void      *data);
+dbus_bool_t _dbus_list_prepend            (DBusList **list,
+                                           void      *data);
+dbus_bool_t _dbus_list_insert_before      (DBusList **list,
+                                           DBusList  *before_this_link,
+                                           void      *data);
+dbus_bool_t _dbus_list_insert_after       (DBusList **list,
+                                           DBusList  *after_this_link,
+                                           void      *data);
+void        _dbus_list_insert_before_link (DBusList **list,
+                                           DBusList  *before_this_link,
+                                           DBusList  *link);
+void        _dbus_list_insert_after_link  (DBusList **list,
+                                           DBusList  *after_this_link,
+                                           DBusList  *link);
+dbus_bool_t _dbus_list_remove             (DBusList **list,
+                                           void      *data);
+dbus_bool_t _dbus_list_remove_last        (DBusList **list,
+                                           void      *data);
+void        _dbus_list_remove_link        (DBusList **list,
+                                           DBusList  *link);
+DBusList*   _dbus_list_find_last          (DBusList **list,
+                                           void      *data);
+void        _dbus_list_clear              (DBusList **list);
+DBusList*   _dbus_list_get_first_link     (DBusList **list);
+DBusList*   _dbus_list_get_last_link      (DBusList **list);
+void*       _dbus_list_get_last           (DBusList **list);
+void*       _dbus_list_get_first          (DBusList **list);
+void*       _dbus_list_pop_first          (DBusList **list);
+void*       _dbus_list_pop_last           (DBusList **list);
+DBusList*   _dbus_list_pop_first_link     (DBusList **list);
+DBusList*   _dbus_list_pop_last_link      (DBusList **list);
+dbus_bool_t _dbus_list_copy               (DBusList **list,
+                                           DBusList **dest);
+int         _dbus_list_get_length         (DBusList **list);
+DBusList*   _dbus_list_alloc_link         (void      *data);
+void        _dbus_list_free_link          (DBusList  *link);
+void        _dbus_list_unlink             (DBusList **list,
+                                           DBusList  *link);
+void        _dbus_list_append_link        (DBusList **list,
+                                           DBusList  *link);
+void        _dbus_list_prepend_link       (DBusList **list,
+                                           DBusList  *link);
+dbus_bool_t _dbus_list_length_is_one      (DBusList **list);
+
+
+
+
+void _dbus_list_foreach (DBusList            **list,
+                         DBusForeachFunction   function,
+                         void                 *data);
+
+#define _dbus_list_get_next_link(list, link) ((link)->next == *(list) ? NULL : (link)->next)
+#define _dbus_list_get_prev_link(list, link) ((link) == *(list) ? NULL : (link)->prev)
+
+DBUS_END_DECLS
+
+#endif /* DBUS_LIST_H */
diff --git a/src/dbus/dbus-macros.h b/src/dbus/dbus-macros.h
new file mode 100644 (file)
index 0000000..bf004b8
--- /dev/null
@@ -0,0 +1,137 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-macros.h  generic macros
+ *
+ * Copyright (C) 2002  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_MACROS_H
+#define DBUS_MACROS_H
+
+#ifdef  __cplusplus
+#  define DBUS_BEGIN_DECLS  extern "C" {
+#  define DBUS_END_DECLS    }
+#else
+#  define DBUS_BEGIN_DECLS
+#  define DBUS_END_DECLS
+#endif
+
+#ifndef TRUE
+#  define TRUE 1
+#endif
+#ifndef FALSE
+#  define FALSE 0
+#endif
+
+#ifndef NULL
+#  ifdef __cplusplus
+#    define NULL        (0L)
+#  else /* !__cplusplus */
+#    define NULL        ((void*) 0)
+#  endif /* !__cplusplus */
+#endif
+
+#if  __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+#  define DBUS_DEPRECATED __attribute__ ((__deprecated__))
+#elif defined(_MSC_VER) && (_MSC_VER >= 1300)
+#  define DBUS_DEPRECATED __declspec(deprecated)
+#else
+#  define DBUS_DEPRECATED
+#endif
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)
+#  define _DBUS_GNUC_EXTENSION __extension__
+#else
+#  define _DBUS_GNUC_EXTENSION
+#endif
+
+/* Normally docs are in .c files, but there isn't a .c file for this. */
+/**
+ * @defgroup DBusMacros Utility macros
+ * @ingroup  DBus
+ * @brief #TRUE, #FALSE, #NULL, and so on
+ *
+ * Utility macros.
+ *
+ * @{
+ */
+
+/**
+ * @def DBUS_BEGIN_DECLS
+ *
+ * Macro used prior to declaring functions in the D-Bus header
+ * files. Expands to "extern "C"" when using a C++ compiler,
+ * and expands to nothing when using a C compiler.
+ *
+ * Please don't use this in your own code, consider it
+ * D-Bus internal.
+ */
+/**
+ * @def DBUS_END_DECLS
+ *
+ * Macro used after declaring functions in the D-Bus header
+ * files. Expands to "}" when using a C++ compiler,
+ * and expands to nothing when using a C compiler.
+ *
+ * Please don't use this in your own code, consider it
+ * D-Bus internal.
+ */
+/**
+ * @def TRUE
+ *
+ * Expands to "1"
+ */
+/**
+ * @def FALSE
+ *
+ * Expands to "0"
+ */
+/**
+ * @def NULL
+ *
+ * A null pointer, defined appropriately for C or C++.
+ */
+/**
+ * @def DBUS_DEPRECATED
+ *
+ * Tells the compiler to warn about a function or type if it's used.
+ * Code marked in this way should also be enclosed in
+ * @code
+ * #ifndef DBUS_DISABLE_DEPRECATED
+ *  deprecated stuff here
+ * #endif
+ * @endcode
+ *
+ * Please don't use this in your own code, consider it
+ * D-Bus internal.
+ */
+/**
+ * @def _DBUS_GNUC_EXTENSION
+ *
+ * Tells gcc not to warn about extensions to the C standard in the
+ * following expression, even if compiling with -pedantic. Do not use
+ * this macro in your own code; please consider it to be internal to libdbus.
+ */
+
+/** @} */
+
+#endif /* DBUS_MACROS_H */
diff --git a/src/dbus/dbus-mainloop.c b/src/dbus/dbus-mainloop.c
new file mode 100644 (file)
index 0000000..ab595af
--- /dev/null
@@ -0,0 +1,908 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-mainloop.c  Main loop utility
+ *
+ * Copyright (C) 2003, 2004  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-mainloop.h"
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-sysdeps.h>
+
+#define MAINLOOP_SPEW 0
+
+#if MAINLOOP_SPEW
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+static const char*
+watch_flags_to_string (int flags)
+{
+  const char *watch_type;
+
+  if ((flags & DBUS_WATCH_READABLE) &&
+      (flags & DBUS_WATCH_WRITABLE))
+    watch_type = "readwrite";
+  else if (flags & DBUS_WATCH_READABLE)
+    watch_type = "read";
+  else if (flags & DBUS_WATCH_WRITABLE)
+    watch_type = "write";
+  else
+    watch_type = "not read or write";
+  return watch_type;
+}
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
+#endif /* MAINLOOP_SPEW */
+
+struct DBusLoop
+{
+  int refcount;
+  DBusList *callbacks;
+  int callback_list_serial;
+  int watch_count;
+  int timeout_count;
+  int depth; /**< number of recursive runs */
+  DBusList *need_dispatch;
+};
+
+typedef enum
+{
+  CALLBACK_WATCH,
+  CALLBACK_TIMEOUT
+} CallbackType;
+
+typedef struct
+{
+  int refcount;
+  CallbackType type;
+  void *data;
+  DBusFreeFunction free_data_func;
+} Callback;
+
+typedef struct
+{
+  Callback callback;
+  DBusWatchFunction function;
+  DBusWatch *watch;
+  /* last watch handle failed due to OOM */
+  unsigned int last_iteration_oom : 1;
+} WatchCallback;
+
+typedef struct
+{
+  Callback callback;
+  DBusTimeout *timeout;
+  DBusTimeoutFunction function;
+  unsigned long last_tv_sec;
+  unsigned long last_tv_usec;
+} TimeoutCallback;
+
+#define WATCH_CALLBACK(callback)   ((WatchCallback*)callback)
+#define TIMEOUT_CALLBACK(callback) ((TimeoutCallback*)callback)
+
+static WatchCallback*
+watch_callback_new (DBusWatch         *watch,
+                    DBusWatchFunction  function,
+                    void              *data,
+                    DBusFreeFunction   free_data_func)
+{
+  WatchCallback *cb;
+
+  cb = dbus_new (WatchCallback, 1);
+  if (cb == NULL)
+    return NULL;
+
+  cb->watch = watch;
+  cb->function = function;
+  cb->last_iteration_oom = FALSE;
+  cb->callback.refcount = 1;
+  cb->callback.type = CALLBACK_WATCH;
+  cb->callback.data = data;
+  cb->callback.free_data_func = free_data_func;
+  
+  return cb;
+}
+
+static TimeoutCallback*
+timeout_callback_new (DBusTimeout         *timeout,
+                      DBusTimeoutFunction  function,
+                      void                *data,
+                      DBusFreeFunction     free_data_func)
+{
+  TimeoutCallback *cb;
+
+  cb = dbus_new (TimeoutCallback, 1);
+  if (cb == NULL)
+    return NULL;
+
+  cb->timeout = timeout;
+  cb->function = function;
+  _dbus_get_current_time (&cb->last_tv_sec,
+                          &cb->last_tv_usec);
+  cb->callback.refcount = 1;    
+  cb->callback.type = CALLBACK_TIMEOUT;
+  cb->callback.data = data;
+  cb->callback.free_data_func = free_data_func;
+  
+  return cb;
+}
+
+static Callback * 
+callback_ref (Callback *cb)
+{
+  _dbus_assert (cb->refcount > 0);
+  
+  cb->refcount += 1;
+
+  return cb;
+}
+
+static void
+callback_unref (Callback *cb)
+{
+  _dbus_assert (cb->refcount > 0);
+
+  cb->refcount -= 1;
+
+  if (cb->refcount == 0)
+    {
+      if (cb->free_data_func)
+        (* cb->free_data_func) (cb->data);
+      
+      dbus_free (cb);
+    }
+}
+
+static dbus_bool_t
+add_callback (DBusLoop  *loop,
+              Callback *cb)
+{
+  if (!_dbus_list_append (&loop->callbacks, cb))
+    return FALSE;
+
+  loop->callback_list_serial += 1;
+
+  switch (cb->type)
+    {
+    case CALLBACK_WATCH:
+      loop->watch_count += 1;
+      break;
+    case CALLBACK_TIMEOUT:
+      loop->timeout_count += 1;
+      break;
+    }
+  
+  return TRUE;
+}
+
+static void
+remove_callback (DBusLoop  *loop,
+                 DBusList *link)
+{
+  Callback *cb = link->data;
+  
+  switch (cb->type)
+    {
+    case CALLBACK_WATCH:
+      loop->watch_count -= 1;
+      break;
+    case CALLBACK_TIMEOUT:
+      loop->timeout_count -= 1;
+      break;
+    }
+  
+  callback_unref (cb);
+  _dbus_list_remove_link (&loop->callbacks, link);
+  loop->callback_list_serial += 1;
+}
+
+DBusLoop*
+_dbus_loop_new (void)
+{
+  DBusLoop *loop;
+
+  loop = dbus_new0 (DBusLoop, 1);
+  if (loop == NULL)
+    return NULL;
+
+  loop->refcount = 1;
+  
+  return loop;
+}
+
+DBusLoop *
+_dbus_loop_ref (DBusLoop *loop)
+{
+  _dbus_assert (loop != NULL);
+  _dbus_assert (loop->refcount > 0);
+
+  loop->refcount += 1;
+
+  return loop;
+}
+
+void
+_dbus_loop_unref (DBusLoop *loop)
+{
+  _dbus_assert (loop != NULL);
+  _dbus_assert (loop->refcount > 0);
+
+  loop->refcount -= 1;
+  if (loop->refcount == 0)
+    {
+      while (loop->need_dispatch)
+        {
+          DBusConnection *connection = _dbus_list_pop_first (&loop->need_dispatch);
+
+          dbus_connection_unref (connection);
+        }
+      
+      dbus_free (loop);
+    }
+}
+
+dbus_bool_t
+_dbus_loop_add_watch (DBusLoop          *loop,
+                      DBusWatch        *watch,
+                      DBusWatchFunction  function,
+                      void             *data,
+                      DBusFreeFunction  free_data_func)
+{
+  WatchCallback *wcb;
+
+  wcb = watch_callback_new (watch, function, data, free_data_func);
+  if (wcb == NULL)
+    return FALSE;
+
+  if (!add_callback (loop, (Callback*) wcb))
+    {
+      wcb->callback.free_data_func = NULL; /* don't want to have this side effect */
+      callback_unref ((Callback*) wcb);
+      return FALSE;
+    }
+  
+  return TRUE;
+}
+
+void
+_dbus_loop_remove_watch (DBusLoop          *loop,
+                         DBusWatch        *watch,
+                         DBusWatchFunction  function,
+                         void             *data)
+{
+  DBusList *link;
+  
+  link = _dbus_list_get_first_link (&loop->callbacks);
+  while (link != NULL)
+    {
+      DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
+      Callback *this = link->data;
+
+      if (this->type == CALLBACK_WATCH &&
+          WATCH_CALLBACK (this)->watch == watch &&
+          this->data == data &&
+          WATCH_CALLBACK (this)->function == function)
+        {
+          remove_callback (loop, link);
+          
+          return;
+        }
+      
+      link = next;
+    }
+
+  _dbus_warn ("could not find watch %p function %p data %p to remove\n",
+              watch, (void *)function, data);
+}
+
+dbus_bool_t
+_dbus_loop_add_timeout (DBusLoop            *loop,
+                        DBusTimeout        *timeout,
+                        DBusTimeoutFunction  function,
+                        void               *data,
+                        DBusFreeFunction    free_data_func)
+{
+  TimeoutCallback *tcb;
+
+  tcb = timeout_callback_new (timeout, function, data, free_data_func);
+  if (tcb == NULL)
+    return FALSE;
+
+  if (!add_callback (loop, (Callback*) tcb))
+    {
+      tcb->callback.free_data_func = NULL; /* don't want to have this side effect */
+      callback_unref ((Callback*) tcb);
+      return FALSE;
+    }
+  
+  return TRUE;
+}
+
+void
+_dbus_loop_remove_timeout (DBusLoop            *loop,
+                           DBusTimeout        *timeout,
+                           DBusTimeoutFunction  function,
+                           void               *data)
+{
+  DBusList *link;
+  
+  link = _dbus_list_get_first_link (&loop->callbacks);
+  while (link != NULL)
+    {
+      DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
+      Callback *this = link->data;
+
+      if (this->type == CALLBACK_TIMEOUT &&
+          TIMEOUT_CALLBACK (this)->timeout == timeout &&
+          this->data == data &&
+          TIMEOUT_CALLBACK (this)->function == function)
+        {
+          remove_callback (loop, link);
+          
+          return;
+        }
+      
+      link = next;
+    }
+
+  _dbus_warn ("could not find timeout %p function %p data %p to remove\n",
+              timeout, (void *)function, data);
+}
+
+/* Convolutions from GLib, there really must be a better way
+ * to do this.
+ */
+static dbus_bool_t
+check_timeout (unsigned long    tv_sec,
+               unsigned long    tv_usec,
+               TimeoutCallback *tcb,
+               int             *timeout)
+{
+  long sec_remaining;
+  long msec_remaining;
+  unsigned long expiration_tv_sec;
+  unsigned long expiration_tv_usec;
+  long interval_seconds;
+  long interval_milliseconds;
+  int interval;
+
+  /* I'm pretty sure this function could suck (a lot) less */
+  
+  interval = dbus_timeout_get_interval (tcb->timeout);
+  
+  interval_seconds = interval / 1000L;
+  interval_milliseconds = interval % 1000L;
+  
+  expiration_tv_sec = tcb->last_tv_sec + interval_seconds;
+  expiration_tv_usec = tcb->last_tv_usec + interval_milliseconds * 1000;
+  if (expiration_tv_usec >= 1000000)
+    {
+      expiration_tv_usec -= 1000000;
+      expiration_tv_sec += 1;
+    }
+  
+  sec_remaining = expiration_tv_sec - tv_sec;
+  /* need to force this to be signed, as it is intended to sometimes
+   * produce a negative result
+   */
+  msec_remaining = ((long) expiration_tv_usec - (long) tv_usec) / 1000L;
+
+#if MAINLOOP_SPEW
+  _dbus_verbose ("Interval is %ld seconds %ld msecs\n",
+                 interval_seconds,
+                 interval_milliseconds);
+  _dbus_verbose ("Now is  %lu seconds %lu usecs\n",
+                 tv_sec, tv_usec);
+  _dbus_verbose ("Last is %lu seconds %lu usecs\n",
+                 tcb->last_tv_sec, tcb->last_tv_usec);
+  _dbus_verbose ("Exp is  %lu seconds %lu usecs\n",
+                 expiration_tv_sec, expiration_tv_usec);
+  _dbus_verbose ("Pre-correction, sec_remaining %ld msec_remaining %ld\n",
+                 sec_remaining, msec_remaining);
+#endif
+  
+  /* We do the following in a rather convoluted fashion to deal with
+   * the fact that we don't have an integral type big enough to hold
+   * the difference of two timevals in milliseconds.
+   */
+  if (sec_remaining < 0 || (sec_remaining == 0 && msec_remaining < 0))
+    {
+      *timeout = 0;
+    }
+  else
+    {
+      if (msec_remaining < 0)
+       {
+         msec_remaining += 1000;
+         sec_remaining -= 1;
+       }
+
+      if (sec_remaining > (_DBUS_INT_MAX / 1000) ||
+          msec_remaining > _DBUS_INT_MAX)
+        *timeout = _DBUS_INT_MAX;
+      else
+        *timeout = sec_remaining * 1000 + msec_remaining;        
+    }
+
+  if (*timeout > interval)
+    {
+      /* This indicates that the system clock probably moved backward */
+      _dbus_verbose ("System clock set backward! Resetting timeout.\n");
+      
+      tcb->last_tv_sec = tv_sec;
+      tcb->last_tv_usec = tv_usec;
+
+      *timeout = interval;
+    }
+  
+#if MAINLOOP_SPEW
+  _dbus_verbose ("  timeout expires in %d milliseconds\n", *timeout);
+#endif
+  
+  return *timeout == 0;
+}
+
+dbus_bool_t
+_dbus_loop_dispatch (DBusLoop *loop)
+{
+
+#if MAINLOOP_SPEW
+  _dbus_verbose ("  %d connections to dispatch\n", _dbus_list_get_length (&loop->need_dispatch));
+#endif
+  
+  if (loop->need_dispatch == NULL)
+    return FALSE;
+  
+ next:
+  while (loop->need_dispatch != NULL)
+    {
+      DBusConnection *connection = _dbus_list_pop_first (&loop->need_dispatch);
+      
+      while (TRUE)
+        {
+          DBusDispatchStatus status;
+          
+          status = dbus_connection_dispatch (connection);
+
+          if (status == DBUS_DISPATCH_COMPLETE)
+            {
+              dbus_connection_unref (connection);
+              goto next;
+            }
+          else
+            {
+              if (status == DBUS_DISPATCH_NEED_MEMORY)
+                _dbus_wait_for_memory ();
+            }
+        }
+    }
+
+  return TRUE;
+}
+
+dbus_bool_t
+_dbus_loop_queue_dispatch (DBusLoop       *loop,
+                           DBusConnection *connection)
+{
+  if (_dbus_list_append (&loop->need_dispatch, connection))
+    {
+      dbus_connection_ref (connection);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+/* Returns TRUE if we invoked any timeouts or have ready file
+ * descriptors, which is just used in test code as a debug hack
+ */
+
+dbus_bool_t
+_dbus_loop_iterate (DBusLoop     *loop,
+                    dbus_bool_t   block)
+{  
+#define N_STACK_DESCRIPTORS 64
+  dbus_bool_t retval;
+  DBusPollFD *fds;
+  DBusPollFD stack_fds[N_STACK_DESCRIPTORS];
+  int n_fds;
+  WatchCallback **watches_for_fds;
+  WatchCallback *stack_watches_for_fds[N_STACK_DESCRIPTORS];
+  int i;
+  DBusList *link;
+  int n_ready;
+  int initial_serial;
+  long timeout;
+  dbus_bool_t oom_watch_pending;
+  int orig_depth;
+  
+  retval = FALSE;      
+
+  fds = NULL;
+  watches_for_fds = NULL;
+  n_fds = 0;
+  oom_watch_pending = FALSE;
+  orig_depth = loop->depth;
+  
+#if MAINLOOP_SPEW
+  _dbus_verbose ("Iteration block=%d depth=%d timeout_count=%d watch_count=%d\n",
+                 block, loop->depth, loop->timeout_count, loop->watch_count);
+#endif
+  
+  if (loop->callbacks == NULL)
+    goto next_iteration;
+
+  if (loop->watch_count > N_STACK_DESCRIPTORS)
+    {
+      fds = dbus_new0 (DBusPollFD, loop->watch_count);
+      
+      while (fds == NULL)
+        {
+          _dbus_wait_for_memory ();
+          fds = dbus_new0 (DBusPollFD, loop->watch_count);
+        }
+      
+      watches_for_fds = dbus_new (WatchCallback*, loop->watch_count);
+      while (watches_for_fds == NULL)
+        {
+          _dbus_wait_for_memory ();
+          watches_for_fds = dbus_new (WatchCallback*, loop->watch_count);
+        }
+    }
+  else
+    {      
+      fds = stack_fds;
+      watches_for_fds = stack_watches_for_fds;
+    }
+
+  /* fill our array of fds and watches */
+  n_fds = 0;
+  link = _dbus_list_get_first_link (&loop->callbacks);
+  while (link != NULL)
+    {
+      DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
+      Callback *cb = link->data;
+      if (cb->type == CALLBACK_WATCH)
+        {
+          unsigned int flags;
+          WatchCallback *wcb = WATCH_CALLBACK (cb);
+
+          if (wcb->last_iteration_oom)
+            {
+              /* we skip this one this time, but reenable it next time,
+               * and have a timeout on this iteration
+               */
+              wcb->last_iteration_oom = FALSE;
+              oom_watch_pending = TRUE;
+              
+              retval = TRUE; /* return TRUE here to keep the loop going,
+                              * since we don't know the watch is inactive
+                              */
+
+#if MAINLOOP_SPEW
+              _dbus_verbose ("  skipping watch on fd %d as it was out of memory last time\n",
+                             dbus_watch_get_socket (wcb->watch));
+#endif
+            }
+          else if (dbus_watch_get_enabled (wcb->watch))
+            {
+              watches_for_fds[n_fds] = wcb;
+
+              callback_ref (cb);
+                  
+              flags = dbus_watch_get_flags (wcb->watch);
+                  
+              fds[n_fds].fd = dbus_watch_get_socket (wcb->watch);
+              fds[n_fds].revents = 0;
+              fds[n_fds].events = 0;
+              if (flags & DBUS_WATCH_READABLE)
+                fds[n_fds].events |= _DBUS_POLLIN;
+              if (flags & DBUS_WATCH_WRITABLE)
+                fds[n_fds].events |= _DBUS_POLLOUT;
+
+#if MAINLOOP_SPEW
+              _dbus_verbose ("  polling watch on fd %d  %s\n",
+                             fds[n_fds].fd, watch_flags_to_string (flags));
+#endif
+
+              n_fds += 1;
+            }
+          else
+            {
+#if MAINLOOP_SPEW
+              _dbus_verbose ("  skipping disabled watch on fd %d  %s\n",
+                             dbus_watch_get_socket (wcb->watch),
+                             watch_flags_to_string (dbus_watch_get_flags (wcb->watch)));
+#endif
+            }
+        }
+              
+      link = next;
+    }
+  
+  timeout = -1;
+  if (loop->timeout_count > 0)
+    {
+      unsigned long tv_sec;
+      unsigned long tv_usec;
+      
+      _dbus_get_current_time (&tv_sec, &tv_usec);
+          
+      link = _dbus_list_get_first_link (&loop->callbacks);
+      while (link != NULL)
+        {
+          DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
+          Callback *cb = link->data;
+
+          if (cb->type == CALLBACK_TIMEOUT &&
+              dbus_timeout_get_enabled (TIMEOUT_CALLBACK (cb)->timeout))
+            {
+              TimeoutCallback *tcb = TIMEOUT_CALLBACK (cb);
+              int msecs_remaining;
+
+              check_timeout (tv_sec, tv_usec, tcb, &msecs_remaining);
+
+              if (timeout < 0)
+                timeout = msecs_remaining;
+              else
+                timeout = MIN (msecs_remaining, timeout);
+
+#if MAINLOOP_SPEW
+              _dbus_verbose ("  timeout added, %d remaining, aggregate timeout %ld\n",
+                             msecs_remaining, timeout);
+#endif
+              
+              _dbus_assert (timeout >= 0);
+                  
+              if (timeout == 0)
+                break; /* it's not going to get shorter... */
+            }
+#if MAINLOOP_SPEW
+          else if (cb->type == CALLBACK_TIMEOUT)
+            {
+              _dbus_verbose ("  skipping disabled timeout\n");
+            }
+#endif
+          
+          link = next;
+        }
+    }
+
+  /* Never block if we have stuff to dispatch */
+  if (!block || loop->need_dispatch != NULL)
+    {
+      timeout = 0;
+#if MAINLOOP_SPEW
+      _dbus_verbose ("  timeout is 0 as we aren't blocking\n");
+#endif
+    }
+
+  /* if a watch is OOM, don't wait longer than the OOM
+   * wait to re-enable it
+   */
+  if (oom_watch_pending)
+    timeout = MIN (timeout, _dbus_get_oom_wait ());
+
+#if MAINLOOP_SPEW
+  _dbus_verbose ("  polling on %d descriptors timeout %ld\n", n_fds, timeout);
+#endif
+  
+  n_ready = _dbus_poll (fds, n_fds, timeout);
+
+  initial_serial = loop->callback_list_serial;
+
+  if (loop->timeout_count > 0)
+    {
+      unsigned long tv_sec;
+      unsigned long tv_usec;
+
+      _dbus_get_current_time (&tv_sec, &tv_usec);
+
+      /* It'd be nice to avoid this O(n) thingy here */
+      link = _dbus_list_get_first_link (&loop->callbacks);
+      while (link != NULL)
+        {
+          DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
+          Callback *cb = link->data;
+
+          if (initial_serial != loop->callback_list_serial)
+            goto next_iteration;
+
+          if (loop->depth != orig_depth)
+            goto next_iteration;
+              
+          if (cb->type == CALLBACK_TIMEOUT &&
+              dbus_timeout_get_enabled (TIMEOUT_CALLBACK (cb)->timeout))
+            {
+              TimeoutCallback *tcb = TIMEOUT_CALLBACK (cb);
+              int msecs_remaining;
+              
+              if (check_timeout (tv_sec, tv_usec,
+                                 tcb, &msecs_remaining))
+                {
+                  /* Save last callback time and fire this timeout */
+                  tcb->last_tv_sec = tv_sec;
+                  tcb->last_tv_usec = tv_usec;
+
+#if MAINLOOP_SPEW
+                  _dbus_verbose ("  invoking timeout\n");
+#endif
+                  
+                  (* tcb->function) (tcb->timeout,
+                                     cb->data);
+
+                  retval = TRUE;
+                }
+              else
+                {
+#if MAINLOOP_SPEW
+                  _dbus_verbose ("  timeout has not expired\n");
+#endif
+                }
+            }
+#if MAINLOOP_SPEW
+          else if (cb->type == CALLBACK_TIMEOUT)
+            {
+              _dbus_verbose ("  skipping invocation of disabled timeout\n");
+            }
+#endif
+
+          link = next;
+        }
+    }
+      
+  if (n_ready > 0)
+    {
+      i = 0;
+      while (i < n_fds)
+        {
+          /* FIXME I think this "restart if we change the watches"
+           * approach could result in starving watches
+           * toward the end of the list.
+           */
+          if (initial_serial != loop->callback_list_serial)
+            goto next_iteration;
+
+          if (loop->depth != orig_depth)
+            goto next_iteration;
+
+          if (fds[i].revents != 0)
+            {
+              WatchCallback *wcb;
+              unsigned int condition;
+                  
+              wcb = watches_for_fds[i];
+              
+              condition = 0;
+              if (fds[i].revents & _DBUS_POLLIN)
+                condition |= DBUS_WATCH_READABLE;
+              if (fds[i].revents & _DBUS_POLLOUT)
+                condition |= DBUS_WATCH_WRITABLE;
+              if (fds[i].revents & _DBUS_POLLHUP)
+                condition |= DBUS_WATCH_HANGUP;
+              if (fds[i].revents & _DBUS_POLLERR)
+                condition |= DBUS_WATCH_ERROR;
+
+              /* condition may still be 0 if we got some
+               * weird POLLFOO thing like POLLWRBAND
+               */
+                  
+              if (condition != 0 &&
+                  dbus_watch_get_enabled (wcb->watch))
+                {
+                  if (!(* wcb->function) (wcb->watch,
+                                          condition,
+                                          ((Callback*)wcb)->data))
+                    wcb->last_iteration_oom = TRUE;
+
+#if MAINLOOP_SPEW
+                  _dbus_verbose ("  Invoked watch, oom = %d\n",
+                                 wcb->last_iteration_oom);
+#endif
+                  
+                  retval = TRUE;
+                }
+            }
+              
+          ++i;
+        }
+    }
+      
+ next_iteration:
+#if MAINLOOP_SPEW
+  _dbus_verbose ("  moving to next iteration\n");
+#endif
+  
+  if (fds && fds != stack_fds)
+    dbus_free (fds);
+  if (watches_for_fds)
+    {
+      i = 0;
+      while (i < n_fds)
+        {
+          callback_unref (&watches_for_fds[i]->callback);
+          ++i;
+        }
+      
+      if (watches_for_fds != stack_watches_for_fds)
+        dbus_free (watches_for_fds);
+    }
+  
+  if (_dbus_loop_dispatch (loop))
+    retval = TRUE;
+  
+#if MAINLOOP_SPEW
+  _dbus_verbose ("Returning %d\n", retval);
+#endif
+  
+  return retval;
+}
+
+void
+_dbus_loop_run (DBusLoop *loop)
+{
+  int our_exit_depth;
+
+  _dbus_assert (loop->depth >= 0);
+  
+  _dbus_loop_ref (loop);
+  
+  our_exit_depth = loop->depth;
+  loop->depth += 1;
+
+  _dbus_verbose ("Running main loop, depth %d -> %d\n",
+                 loop->depth - 1, loop->depth);
+  
+  while (loop->depth != our_exit_depth)
+    _dbus_loop_iterate (loop, TRUE);
+
+  _dbus_loop_unref (loop);
+}
+
+void
+_dbus_loop_quit (DBusLoop *loop)
+{
+  _dbus_assert (loop->depth > 0);  
+  
+  loop->depth -= 1;
+
+  _dbus_verbose ("Quit main loop, depth %d -> %d\n",
+                 loop->depth + 1, loop->depth);
+}
+
+int
+_dbus_get_oom_wait (void)
+{
+#ifdef DBUS_BUILD_TESTS
+  /* make tests go fast */
+  return 0;
+#else
+  return 500;
+#endif
+}
+
+void
+_dbus_wait_for_memory (void)
+{
+  _dbus_verbose ("Waiting for more memory\n");
+  _dbus_sleep_milliseconds (_dbus_get_oom_wait ());
+}
+
+#endif /* !DOXYGEN_SHOULD_SKIP_THIS */
diff --git a/src/dbus/dbus-mainloop.h b/src/dbus/dbus-mainloop.h
new file mode 100644 (file)
index 0000000..4a3f9ec
--- /dev/null
@@ -0,0 +1,76 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-mainloop.h  Main loop utility
+ *
+ * Copyright (C) 2003  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_MAINLOOP_H
+#define DBUS_MAINLOOP_H
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+#include <dbus/dbus.h>
+
+typedef struct DBusLoop DBusLoop;
+
+typedef dbus_bool_t (* DBusWatchFunction)   (DBusWatch     *watch,
+                                             unsigned int   condition,
+                                             void          *data);
+typedef void        (* DBusTimeoutFunction) (DBusTimeout   *timeout,
+                                             void          *data);
+
+DBusLoop*   _dbus_loop_new            (void);
+DBusLoop*   _dbus_loop_ref            (DBusLoop            *loop);
+void        _dbus_loop_unref          (DBusLoop            *loop);
+dbus_bool_t _dbus_loop_add_watch      (DBusLoop            *loop,
+                                       DBusWatch           *watch,
+                                       DBusWatchFunction    function,
+                                       void                *data,
+                                       DBusFreeFunction     free_data_func);
+void        _dbus_loop_remove_watch   (DBusLoop            *loop,
+                                       DBusWatch           *watch,
+                                       DBusWatchFunction    function,
+                                       void                *data);
+dbus_bool_t _dbus_loop_add_timeout    (DBusLoop            *loop,
+                                       DBusTimeout         *timeout,
+                                       DBusTimeoutFunction  function,
+                                       void                *data,
+                                       DBusFreeFunction     free_data_func);
+void        _dbus_loop_remove_timeout (DBusLoop            *loop,
+                                       DBusTimeout         *timeout,
+                                       DBusTimeoutFunction  function,
+                                       void                *data);
+
+dbus_bool_t _dbus_loop_queue_dispatch (DBusLoop            *loop,
+                                       DBusConnection      *connection);
+
+void        _dbus_loop_run            (DBusLoop            *loop);
+void        _dbus_loop_quit           (DBusLoop            *loop);
+dbus_bool_t _dbus_loop_iterate        (DBusLoop            *loop,
+                                       dbus_bool_t          block);
+dbus_bool_t _dbus_loop_dispatch       (DBusLoop            *loop);
+
+int  _dbus_get_oom_wait    (void);
+void _dbus_wait_for_memory (void);
+
+#endif /* !DOXYGEN_SHOULD_SKIP_THIS */
+
+#endif /* DBUS_MAINLOOP_H */
+
diff --git a/src/dbus/dbus-marshal-basic.c b/src/dbus/dbus-marshal-basic.c
new file mode 100644 (file)
index 0000000..38fbe2d
--- /dev/null
@@ -0,0 +1,1988 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-basic.c  Marshalling routines for basic (primitive) types
+ *
+ * Copyright (C) 2002 CodeFactory AB
+ * Copyright (C) 2003, 2004, 2005 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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-internals.h"
+#include "dbus-marshal-basic.h"
+#include "dbus-signature.h"
+
+#include <string.h>
+
+/**
+ * @defgroup DBusMarshal marshaling and unmarshaling
+ * @ingroup  DBusInternals
+ * @brief functions to marshal/unmarshal data from the wire
+ *
+ * Types and functions related to converting primitive data types from
+ * wire format to native machine format, and vice versa.
+ *
+ * A signature is just a string with multiple types one after the other.
+ * for example a type is "i" or "(ii)", a signature is "i(ii)"
+ * where i is int and (ii) is struct { int; int; }
+ *
+ * @{
+ */
+
+static void
+pack_2_octets (dbus_uint16_t   value,
+               int             byte_order,
+               unsigned char  *data)
+{
+  _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 2) == data);
+
+  if ((byte_order) == DBUS_LITTLE_ENDIAN)
+    *((dbus_uint16_t*)(data)) = DBUS_UINT16_TO_LE (value);
+  else
+    *((dbus_uint16_t*)(data)) = DBUS_UINT16_TO_BE (value);
+}
+
+static void
+pack_4_octets (dbus_uint32_t   value,
+               int             byte_order,
+               unsigned char  *data)
+{
+  _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 4) == data);
+
+  if ((byte_order) == DBUS_LITTLE_ENDIAN)
+    *((dbus_uint32_t*)(data)) = DBUS_UINT32_TO_LE (value);
+  else
+    *((dbus_uint32_t*)(data)) = DBUS_UINT32_TO_BE (value);
+}
+
+static void
+pack_8_octets (DBusBasicValue     value,
+               int                byte_order,
+               unsigned char     *data)
+{
+  _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 8) == data);
+
+#ifdef DBUS_HAVE_INT64
+  if ((byte_order) == DBUS_LITTLE_ENDIAN)
+    *((dbus_uint64_t*)(data)) = DBUS_UINT64_TO_LE (value.u64);
+  else
+    *((dbus_uint64_t*)(data)) = DBUS_UINT64_TO_BE (value.u64);
+#else
+  *(DBus8ByteStruct*)data = value.u64;
+  swap_8_octets ((DBusBasicValue*)data, byte_order);
+#endif
+}
+
+/**
+ * Packs a 32 bit unsigned integer into a data pointer.
+ *
+ * @param value the value
+ * @param byte_order the byte order to use
+ * @param data the data pointer
+ */
+void
+_dbus_pack_uint32 (dbus_uint32_t   value,
+                   int             byte_order,
+                   unsigned char  *data)
+{
+  pack_4_octets (value, byte_order, data);
+}
+
+#ifndef DBUS_HAVE_INT64
+/* from ORBit */
+static void
+swap_bytes (unsigned char *data,
+            unsigned int   len)
+{
+  unsigned char *p1 = data;
+  unsigned char *p2 = data + len - 1;
+
+  while (p1 < p2)
+    {
+      unsigned char tmp = *p1;
+      *p1 = *p2;
+      *p2 = tmp;
+
+      --p2;
+      ++p1;
+    }
+}
+#endif /* !DBUS_HAVE_INT64 */
+
+static void
+swap_8_octets (DBusBasicValue    *value,
+               int                byte_order)
+{
+  if (byte_order != DBUS_COMPILER_BYTE_ORDER)
+    {
+#ifdef DBUS_HAVE_INT64
+      value->u64 = DBUS_UINT64_SWAP_LE_BE (value->u64);
+#else
+      swap_bytes ((unsigned char *)value, 8);
+#endif
+    }
+}
+
+#if 0
+static DBusBasicValue
+unpack_8_octets (int                  byte_order,
+                 const unsigned char *data)
+{
+  DBusBasicValue r;
+
+  _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 8) == data);
+  _dbus_assert (sizeof (r) == 8);
+
+#ifdef DBUS_HAVE_INT64
+  if (byte_order == DBUS_LITTLE_ENDIAN)
+    r.u64 = DBUS_UINT64_FROM_LE (*(dbus_uint64_t*)data);
+  else
+    r.u64 = DBUS_UINT64_FROM_BE (*(dbus_uint64_t*)data);
+#else
+  r.u64 = *(DBus8ByteStruct*)data;
+  swap_8_octets (&r, byte_order);
+#endif
+
+  return r;
+}
+#endif
+
+#ifndef _dbus_unpack_uint16
+/**
+ * Unpacks a 16 bit unsigned integer from a data pointer
+ *
+ * @param byte_order The byte order to use
+ * @param data the data pointer
+ * @returns the integer
+ */
+dbus_uint16_t
+_dbus_unpack_uint16 (int                  byte_order,
+                     const unsigned char *data)
+{
+  _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 2) == data);
+
+  if (byte_order == DBUS_LITTLE_ENDIAN)
+    return DBUS_UINT16_FROM_LE (*(dbus_uint16_t*)data);
+  else
+    return DBUS_UINT16_FROM_BE (*(dbus_uint16_t*)data);
+}
+#endif /* _dbus_unpack_uint16 */
+
+#ifndef _dbus_unpack_uint32
+/**
+ * Unpacks a 32 bit unsigned integer from a data pointer
+ *
+ * @param byte_order The byte order to use
+ * @param data the data pointer
+ * @returns the integer
+ */
+dbus_uint32_t
+_dbus_unpack_uint32 (int                  byte_order,
+                     const unsigned char *data)
+{
+  _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 4) == data);
+
+  if (byte_order == DBUS_LITTLE_ENDIAN)
+    return DBUS_UINT32_FROM_LE (*(dbus_uint32_t*)data);
+  else
+    return DBUS_UINT32_FROM_BE (*(dbus_uint32_t*)data);
+}
+#endif /* _dbus_unpack_uint32 */
+
+static void
+set_2_octets (DBusString          *str,
+              int                  offset,
+              dbus_uint16_t        value,
+              int                  byte_order)
+{
+  char *data;
+
+  _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN ||
+                byte_order == DBUS_BIG_ENDIAN);
+
+  data = _dbus_string_get_data_len (str, offset, 2);
+
+  pack_2_octets (value, byte_order, data);
+}
+
+static void
+set_4_octets (DBusString          *str,
+              int                  offset,
+              dbus_uint32_t        value,
+              int                  byte_order)
+{
+  char *data;
+
+  _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN ||
+                byte_order == DBUS_BIG_ENDIAN);
+
+  data = _dbus_string_get_data_len (str, offset, 4);
+
+  pack_4_octets (value, byte_order, data);
+}
+
+static void
+set_8_octets (DBusString          *str,
+              int                  offset,
+              DBusBasicValue       value,
+              int                  byte_order)
+{
+  char *data;
+
+  _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN ||
+                byte_order == DBUS_BIG_ENDIAN);
+
+  data = _dbus_string_get_data_len (str, offset, 8);
+
+  pack_8_octets (value, byte_order, data);
+}
+
+/**
+ * Sets the 4 bytes at the given offset to a marshaled unsigned
+ * integer, replacing anything found there previously.
+ *
+ * @param str the string to write the marshalled int to
+ * @param pos the byte offset where int should be written
+ * @param value the value
+ * @param byte_order the byte order to use
+ *
+ */
+void
+_dbus_marshal_set_uint32 (DBusString          *str,
+                          int                  pos,
+                          dbus_uint32_t        value,
+                          int                  byte_order)
+{
+  set_4_octets (str, pos, value, byte_order);
+}
+
+/**
+ * Sets the existing marshaled string at the given offset with
+ * a new marshaled string. The given offset must point to
+ * an existing string or the wrong length will be deleted
+ * and replaced with the new string.
+ *
+ * Note: no attempt is made by this function to re-align
+ * any data which has been already marshalled after this
+ * string. Use with caution.
+ *
+ * @param str the string to write the marshalled string to
+ * @param pos the position of the marshaled string length
+ * @param value the value
+ * @param byte_order the byte order to use
+ * @param old_end_pos place to store byte after the nul byte of the old value
+ * @param new_end_pos place to store byte after the nul byte of the new value
+ * @returns #TRUE on success, #FALSE if no memory
+ *
+ */
+static dbus_bool_t
+set_string (DBusString          *str,
+            int                  pos,
+            const char          *value,
+            int                  byte_order,
+            int                 *old_end_pos,
+            int                 *new_end_pos)
+{
+  int old_len, new_len;
+  DBusString dstr;
+
+  _dbus_string_init_const (&dstr, value);
+
+  _dbus_assert (_DBUS_ALIGN_VALUE (pos, 4) == (unsigned) pos);
+  old_len = _dbus_unpack_uint32 (byte_order,
+                                 _dbus_string_get_const_data_len (str, pos, 4));
+
+  new_len = _dbus_string_get_length (&dstr);
+
+  if (!_dbus_string_replace_len (&dstr, 0, new_len,
+                                 str, pos + 4, old_len))
+    return FALSE;
+
+  _dbus_marshal_set_uint32 (str, pos, new_len, byte_order);
+
+  if (old_end_pos)
+    *old_end_pos = pos + 4 + old_len + 1;
+  if (new_end_pos)
+    *new_end_pos = pos + 4 + new_len + 1;
+
+  return TRUE;
+}
+
+/**
+ * Sets the existing marshaled signature at the given offset to a new
+ * marshaled signature. Same basic ideas as set_string().
+ *
+ * @param str the string to write the marshalled signature to
+ * @param pos the position of the marshaled signature length
+ * @param value the value
+ * @param byte_order the byte order to use
+ * @param old_end_pos place to store byte after the nul byte of the old value
+ * @param new_end_pos place to store byte after the nul byte of the new value
+ * @returns #TRUE on success, #FALSE if no memory
+ *
+ */
+static dbus_bool_t
+set_signature (DBusString          *str,
+               int                  pos,
+               const char          *value,
+               int                  byte_order,
+               int                 *old_end_pos,
+               int                 *new_end_pos)
+{
+  int old_len, new_len;
+  DBusString dstr;
+
+  _dbus_string_init_const (&dstr, value);
+
+  old_len = _dbus_string_get_byte (str, pos);
+  new_len = _dbus_string_get_length (&dstr);
+
+  if (!_dbus_string_replace_len (&dstr, 0, new_len,
+                                 str, pos + 1, old_len))
+    return FALSE;
+
+  _dbus_string_set_byte (str, pos, new_len);
+
+  if (old_end_pos)
+    *old_end_pos = pos + 1 + old_len + 1;
+  if (new_end_pos)
+    *new_end_pos = pos + 1 + new_len + 1;
+
+  return TRUE;
+}
+
+/**
+ * Sets an existing basic type value to a new value.
+ * Arguments work the same way as _dbus_marshal_basic_type().
+ *
+ * @param str the string
+ * @param pos location of the current value
+ * @param type the type of the current and new values
+ * @param value the address of the new value
+ * @param byte_order byte order for marshaling
+ * @param old_end_pos location to store end position of the old value, or #NULL
+ * @param new_end_pos location to store end position of the new value, or #NULL
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_marshal_set_basic (DBusString       *str,
+                         int               pos,
+                         int               type,
+                         const void       *value,
+                         int               byte_order,
+                         int              *old_end_pos,
+                         int              *new_end_pos)
+{
+  const DBusBasicValue *vp;
+
+  vp = value;
+
+  switch (type)
+    {
+    case DBUS_TYPE_BYTE:
+      _dbus_string_set_byte (str, pos, vp->byt);
+      if (old_end_pos)
+        *old_end_pos = pos + 1;
+      if (new_end_pos)
+        *new_end_pos = pos + 1;
+      return TRUE;
+      break;
+    case DBUS_TYPE_INT16:
+    case DBUS_TYPE_UINT16:
+      pos = _DBUS_ALIGN_VALUE (pos, 2);
+      set_2_octets (str, pos, vp->u16, byte_order);
+      if (old_end_pos)
+        *old_end_pos = pos + 2;
+      if (new_end_pos)
+        *new_end_pos = pos + 2;
+      return TRUE;
+      break;
+    case DBUS_TYPE_BOOLEAN:
+    case DBUS_TYPE_INT32:
+    case DBUS_TYPE_UINT32:
+      pos = _DBUS_ALIGN_VALUE (pos, 4);
+      set_4_octets (str, pos, vp->u32, byte_order);
+      if (old_end_pos)
+        *old_end_pos = pos + 4;
+      if (new_end_pos)
+        *new_end_pos = pos + 4;
+      return TRUE;
+      break;
+    case DBUS_TYPE_INT64:
+    case DBUS_TYPE_UINT64:
+    case DBUS_TYPE_DOUBLE:
+      pos = _DBUS_ALIGN_VALUE (pos, 8);
+      set_8_octets (str, pos, *vp, byte_order);
+      if (old_end_pos)
+        *old_end_pos = pos + 8;
+      if (new_end_pos)
+        *new_end_pos = pos + 8;
+      return TRUE;
+      break;
+    case DBUS_TYPE_STRING:
+    case DBUS_TYPE_OBJECT_PATH:
+      pos = _DBUS_ALIGN_VALUE (pos, 4);
+      _dbus_assert (vp->str != NULL);
+      return set_string (str, pos, vp->str, byte_order,
+                         old_end_pos, new_end_pos);
+      break;
+    case DBUS_TYPE_SIGNATURE:
+      _dbus_assert (vp->str != NULL);
+      return set_signature (str, pos, vp->str, byte_order,
+                            old_end_pos, new_end_pos);
+      break;
+    default:
+      _dbus_assert_not_reached ("not a basic type");
+      return FALSE;
+      break;
+    }
+}
+
+/**
+ * Convenience function to demarshal a 32 bit unsigned integer.
+ *
+ * @param str the string containing the data
+ * @param byte_order the byte order
+ * @param pos the position in the string
+ * @param new_pos the new position of the string
+ * @returns the demarshaled integer.
+ */
+dbus_uint32_t
+_dbus_marshal_read_uint32  (const DBusString *str,
+                            int               pos,
+                            int               byte_order,
+                            int              *new_pos)
+{
+  pos = _DBUS_ALIGN_VALUE (pos, 4);
+
+  if (new_pos)
+    *new_pos = pos + 4;
+
+  _dbus_assert (pos + 4 <= _dbus_string_get_length (str));
+  
+  return _dbus_unpack_uint32 (byte_order,
+                              _dbus_string_get_const_data (str) + pos);
+}
+
+/**
+ * Demarshals a basic-typed value. The "value" pointer is always
+ * the address of a variable of the basic type. So e.g.
+ * if the basic type is "double" then the pointer is
+ * a double*, and if it's "char*" then the pointer is
+ * a "char**".
+ *
+ * A value of type #DBusBasicValue is guaranteed to be large enough to
+ * hold any of the types that may be returned, which is handy if you
+ * are trying to do things generically. For example you can pass
+ * a DBusBasicValue* in to this function, and then pass the same
+ * DBusBasicValue* in to _dbus_marshal_basic_type() in order to
+ * move a value from one place to another.
+ *
+ * @param str the string containing the data
+ * @param pos position in the string
+ * @param type type of value to demarshal
+ * @param value pointer to return value data
+ * @param byte_order the byte order
+ * @param new_pos pointer to update with new position, or #NULL
+ **/
+void
+_dbus_marshal_read_basic (const DBusString      *str,
+                          int                    pos,
+                          int                    type,
+                          void                  *value,
+                          int                    byte_order,
+                          int                   *new_pos)
+{
+  const char *str_data;
+
+  _dbus_assert (dbus_type_is_basic (type));
+
+  str_data = _dbus_string_get_const_data (str);
+
+  /* Below we volatile types to avoid aliasing issues;
+   * see http://bugs.freedesktop.org/show_bug.cgi?id=20137
+   */
+  
+  switch (type)
+    {
+    case DBUS_TYPE_BYTE:
+      {
+      volatile unsigned char *vp = value;
+      *vp = (unsigned char) _dbus_string_get_byte (str, pos);
+      (pos)++;
+      }
+      break;
+    case DBUS_TYPE_INT16:
+    case DBUS_TYPE_UINT16:
+      {
+      volatile dbus_uint16_t *vp = value;
+      pos = _DBUS_ALIGN_VALUE (pos, 2);
+      *vp = *(dbus_uint16_t *)(str_data + pos);
+      if (byte_order != DBUS_COMPILER_BYTE_ORDER)
+       *vp = DBUS_UINT16_SWAP_LE_BE (*vp);
+      pos += 2;
+      }
+      break;
+    case DBUS_TYPE_INT32:
+    case DBUS_TYPE_UINT32:
+    case DBUS_TYPE_BOOLEAN:
+      {
+      volatile dbus_uint32_t *vp = value;
+      pos = _DBUS_ALIGN_VALUE (pos, 4);
+      *vp = *(dbus_uint32_t *)(str_data + pos);
+      if (byte_order != DBUS_COMPILER_BYTE_ORDER)
+       *vp = DBUS_UINT32_SWAP_LE_BE (*vp);
+      pos += 4;
+      }
+      break;
+    case DBUS_TYPE_INT64:
+    case DBUS_TYPE_UINT64:
+    case DBUS_TYPE_DOUBLE:
+      {
+      volatile dbus_uint64_t *vp = value;
+      pos = _DBUS_ALIGN_VALUE (pos, 8);
+#ifdef DBUS_HAVE_INT64
+      if (byte_order != DBUS_COMPILER_BYTE_ORDER)
+        *vp = DBUS_UINT64_SWAP_LE_BE (*(dbus_uint64_t*)(str_data + pos));
+      else
+        *vp = *(dbus_uint64_t*)(str_data + pos);
+#else
+      *vp = *(DBus8ByteStruct*) (str_data + pos);
+      swap_8_octets (vp, byte_order);
+#endif
+      pos += 8;
+      }
+      break;
+    case DBUS_TYPE_STRING:
+    case DBUS_TYPE_OBJECT_PATH:
+      {
+        int len;
+        volatile char **vp = value;
+
+        len = _dbus_marshal_read_uint32 (str, pos, byte_order, &pos);
+
+        *vp = (char*) str_data + pos;
+
+        pos += len + 1; /* length plus nul */
+      }
+      break;
+    case DBUS_TYPE_SIGNATURE:
+      {
+        int len;
+        volatile char **vp = value;
+
+        len = _dbus_string_get_byte (str, pos);
+        pos += 1;
+
+        *vp = (char*) str_data + pos;
+
+        pos += len + 1; /* length plus nul */
+      }
+      break;
+    default:
+      _dbus_warn_check_failed ("type %s %d not a basic type\n",
+                               _dbus_type_to_string (type), type);
+      _dbus_assert_not_reached ("not a basic type");
+      break;
+    }
+
+  if (new_pos)
+    *new_pos = pos;
+}
+
+static dbus_bool_t
+marshal_2_octets (DBusString   *str,
+                  int           insert_at,
+                  dbus_uint16_t value,
+                  int           byte_order,
+                  int          *pos_after)
+{
+  dbus_bool_t retval;
+  int orig_len;
+
+  _dbus_assert (sizeof (value) == 2);
+
+  if (byte_order != DBUS_COMPILER_BYTE_ORDER)
+    value = DBUS_UINT16_SWAP_LE_BE (value);
+
+  orig_len = _dbus_string_get_length (str);
+
+  retval = _dbus_string_insert_2_aligned (str, insert_at,
+                                          (const unsigned char *)&value);
+
+  if (pos_after)
+    {
+      *pos_after = insert_at + (_dbus_string_get_length (str) - orig_len);
+      _dbus_assert (*pos_after <= _dbus_string_get_length (str));
+    }
+
+  return retval;
+}
+
+static dbus_bool_t
+marshal_4_octets (DBusString   *str,
+                  int           insert_at,
+                  dbus_uint32_t value,
+                  int           byte_order,
+                  int          *pos_after)
+{
+  dbus_bool_t retval;
+  int orig_len;
+
+  _dbus_assert (sizeof (value) == 4);
+
+  if (byte_order != DBUS_COMPILER_BYTE_ORDER)
+    value = DBUS_UINT32_SWAP_LE_BE (value);
+
+  orig_len = _dbus_string_get_length (str);
+
+  retval = _dbus_string_insert_4_aligned (str, insert_at,
+                                          (const unsigned char *)&value);
+
+  if (pos_after)
+    {
+      *pos_after = insert_at + (_dbus_string_get_length (str) - orig_len);
+      _dbus_assert (*pos_after <= _dbus_string_get_length (str));
+    }
+
+  return retval;
+}
+
+static dbus_bool_t
+marshal_8_octets (DBusString    *str,
+                  int            insert_at,
+                  DBusBasicValue value,
+                  int            byte_order,
+                  int           *pos_after)
+{
+  dbus_bool_t retval;
+  int orig_len;
+
+  _dbus_assert (sizeof (value) == 8);
+
+  swap_8_octets (&value, byte_order);
+
+  orig_len = _dbus_string_get_length (str);
+
+  retval = _dbus_string_insert_8_aligned (str, insert_at,
+                                          (const unsigned char *)&value);
+
+  if (pos_after)
+    *pos_after = insert_at + _dbus_string_get_length (str) - orig_len;
+
+  return retval;
+}
+
+enum
+  {
+    MARSHAL_AS_STRING,
+    MARSHAL_AS_SIGNATURE,
+    MARSHAL_AS_BYTE_ARRAY
+  };
+
+static dbus_bool_t
+marshal_len_followed_by_bytes (int                  marshal_as,
+                               DBusString          *str,
+                               int                  insert_at,
+                               const unsigned char *value,
+                               int                  data_len, /* doesn't include nul if any */
+                               int                  byte_order,
+                               int                 *pos_after)
+{
+  int pos;
+  DBusString value_str;
+  int value_len;
+
+  _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN || byte_order == DBUS_BIG_ENDIAN);
+  if (insert_at > _dbus_string_get_length (str))
+    _dbus_warn ("insert_at = %d string len = %d data_len = %d\n",
+                insert_at, _dbus_string_get_length (str), data_len);
+  
+  if (marshal_as == MARSHAL_AS_BYTE_ARRAY)
+    value_len = data_len;
+  else
+    value_len = data_len + 1; /* value has a nul */
+
+  _dbus_string_init_const_len (&value_str, value, value_len);
+
+  pos = insert_at;
+
+  if (marshal_as == MARSHAL_AS_SIGNATURE)
+    {
+      _dbus_assert (data_len <= DBUS_MAXIMUM_SIGNATURE_LENGTH);
+      _dbus_assert (data_len <= 255); /* same as max sig len right now */
+      
+      if (!_dbus_string_insert_byte (str, pos, data_len))
+        goto oom;
+
+      pos += 1;
+    }
+  else
+    {
+      if (!marshal_4_octets (str, pos, data_len,
+                             byte_order, &pos))
+        goto oom;
+    }
+
+  if (!_dbus_string_copy_len (&value_str, 0, value_len,
+                              str, pos))
+    goto oom;
+
+#if 0
+  /* too expensive */
+  _dbus_assert (_dbus_string_equal_substring (&value_str, 0, value_len,
+                                              str, pos));
+  _dbus_verbose_bytes_of_string (str, pos, value_len);
+#endif
+
+  pos += value_len;
+
+  if (pos_after)
+    *pos_after = pos;
+
+  return TRUE;
+
+ oom:
+  /* Delete what we've inserted */
+  _dbus_string_delete (str, insert_at, pos - insert_at);
+
+  return FALSE;
+}
+
+static dbus_bool_t
+marshal_string (DBusString    *str,
+                int            insert_at,
+                const char    *value,
+                int            byte_order,
+                int           *pos_after)
+{
+  return marshal_len_followed_by_bytes (MARSHAL_AS_STRING,
+                                        str, insert_at, value,
+                                        strlen (value),
+                                        byte_order, pos_after);
+}
+
+static dbus_bool_t
+marshal_signature (DBusString    *str,
+                   int            insert_at,
+                   const char    *value,
+                   int           *pos_after)
+{
+  return marshal_len_followed_by_bytes (MARSHAL_AS_SIGNATURE,
+                                        str, insert_at, value,
+                                        strlen (value),
+                                        DBUS_COMPILER_BYTE_ORDER, /* irrelevant */
+                                        pos_after);
+}
+
+/**
+ * Marshals a basic-typed value. The "value" pointer is always the
+ * address of a variable containing the basic type value.
+ * So for example for int32 it will be dbus_int32_t*, and
+ * for string it will be const char**. This is for symmetry
+ * with _dbus_marshal_read_basic() and to have a simple
+ * consistent rule.
+ *
+ * @param str string to marshal to
+ * @param insert_at where to insert the value
+ * @param type type of value
+ * @param value pointer to a variable containing the value
+ * @param byte_order byte order
+ * @param pos_after #NULL or the position after the type
+ * @returns #TRUE on success
+ **/
+dbus_bool_t
+_dbus_marshal_write_basic (DBusString *str,
+                           int         insert_at,
+                           int         type,
+                           const void *value,
+                           int         byte_order,
+                           int        *pos_after)
+{
+  const DBusBasicValue *vp;
+
+  _dbus_assert (dbus_type_is_basic (type));
+
+  vp = value;
+
+  switch (type)
+    {
+    case DBUS_TYPE_BYTE:
+      if (!_dbus_string_insert_byte (str, insert_at, vp->byt))
+        return FALSE;
+      if (pos_after)
+        *pos_after = insert_at + 1;
+      return TRUE;
+      break;
+    case DBUS_TYPE_INT16:
+    case DBUS_TYPE_UINT16:
+      return marshal_2_octets (str, insert_at, vp->u16,
+                               byte_order, pos_after);
+      break;
+    case DBUS_TYPE_BOOLEAN:
+      return marshal_4_octets (str, insert_at, vp->u32 != FALSE,
+                               byte_order, pos_after);
+      break;
+    case DBUS_TYPE_INT32:
+    case DBUS_TYPE_UINT32:
+      return marshal_4_octets (str, insert_at, vp->u32,
+                               byte_order, pos_after);
+      break;
+    case DBUS_TYPE_INT64:
+    case DBUS_TYPE_UINT64:
+    case DBUS_TYPE_DOUBLE:
+      return marshal_8_octets (str, insert_at, *vp, byte_order, pos_after);
+      break;
+
+    case DBUS_TYPE_STRING:
+    case DBUS_TYPE_OBJECT_PATH:
+      _dbus_assert (vp->str != NULL);
+      return marshal_string (str, insert_at, vp->str, byte_order, pos_after);
+      break;
+    case DBUS_TYPE_SIGNATURE:
+      _dbus_assert (vp->str != NULL);
+      return marshal_signature (str, insert_at, vp->str, pos_after);
+      break;
+    default:
+      _dbus_assert_not_reached ("not a basic type");
+      return FALSE;
+      break;
+    }
+}
+
+static dbus_bool_t
+marshal_1_octets_array (DBusString          *str,
+                        int                  insert_at,
+                        const unsigned char *value,
+                        int                  n_elements,
+                        int                  byte_order,
+                        int                 *pos_after)
+{
+  int pos;
+  DBusString value_str;
+
+  _dbus_string_init_const_len (&value_str, value, n_elements);
+
+  pos = insert_at;
+
+  if (!_dbus_string_copy_len (&value_str, 0, n_elements,
+                              str, pos))
+    return FALSE;
+
+  pos += n_elements;
+
+  if (pos_after)
+    *pos_after = pos;
+
+  return TRUE;
+}
+
+/**
+ * Swaps the elements of an array to the opposite byte order
+ *
+ * @param data start of array
+ * @param n_elements number of elements
+ * @param alignment size of each element
+ */
+void
+_dbus_swap_array (unsigned char *data,
+                  int            n_elements,
+                  int            alignment)
+{
+  unsigned char *d;
+  unsigned char *end;
+
+  _dbus_assert (_DBUS_ALIGN_ADDRESS (data, alignment) == data);
+
+  /* we use const_data and cast it off so DBusString can be a const string
+   * for the unit tests. don't ask.
+   */
+  d = data;
+  end = d + (n_elements * alignment);
+  
+  if (alignment == 8)
+    {
+      while (d != end)
+        {
+#ifdef DBUS_HAVE_INT64
+          *((dbus_uint64_t*)d) = DBUS_UINT64_SWAP_LE_BE (*((dbus_uint64_t*)d));
+#else
+          swap_8_bytes ((DBusBasicValue*) d);
+#endif
+          d += 8;
+        }
+    }
+  else if (alignment == 4)
+    {
+      while (d != end)
+        {
+          *((dbus_uint32_t*)d) = DBUS_UINT32_SWAP_LE_BE (*((dbus_uint32_t*)d));
+          d += 4;
+        }
+    }
+  else
+    {
+      _dbus_assert (alignment == 2);
+      
+      while (d != end)
+        {
+          *((dbus_uint16_t*)d) = DBUS_UINT16_SWAP_LE_BE (*((dbus_uint16_t*)d));
+          d += 2;
+        }
+    }
+}
+
+static void
+swap_array (DBusString *str,
+            int         array_start,
+            int         n_elements,
+            int         byte_order,
+            int         alignment)
+{
+  _dbus_assert (_DBUS_ALIGN_VALUE (array_start, alignment) == (unsigned) array_start);
+
+  if (byte_order != DBUS_COMPILER_BYTE_ORDER)
+    {
+      /* we use const_data and cast it off so DBusString can be a const string
+       * for the unit tests. don't ask.
+       */
+      _dbus_swap_array ((unsigned char*) (_dbus_string_get_const_data (str) + array_start),
+                        n_elements, alignment);
+    }
+}
+
+static dbus_bool_t
+marshal_fixed_multi (DBusString           *str,
+                     int                   insert_at,
+                     const DBusBasicValue *value,
+                     int                   n_elements,
+                     int                   byte_order,
+                     int                   alignment,
+                     int                  *pos_after)
+{
+  int old_string_len;
+  int array_start;
+  DBusString t;
+  int len_in_bytes;
+
+  _dbus_assert (n_elements <= DBUS_MAXIMUM_ARRAY_LENGTH / alignment);
+  
+  old_string_len = _dbus_string_get_length (str);
+
+  len_in_bytes = n_elements * alignment;
+  array_start = insert_at;
+  
+  /* Note that we do alignment padding unconditionally
+   * even if the array is empty; this means that
+   * padding + len is always equal to the number of bytes
+   * in the array.
+   */
+
+  if (!_dbus_string_insert_alignment (str, &array_start, alignment))
+    goto error;
+
+  _dbus_string_init_const_len (&t,
+                               (const unsigned char*) value,
+                               len_in_bytes);
+
+  if (!_dbus_string_copy (&t, 0,
+                          str, array_start))
+    goto error;
+
+  swap_array (str, array_start, n_elements, byte_order, alignment);
+
+  if (pos_after)
+    *pos_after = array_start + len_in_bytes;
+  
+  return TRUE;
+
+ error:
+  _dbus_string_delete (str, insert_at,
+                       _dbus_string_get_length (str) - old_string_len);
+
+  return FALSE;
+}
+
+/**
+ * Marshals a block of values of fixed-length type all at once, as an
+ * optimization.  dbus_type_is_fixed() returns #TRUE for fixed-length
+ * types, which are the basic types minus the string-like types.
+ *
+ * The value argument should be the adddress of an
+ * array, so e.g. "const dbus_uint32_t**"
+ *
+ * @param str string to marshal to
+ * @param insert_at where to insert the value
+ * @param element_type type of array elements
+ * @param value address of an array to marshal
+ * @param n_elements number of elements in the array
+ * @param byte_order byte order
+ * @param pos_after #NULL or the position after the type
+ * @returns #TRUE on success
+ **/
+dbus_bool_t
+_dbus_marshal_write_fixed_multi (DBusString *str,
+                                 int         insert_at,
+                                 int         element_type,
+                                 const void *value,
+                                 int         n_elements,
+                                 int         byte_order,
+                                 int        *pos_after)
+{
+  const void* vp = *(const DBusBasicValue**)value;
+  
+  _dbus_assert (dbus_type_is_fixed (element_type));
+  _dbus_assert (n_elements >= 0);
+
+#if 0
+  _dbus_verbose ("writing %d elements of %s\n",
+                 n_elements, _dbus_type_to_string (element_type));
+#endif
+  
+  switch (element_type)
+    {
+    case DBUS_TYPE_BYTE:
+      return marshal_1_octets_array (str, insert_at, vp, n_elements, byte_order, pos_after);
+      break;
+    case DBUS_TYPE_INT16:
+    case DBUS_TYPE_UINT16:
+      return marshal_fixed_multi (str, insert_at, vp, n_elements, byte_order, 2, pos_after);
+      /* FIXME: we canonicalize to 0 or 1 for the single boolean case
+       * should we here too ? */
+    case DBUS_TYPE_BOOLEAN:
+    case DBUS_TYPE_INT32:
+    case DBUS_TYPE_UINT32:
+      return marshal_fixed_multi (str, insert_at, vp, n_elements, byte_order, 4, pos_after);
+      break;
+    case DBUS_TYPE_INT64:
+    case DBUS_TYPE_UINT64:
+    case DBUS_TYPE_DOUBLE:
+      return marshal_fixed_multi (str, insert_at, vp, n_elements, byte_order, 8, pos_after);
+      break;
+
+    default:
+      _dbus_assert_not_reached ("non fixed type in array write");
+      break;
+    }
+
+  return FALSE;
+}
+
+
+/**
+ * Skips over a basic-typed value, reporting the following position.
+ *
+ * @param str the string containing the data
+ * @param type type of value to read
+ * @param byte_order the byte order
+ * @param pos pointer to position in the string,
+ *            updated on return to new position
+ **/
+void
+_dbus_marshal_skip_basic (const DBusString      *str,
+                          int                    type,
+                          int                    byte_order,
+                          int                   *pos)
+{
+  _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN ||
+                byte_order == DBUS_BIG_ENDIAN);
+  
+  switch (type)
+    {
+    case DBUS_TYPE_BYTE:
+      (*pos)++;
+      break;
+    case DBUS_TYPE_INT16:
+    case DBUS_TYPE_UINT16:
+      *pos = _DBUS_ALIGN_VALUE (*pos, 2);
+      *pos += 2;
+      break;
+    case DBUS_TYPE_BOOLEAN:
+    case DBUS_TYPE_INT32:
+    case DBUS_TYPE_UINT32:
+      *pos = _DBUS_ALIGN_VALUE (*pos, 4);
+      *pos += 4;
+      break;
+    case DBUS_TYPE_INT64:
+    case DBUS_TYPE_UINT64:
+    case DBUS_TYPE_DOUBLE:
+      *pos = _DBUS_ALIGN_VALUE (*pos, 8);
+      *pos += 8;
+      break;
+    case DBUS_TYPE_STRING:
+    case DBUS_TYPE_OBJECT_PATH:
+      {
+        int len;
+
+        len = _dbus_marshal_read_uint32 (str, *pos, byte_order, pos);
+        
+        *pos += len + 1; /* length plus nul */
+      }
+      break;
+    case DBUS_TYPE_SIGNATURE:
+      {
+        int len;
+
+        len = _dbus_string_get_byte (str, *pos);
+
+        *pos += len + 2; /* length byte plus length plus nul */
+      }
+      break;
+    default:
+      _dbus_warn ("type %s not a basic type\n",
+                  _dbus_type_to_string (type));
+      _dbus_assert_not_reached ("not a basic type");
+      break;
+    }
+}
+
+/**
+ * Skips an array, returning the next position.
+ *
+ * @param str the string containing the data
+ * @param element_type the type of array elements
+ * @param byte_order the byte order
+ * @param pos pointer to position in the string,
+ *            updated on return to new position
+ */
+void
+_dbus_marshal_skip_array (const DBusString  *str,
+                          int                element_type,
+                          int                byte_order,
+                          int               *pos)
+{
+  dbus_uint32_t array_len;
+  int i;
+  int alignment;
+
+  i = _DBUS_ALIGN_VALUE (*pos, 4);
+
+  array_len = _dbus_marshal_read_uint32 (str, i, byte_order, &i);
+
+  alignment = _dbus_type_get_alignment (element_type);
+
+  i = _DBUS_ALIGN_VALUE (i, alignment);
+
+  *pos = i + array_len;
+}
+
+/**
+ * Gets the alignment requirement for the given type;
+ * will be 1, 4, or 8.
+ *
+ * @param typecode the type
+ * @returns alignment of 1, 4, or 8
+ */
+int
+_dbus_type_get_alignment (int typecode)
+{
+  switch (typecode)
+    {
+    case DBUS_TYPE_BYTE:
+    case DBUS_TYPE_VARIANT:
+    case DBUS_TYPE_SIGNATURE:
+      return 1;
+    case DBUS_TYPE_INT16:
+    case DBUS_TYPE_UINT16:
+      return 2;
+    case DBUS_TYPE_BOOLEAN:
+    case DBUS_TYPE_INT32:
+    case DBUS_TYPE_UINT32:
+      /* this stuff is 4 since it starts with a length */
+    case DBUS_TYPE_STRING:
+    case DBUS_TYPE_OBJECT_PATH:
+    case DBUS_TYPE_ARRAY:
+      return 4;
+    case DBUS_TYPE_INT64:
+    case DBUS_TYPE_UINT64:
+    case DBUS_TYPE_DOUBLE:
+      /* struct is 8 since it could contain an 8-aligned item
+       * and it's simpler to just always align structs to 8;
+       * we want the amount of padding in a struct of a given
+       * type to be predictable, not location-dependent.
+       * DICT_ENTRY is always the same as struct.
+       */
+    case DBUS_TYPE_STRUCT:
+    case DBUS_TYPE_DICT_ENTRY:
+      return 8;
+
+    default:
+      _dbus_assert_not_reached ("unknown typecode in _dbus_type_get_alignment()");
+      return 0;
+    }
+}
+
+
+/**
+ * Return #TRUE if the typecode is a valid typecode.
+ * #DBUS_TYPE_INVALID surprisingly enough is not considered valid, and
+ * random unknown bytes aren't either. This function is safe with
+ * untrusted data.
+ *
+ * @returns #TRUE if valid
+ */
+dbus_bool_t
+_dbus_type_is_valid (int typecode)
+{
+  switch (typecode)
+    {
+    case DBUS_TYPE_BYTE:
+    case DBUS_TYPE_BOOLEAN:
+    case DBUS_TYPE_INT16:
+    case DBUS_TYPE_UINT16:
+    case DBUS_TYPE_INT32:
+    case DBUS_TYPE_UINT32:
+    case DBUS_TYPE_INT64:
+    case DBUS_TYPE_UINT64:
+    case DBUS_TYPE_DOUBLE:
+    case DBUS_TYPE_STRING:
+    case DBUS_TYPE_OBJECT_PATH:
+    case DBUS_TYPE_SIGNATURE:
+    case DBUS_TYPE_ARRAY:
+    case DBUS_TYPE_STRUCT:
+    case DBUS_TYPE_DICT_ENTRY:
+    case DBUS_TYPE_VARIANT:
+      return TRUE;
+
+    default:
+      return FALSE;
+    }
+}
+
+/**
+ * Returns a string describing the given type.
+ *
+ * @param typecode the type to describe
+ * @returns a constant string describing the type
+ */
+const char *
+_dbus_type_to_string (int typecode)
+{
+  switch (typecode)
+    {
+    case DBUS_TYPE_INVALID:
+      return "invalid";
+    case DBUS_TYPE_BOOLEAN:
+      return "boolean";
+    case DBUS_TYPE_BYTE:
+      return "byte";
+    case DBUS_TYPE_INT16:
+      return "int16";
+    case DBUS_TYPE_UINT16:
+      return "uint16";
+    case DBUS_TYPE_INT32:
+      return "int32";
+    case DBUS_TYPE_UINT32:
+      return "uint32";
+    case DBUS_TYPE_INT64:
+      return "int64";
+    case DBUS_TYPE_UINT64:
+      return "uint64";      
+    case DBUS_TYPE_DOUBLE:
+      return "double";
+    case DBUS_TYPE_STRING:
+      return "string";
+    case DBUS_TYPE_OBJECT_PATH:
+      return "object_path";
+    case DBUS_TYPE_SIGNATURE:
+      return "signature";
+    case DBUS_TYPE_STRUCT:
+      return "struct";
+    case DBUS_TYPE_DICT_ENTRY:
+      return "dict_entry";
+    case DBUS_TYPE_ARRAY:
+      return "array";
+    case DBUS_TYPE_VARIANT:
+      return "variant";
+    case DBUS_STRUCT_BEGIN_CHAR:
+      return "begin_struct";
+    case DBUS_STRUCT_END_CHAR:
+      return "end_struct";
+    case DBUS_DICT_ENTRY_BEGIN_CHAR:
+      return "begin_dict_entry";
+    case DBUS_DICT_ENTRY_END_CHAR:
+      return "end_dict_entry";
+    default:
+      return "unknown";
+    }
+}
+
+/**
+ * If in verbose mode, print a block of binary data.
+ *
+ * @param data the data
+ * @param len the length of the data
+ * @param offset where to start counting for byte indexes
+ */
+void
+_dbus_verbose_bytes (const unsigned char *data,
+                     int                  len,
+                     int                  offset)
+{
+  int i;
+  const unsigned char *aligned;
+
+  _dbus_assert (len >= 0);
+
+  if (!_dbus_is_verbose())
+    return;
+
+  /* Print blanks on first row if appropriate */
+  aligned = _DBUS_ALIGN_ADDRESS (data, 4);
+  if (aligned > data)
+    aligned -= 4;
+  _dbus_assert (aligned <= data);
+
+  if (aligned != data)
+    {
+      _dbus_verbose ("%4ld\t%p: ", - (long)(data - aligned), aligned);
+      while (aligned != data)
+        {
+          _dbus_verbose ("    ");
+          ++aligned;
+        }
+    }
+
+  /* now print the bytes */
+  i = 0;
+  while (i < len)
+    {
+      if (_DBUS_ALIGN_ADDRESS (&data[i], 4) == &data[i])
+        {
+          _dbus_verbose ("%4d\t%p: ",
+                         offset + i, &data[i]);
+        }
+
+      if (data[i] >= 32 &&
+          data[i] <= 126)
+        _dbus_verbose (" '%c' ", data[i]);
+      else
+        _dbus_verbose ("0x%s%x ",
+                       data[i] <= 0xf ? "0" : "", data[i]);
+
+      ++i;
+
+      if (_DBUS_ALIGN_ADDRESS (&data[i], 4) == &data[i])
+        {
+          if (i > 3)
+            _dbus_verbose ("BE: %d LE: %d",
+                           _dbus_unpack_uint32 (DBUS_BIG_ENDIAN, &data[i-4]),
+                           _dbus_unpack_uint32 (DBUS_LITTLE_ENDIAN, &data[i-4]));
+
+          if (i > 7 &&
+              _DBUS_ALIGN_ADDRESS (&data[i], 8) == &data[i])
+            {
+#ifdef DBUS_HAVE_INT64
+              /* I think I probably mean "GNU libc printf" and not "GNUC"
+               * but we'll wait until someone complains. If you hit this,
+               * just turn off verbose mode as a workaround.
+               */
+#if __GNUC__
+              _dbus_verbose (" u64: 0x%llx",
+                             *(dbus_uint64_t*)&data[i-8]);
+#endif
+#endif
+              _dbus_verbose (" dbl: %g",
+                             *(double*)&data[i-8]);
+            }
+
+          _dbus_verbose ("\n");
+        }
+    }
+
+  _dbus_verbose ("\n");
+}
+
+/**
+ * Dump the given part of the string to verbose log.
+ *
+ * @param str the string
+ * @param start the start of range to dump
+ * @param len length of range
+ */
+void
+_dbus_verbose_bytes_of_string (const DBusString    *str,
+                               int                  start,
+                               int                  len)
+{
+  const char *d;
+  int real_len;
+
+  real_len = _dbus_string_get_length (str);
+
+  _dbus_assert (start >= 0);
+
+  if (start > real_len)
+    {
+      _dbus_verbose ("  [%d,%d) is not inside string of length %d\n",
+                     start, len, real_len);
+      return;
+    }
+
+  if ((start + len) > real_len)
+    {
+      _dbus_verbose ("  [%d,%d) extends outside string of length %d\n",
+                     start, len, real_len);
+      len = real_len - start;
+    }
+
+  d = _dbus_string_get_const_data_len (str, start, len);
+
+  _dbus_verbose_bytes (d, len, start);
+}
+
+static int
+map_type_char_to_type (int t)
+{
+  if (t == DBUS_STRUCT_BEGIN_CHAR)
+    return DBUS_TYPE_STRUCT;
+  else if (t == DBUS_DICT_ENTRY_BEGIN_CHAR)
+    return DBUS_TYPE_DICT_ENTRY;
+  else
+    {
+      _dbus_assert (t != DBUS_STRUCT_END_CHAR);
+      _dbus_assert (t != DBUS_DICT_ENTRY_END_CHAR);
+      return t;
+    }
+}
+
+/**
+ * Get the first type in the signature. The difference between this
+ * and just getting the first byte of the signature is that you won't
+ * get DBUS_STRUCT_BEGIN_CHAR, you'll get DBUS_TYPE_STRUCT
+ * instead.
+ *
+ * @param str string containing signature
+ * @param pos where the signature starts
+ * @returns the first type in the signature
+ */
+int
+_dbus_first_type_in_signature (const DBusString *str,
+                               int               pos)
+{
+  return map_type_char_to_type (_dbus_string_get_byte (str, pos));
+}
+
+/**
+ * Similar to #_dbus_first_type_in_signature, but operates
+ * on a C string buffer.
+ *
+ * @param str a C string buffer
+ * @param pos where the signature starts
+ * @returns the first type in the signature
+ */
+int
+_dbus_first_type_in_signature_c_str (const char       *str,
+                                    int               pos)
+{
+  return map_type_char_to_type (str[pos]);
+}
+
+/** @} */
+
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-test.h"
+#include <stdio.h>
+
+/**
+ * Reads a block of fixed-length basic values, as an optimization
+ * vs. reading each one individually into a new buffer.
+ *
+ * This function returns the data in-place; it does not make a copy,
+ * and it does not swap the bytes.
+ *
+ * If you ask for #DBUS_TYPE_DOUBLE you will get a "const double*" back
+ * and the "value" argument should be a "const double**" and so on.
+ *
+ * @param str the string to read from
+ * @param pos position to read from
+ * @param element_type type of array elements
+ * @param value place to return the array
+ * @param n_elements number of array elements to read
+ * @param byte_order the byte order, used to read the array length
+ * @param new_pos #NULL or location to store a position after the elements
+ */
+void
+_dbus_marshal_read_fixed_multi  (const DBusString *str,
+                                 int               pos,
+                                 int               element_type,
+                                 void             *value,
+                                 int               n_elements,
+                                 int               byte_order,
+                                 int              *new_pos)
+{
+  int array_len;
+  int alignment;
+
+  _dbus_assert (dbus_type_is_fixed (element_type));
+  _dbus_assert (dbus_type_is_basic (element_type));
+
+#if 0
+  _dbus_verbose ("reading %d elements of %s\n",
+                 n_elements, _dbus_type_to_string (element_type));
+#endif
+  
+  alignment = _dbus_type_get_alignment (element_type);
+
+  pos = _DBUS_ALIGN_VALUE (pos, alignment);
+  
+  array_len = n_elements * alignment;
+
+  *(const DBusBasicValue**) value = (void*) _dbus_string_get_const_data_len (str, pos, array_len);
+  if (new_pos)
+    *new_pos = pos + array_len;
+}
+
+static void
+swap_test_array (void *array,
+                 int   len_bytes,
+                 int   byte_order,
+                 int   alignment)
+{
+  DBusString t;
+
+  if (alignment == 1)
+    return;
+  
+  _dbus_string_init_const_len (&t, array, len_bytes);
+  swap_array (&t, 0, len_bytes / alignment, byte_order, alignment);
+}
+
+#define MARSHAL_BASIC(typename, byte_order, literal)                    \
+  do {                                                                  \
+     v_##typename = literal;                                            \
+     if (!_dbus_marshal_write_basic (&str, pos, DBUS_TYPE_##typename,   \
+                                    &v_##typename,                      \
+                                    byte_order, NULL))                  \
+       _dbus_assert_not_reached ("no memory");                          \
+   } while (0)
+
+#define DEMARSHAL_BASIC(typename, byte_order)                                   \
+  do {                                                                          \
+    _dbus_marshal_read_basic (&str, pos, DBUS_TYPE_##typename, &v_##typename,   \
+                              byte_order, &pos);                                \
+  } while (0)
+
+#define DEMARSHAL_BASIC_AND_CHECK(typename, byte_order, literal)                        \
+  do {                                                                                  \
+    DEMARSHAL_BASIC (typename, byte_order);                                             \
+    if (literal != v_##typename)                                                        \
+      {                                                                                 \
+        _dbus_verbose_bytes_of_string (&str, dump_pos,                                  \
+                                     _dbus_string_get_length (&str) - dump_pos);        \
+        _dbus_assert_not_reached ("demarshaled wrong value");                           \
+      }                                                                                 \
+  } while (0)
+
+#define MARSHAL_TEST(typename, byte_order, literal)             \
+  do {                                                          \
+    MARSHAL_BASIC (typename, byte_order, literal);              \
+    dump_pos = pos;                                             \
+    DEMARSHAL_BASIC_AND_CHECK (typename, byte_order, literal);  \
+  } while (0)
+
+#define MARSHAL_TEST_STRCMP(typename, byte_order, literal)                              \
+  do {                                                                                  \
+    MARSHAL_BASIC (typename, byte_order, literal);                                      \
+    dump_pos = pos;                                                                     \
+    DEMARSHAL_BASIC (typename, byte_order);                                             \
+    if (strcmp (literal, v_##typename) != 0)                                            \
+      {                                                                                 \
+        _dbus_verbose_bytes_of_string (&str, dump_pos,                                  \
+                                       _dbus_string_get_length (&str) - dump_pos);      \
+        _dbus_warn ("literal '%s'\nvalue  '%s'\n", literal, v_##typename);              \
+        _dbus_assert_not_reached ("demarshaled wrong value");                           \
+      }                                                                                 \
+  } while (0)
+
+#define MARSHAL_FIXED_ARRAY(typename, byte_order, literal)                                      \
+  do {                                                                                          \
+     int next;                                                                                  \
+     v_UINT32 = sizeof(literal);                                                                \
+     if (!_dbus_marshal_write_basic (&str, pos, DBUS_TYPE_UINT32, &v_UINT32,                    \
+                                     byte_order, &next))                                        \
+       _dbus_assert_not_reached ("no memory");                                                  \
+     v_ARRAY_##typename = literal;                                                              \
+     if (!_dbus_marshal_write_fixed_multi (&str, next, DBUS_TYPE_##typename,                    \
+                                           &v_ARRAY_##typename, _DBUS_N_ELEMENTS(literal),      \
+                                           byte_order, NULL))                                   \
+       _dbus_assert_not_reached ("no memory");                                                  \
+   } while (0)
+
+#define DEMARSHAL_FIXED_ARRAY(typename, byte_order)                                             \
+  do {                                                                                          \
+    int next;                                                                                   \
+    alignment = _dbus_type_get_alignment (DBUS_TYPE_##typename);                                \
+    v_UINT32 = _dbus_marshal_read_uint32 (&str, dump_pos, byte_order, &next);                   \
+    _dbus_marshal_read_fixed_multi (&str, next, DBUS_TYPE_##typename, &v_ARRAY_##typename,      \
+                                    v_UINT32/alignment,                                         \
+                                    byte_order, NULL);                                          \
+    swap_test_array (v_ARRAY_##typename, v_UINT32,                                              \
+                     byte_order, alignment);                                                    \
+  } while (0)
+
+#define DEMARSHAL_FIXED_ARRAY_AND_CHECK(typename, byte_order, literal)                  \
+  do {                                                                                  \
+    DEMARSHAL_FIXED_ARRAY (typename, byte_order);                                       \
+    if (memcmp (literal, v_ARRAY_##typename, sizeof (literal) != 0))                    \
+      {                                                                                 \
+        _dbus_verbose ("MARSHALED DATA\n");                                             \
+        _dbus_verbose_bytes_of_string (&str, dump_pos,                                  \
+                                      _dbus_string_get_length (&str) - dump_pos);       \
+        _dbus_verbose ("LITERAL DATA\n");                                               \
+        _dbus_verbose_bytes ((char*)literal, sizeof (literal), 0);                      \
+        _dbus_verbose ("READ DATA\n");                                                  \
+        _dbus_verbose_bytes ((char*)v_ARRAY_##typename, sizeof (literal), 0);           \
+        _dbus_assert_not_reached ("demarshaled wrong fixed array value");               \
+      }                                                                                 \
+  } while (0)
+
+#define MARSHAL_TEST_FIXED_ARRAY(typename, byte_order, literal)         \
+  do {                                                                  \
+    MARSHAL_FIXED_ARRAY (typename, byte_order, literal);                \
+    dump_pos = pos;                                                     \
+    DEMARSHAL_FIXED_ARRAY_AND_CHECK (typename, byte_order, literal);    \
+  } while (0)
+
+dbus_bool_t
+_dbus_marshal_test (void)
+{
+  int alignment;
+  DBusString str;
+  int pos, dump_pos;
+  unsigned char array1[5] = { 3, 4, 0, 1, 9 };
+  dbus_int16_t array2[3] = { 124, 457, 780 };
+  dbus_int32_t array4[3] = { 123, 456, 789 };
+#ifdef DBUS_HAVE_INT64
+  dbus_int64_t array8[3] = { DBUS_INT64_CONSTANT (0x123ffffffff),
+                             DBUS_INT64_CONSTANT (0x456ffffffff),
+                             DBUS_INT64_CONSTANT (0x789ffffffff) };
+  dbus_int64_t *v_ARRAY_INT64;
+#endif
+  unsigned char *v_ARRAY_BYTE;
+  dbus_int16_t *v_ARRAY_INT16;
+  dbus_uint16_t *v_ARRAY_UINT16;
+  dbus_int32_t *v_ARRAY_INT32;
+  dbus_uint32_t *v_ARRAY_UINT32;
+  DBusString t;
+  double v_DOUBLE;
+  double t_DOUBLE;
+  dbus_int16_t v_INT16;
+  dbus_uint16_t v_UINT16;
+  dbus_int32_t v_INT32;
+  dbus_uint32_t v_UINT32;
+  dbus_int64_t v_INT64;
+  dbus_uint64_t v_UINT64;
+  unsigned char v_BYTE;
+  dbus_bool_t v_BOOLEAN;
+  const char *v_STRING;
+  const char *v_SIGNATURE;
+  const char *v_OBJECT_PATH;
+  int byte_order;
+
+  if (!_dbus_string_init (&str))
+    _dbus_assert_not_reached ("failed to init string");
+
+  pos = 0;
+
+  /* Marshal doubles */
+  MARSHAL_BASIC (DOUBLE, DBUS_BIG_ENDIAN, 3.14);
+  DEMARSHAL_BASIC (DOUBLE, DBUS_BIG_ENDIAN);
+  t_DOUBLE = 3.14;
+  if (!_DBUS_DOUBLES_BITWISE_EQUAL (t_DOUBLE, v_DOUBLE))
+    _dbus_assert_not_reached ("got wrong double value");
+
+  MARSHAL_BASIC (DOUBLE, DBUS_LITTLE_ENDIAN, 3.14);
+  DEMARSHAL_BASIC (DOUBLE, DBUS_LITTLE_ENDIAN);
+  t_DOUBLE = 3.14;
+  if (!_DBUS_DOUBLES_BITWISE_EQUAL (t_DOUBLE, v_DOUBLE))
+    _dbus_assert_not_reached ("got wrong double value");
+
+  /* Marshal signed 16 integers */
+  MARSHAL_TEST (INT16, DBUS_BIG_ENDIAN, -12345);
+  MARSHAL_TEST (INT16, DBUS_LITTLE_ENDIAN, -12345);
+
+  /* Marshal unsigned 16 integers */
+  MARSHAL_TEST (UINT16, DBUS_BIG_ENDIAN, 0x1234);
+  MARSHAL_TEST (UINT16, DBUS_LITTLE_ENDIAN, 0x1234);
+  
+  /* Marshal signed integers */
+  MARSHAL_TEST (INT32, DBUS_BIG_ENDIAN, -12345678);
+  MARSHAL_TEST (INT32, DBUS_LITTLE_ENDIAN, -12345678);
+
+  /* Marshal unsigned integers */
+  MARSHAL_TEST (UINT32, DBUS_BIG_ENDIAN, 0x12345678);
+  MARSHAL_TEST (UINT32, DBUS_LITTLE_ENDIAN, 0x12345678);
+
+#ifdef DBUS_HAVE_INT64
+  /* Marshal signed integers */
+  MARSHAL_TEST (INT64, DBUS_BIG_ENDIAN, DBUS_INT64_CONSTANT (-0x123456789abc7));
+  MARSHAL_TEST (INT64, DBUS_LITTLE_ENDIAN, DBUS_INT64_CONSTANT (-0x123456789abc7));
+
+  /* Marshal unsigned integers */
+  MARSHAL_TEST (UINT64, DBUS_BIG_ENDIAN, DBUS_UINT64_CONSTANT (0x123456789abc7));
+  MARSHAL_TEST (UINT64, DBUS_LITTLE_ENDIAN, DBUS_UINT64_CONSTANT (0x123456789abc7));
+#endif /* DBUS_HAVE_INT64 */
+
+  /* Marshal byte */
+  MARSHAL_TEST (BYTE, DBUS_BIG_ENDIAN, 5);
+  MARSHAL_TEST (BYTE, DBUS_LITTLE_ENDIAN, 5);
+
+  /* Marshal all possible bools! */
+  MARSHAL_TEST (BOOLEAN, DBUS_BIG_ENDIAN, FALSE);
+  MARSHAL_TEST (BOOLEAN, DBUS_LITTLE_ENDIAN, FALSE);
+  MARSHAL_TEST (BOOLEAN, DBUS_BIG_ENDIAN, TRUE);
+  MARSHAL_TEST (BOOLEAN, DBUS_LITTLE_ENDIAN, TRUE);
+
+  /* Marshal strings */
+  MARSHAL_TEST_STRCMP (STRING, DBUS_BIG_ENDIAN, "");
+  MARSHAL_TEST_STRCMP (STRING, DBUS_LITTLE_ENDIAN, "");
+  MARSHAL_TEST_STRCMP (STRING, DBUS_BIG_ENDIAN, "This is the dbus test string");
+  MARSHAL_TEST_STRCMP (STRING, DBUS_LITTLE_ENDIAN, "This is the dbus test string");
+
+  /* object paths */
+  MARSHAL_TEST_STRCMP (OBJECT_PATH, DBUS_BIG_ENDIAN, "/a/b/c");
+  MARSHAL_TEST_STRCMP (OBJECT_PATH, DBUS_LITTLE_ENDIAN, "/a/b/c");
+
+  /* signatures */
+  MARSHAL_TEST_STRCMP (SIGNATURE, DBUS_BIG_ENDIAN, "");
+  MARSHAL_TEST_STRCMP (SIGNATURE, DBUS_LITTLE_ENDIAN, "");
+  MARSHAL_TEST_STRCMP (SIGNATURE, DBUS_BIG_ENDIAN, "a(ii)");
+  MARSHAL_TEST_STRCMP (SIGNATURE, DBUS_LITTLE_ENDIAN, "a(ii)");
+
+  /* Arrays */
+  MARSHAL_TEST_FIXED_ARRAY (INT16, DBUS_BIG_ENDIAN, array2);
+  MARSHAL_TEST_FIXED_ARRAY (INT16, DBUS_LITTLE_ENDIAN, array2);
+  MARSHAL_TEST_FIXED_ARRAY (UINT16, DBUS_BIG_ENDIAN, array2);
+  MARSHAL_TEST_FIXED_ARRAY (UINT16, DBUS_LITTLE_ENDIAN, array2);
+  
+  MARSHAL_TEST_FIXED_ARRAY (INT32, DBUS_BIG_ENDIAN, array4);
+  MARSHAL_TEST_FIXED_ARRAY (INT32, DBUS_LITTLE_ENDIAN, array4);
+  MARSHAL_TEST_FIXED_ARRAY (UINT32, DBUS_BIG_ENDIAN, array4);
+  MARSHAL_TEST_FIXED_ARRAY (UINT32, DBUS_LITTLE_ENDIAN, array4);
+
+  MARSHAL_TEST_FIXED_ARRAY (BYTE, DBUS_BIG_ENDIAN, array1);
+  MARSHAL_TEST_FIXED_ARRAY (BYTE, DBUS_LITTLE_ENDIAN, array1);
+  
+#ifdef DBUS_HAVE_INT64
+  MARSHAL_TEST_FIXED_ARRAY (INT64, DBUS_BIG_ENDIAN, array8);
+  MARSHAL_TEST_FIXED_ARRAY (INT64, DBUS_LITTLE_ENDIAN, array8);
+#endif
+
+#if 0
+
+  /*
+   * FIXME restore the set/pack tests
+   */
+
+#ifdef DBUS_HAVE_INT64
+  /* set/pack 64-bit integers */
+  _dbus_string_set_length (&str, 8);
+
+  /* signed little */
+  _dbus_marshal_set_int64 (&str, DBUS_LITTLE_ENDIAN,
+                           0, DBUS_INT64_CONSTANT (-0x123456789abc7));
+
+  _dbus_assert (DBUS_INT64_CONSTANT (-0x123456789abc7) ==
+                _dbus_unpack_int64 (DBUS_LITTLE_ENDIAN,
+                                    _dbus_string_get_const_data (&str)));
+
+  /* signed big */
+  _dbus_marshal_set_int64 (&str, DBUS_BIG_ENDIAN,
+                           0, DBUS_INT64_CONSTANT (-0x123456789abc7));
+
+  _dbus_assert (DBUS_INT64_CONSTANT (-0x123456789abc7) ==
+                _dbus_unpack_int64 (DBUS_BIG_ENDIAN,
+                                    _dbus_string_get_const_data (&str)));
+
+  /* signed little pack */
+  _dbus_pack_int64 (DBUS_INT64_CONSTANT (-0x123456789abc7),
+                    DBUS_LITTLE_ENDIAN,
+                    _dbus_string_get_data (&str));
+
+  _dbus_assert (DBUS_INT64_CONSTANT (-0x123456789abc7) ==
+                _dbus_unpack_int64 (DBUS_LITTLE_ENDIAN,
+                                    _dbus_string_get_const_data (&str)));
+
+  /* signed big pack */
+  _dbus_pack_int64 (DBUS_INT64_CONSTANT (-0x123456789abc7),
+                    DBUS_BIG_ENDIAN,
+                    _dbus_string_get_data (&str));
+
+  _dbus_assert (DBUS_INT64_CONSTANT (-0x123456789abc7) ==
+                _dbus_unpack_int64 (DBUS_BIG_ENDIAN,
+                                    _dbus_string_get_const_data (&str)));
+
+  /* unsigned little */
+  _dbus_marshal_set_uint64 (&str, DBUS_LITTLE_ENDIAN,
+                            0, DBUS_UINT64_CONSTANT (0x123456789abc7));
+
+  _dbus_assert (DBUS_UINT64_CONSTANT (0x123456789abc7) ==
+                _dbus_unpack_uint64 (DBUS_LITTLE_ENDIAN,
+                                     _dbus_string_get_const_data (&str)));
+
+  /* unsigned big */
+  _dbus_marshal_set_uint64 (&str, DBUS_BIG_ENDIAN,
+                            0, DBUS_UINT64_CONSTANT (0x123456789abc7));
+
+  _dbus_assert (DBUS_UINT64_CONSTANT (0x123456789abc7) ==
+                _dbus_unpack_uint64 (DBUS_BIG_ENDIAN,
+                                     _dbus_string_get_const_data (&str)));
+
+  /* unsigned little pack */
+  _dbus_pack_uint64 (DBUS_UINT64_CONSTANT (0x123456789abc7),
+                     DBUS_LITTLE_ENDIAN,
+                     _dbus_string_get_data (&str));
+
+  _dbus_assert (DBUS_UINT64_CONSTANT (0x123456789abc7) ==
+                _dbus_unpack_uint64 (DBUS_LITTLE_ENDIAN,
+                                     _dbus_string_get_const_data (&str)));
+
+  /* unsigned big pack */
+  _dbus_pack_uint64 (DBUS_UINT64_CONSTANT (0x123456789abc7),
+                     DBUS_BIG_ENDIAN,
+                     _dbus_string_get_data (&str));
+
+  _dbus_assert (DBUS_UINT64_CONSTANT (0x123456789abc7) ==
+                _dbus_unpack_uint64 (DBUS_BIG_ENDIAN,
+                                     _dbus_string_get_const_data (&str)));
+#endif /* DBUS_HAVE_INT64 */
+
+  /* set/pack 32-bit integers */
+  _dbus_string_set_length (&str, 4);
+
+  /* signed little */
+  _dbus_marshal_set_int32 (&str, DBUS_LITTLE_ENDIAN,
+                           0, -0x123456);
+
+  _dbus_assert (-0x123456 ==
+                _dbus_unpack_int32 (DBUS_LITTLE_ENDIAN,
+                                    _dbus_string_get_const_data (&str)));
+
+  /* signed big */
+  _dbus_marshal_set_int32 (&str, DBUS_BIG_ENDIAN,
+                           0, -0x123456);
+
+  _dbus_assert (-0x123456 ==
+                _dbus_unpack_int32 (DBUS_BIG_ENDIAN,
+                                    _dbus_string_get_const_data (&str)));
+
+  /* signed little pack */
+  _dbus_pack_int32 (-0x123456,
+                    DBUS_LITTLE_ENDIAN,
+                    _dbus_string_get_data (&str));
+
+  _dbus_assert (-0x123456 ==
+                _dbus_unpack_int32 (DBUS_LITTLE_ENDIAN,
+                                    _dbus_string_get_const_data (&str)));
+
+  /* signed big pack */
+  _dbus_pack_int32 (-0x123456,
+                    DBUS_BIG_ENDIAN,
+                    _dbus_string_get_data (&str));
+
+  _dbus_assert (-0x123456 ==
+                _dbus_unpack_int32 (DBUS_BIG_ENDIAN,
+                                    _dbus_string_get_const_data (&str)));
+
+  /* unsigned little */
+  _dbus_marshal_set_uint32 (&str,
+                            0, 0x123456,
+                            DBUS_LITTLE_ENDIAN);
+
+  _dbus_assert (0x123456 ==
+                _dbus_unpack_uint32 (DBUS_LITTLE_ENDIAN,
+                                     _dbus_string_get_const_data (&str)));
+
+  /* unsigned big */
+  _dbus_marshal_set_uint32 (&str,
+                            0, 0x123456,
+                            DBUS_BIG_ENDIAN);
+
+  _dbus_assert (0x123456 ==
+                _dbus_unpack_uint32 (DBUS_BIG_ENDIAN,
+                                     _dbus_string_get_const_data (&str)));
+
+  /* unsigned little pack */
+  _dbus_pack_uint32 (0x123456,
+                     DBUS_LITTLE_ENDIAN,
+                     _dbus_string_get_data (&str));
+
+  _dbus_assert (0x123456 ==
+                _dbus_unpack_uint32 (DBUS_LITTLE_ENDIAN,
+                                     _dbus_string_get_const_data (&str)));
+
+  /* unsigned big pack */
+  _dbus_pack_uint32 (0x123456,
+                     DBUS_BIG_ENDIAN,
+                     _dbus_string_get_data (&str));
+
+  _dbus_assert (0x123456 ==
+                _dbus_unpack_uint32 (DBUS_BIG_ENDIAN,
+                                     _dbus_string_get_const_data (&str)));
+
+#endif /* set/pack tests for integers */
+
+  /* Strings in-place set */
+  byte_order = DBUS_LITTLE_ENDIAN;
+  while (TRUE)
+    {
+      /* Init a string */
+      _dbus_string_set_length (&str, 0);
+
+      /* reset pos for the macros */
+      pos = 0;
+
+      MARSHAL_TEST_STRCMP (STRING, byte_order, "Hello world");
+
+      /* Set it to something longer */
+      _dbus_string_init_const (&t, "Hello world foo");
+
+      v_STRING = _dbus_string_get_const_data (&t);
+      _dbus_marshal_set_basic (&str, 0, DBUS_TYPE_STRING,
+                               &v_STRING, byte_order, NULL, NULL);
+
+      _dbus_marshal_read_basic (&str, 0, DBUS_TYPE_STRING,
+                                &v_STRING, byte_order,
+                                NULL);
+      _dbus_assert (strcmp (v_STRING, "Hello world foo") == 0);
+
+      /* Set it to something shorter */
+      _dbus_string_init_const (&t, "Hello");
+
+      v_STRING = _dbus_string_get_const_data (&t);
+      _dbus_marshal_set_basic (&str, 0, DBUS_TYPE_STRING,
+                               &v_STRING, byte_order, NULL, NULL);
+      _dbus_marshal_read_basic (&str, 0, DBUS_TYPE_STRING,
+                                &v_STRING, byte_order,
+                                NULL);
+      _dbus_assert (strcmp (v_STRING, "Hello") == 0);
+
+      /* Do the other byte order */
+      if (byte_order == DBUS_LITTLE_ENDIAN)
+        byte_order = DBUS_BIG_ENDIAN;
+      else
+        break;
+    }
+
+  /* Clean up */
+  _dbus_string_free (&str);
+
+  return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-marshal-basic.h b/src/dbus/dbus-marshal-basic.h
new file mode 100644 (file)
index 0000000..28c751f
--- /dev/null
@@ -0,0 +1,261 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-basic.h  Marshalling routines for basic (primitive) types
+ *
+ * Copyright (C) 2002  CodeFactory AB
+ * Copyright (C) 2004, 2005  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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_MARSHAL_BASIC_H
+#define DBUS_MARSHAL_BASIC_H
+
+#include <config.h>
+#include <dbus/dbus-protocol.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-arch-deps.h>
+#include <dbus/dbus-string.h>
+
+#ifndef PACKAGE
+#error "config.h not included here"
+#endif
+
+#ifdef WORDS_BIGENDIAN
+#define DBUS_COMPILER_BYTE_ORDER DBUS_BIG_ENDIAN
+#else
+#define DBUS_COMPILER_BYTE_ORDER DBUS_LITTLE_ENDIAN
+#endif
+
+#define DBUS_UINT16_SWAP_LE_BE_CONSTANT(val)   ((dbus_uint16_t) (      \
+    (dbus_uint16_t) ((dbus_uint16_t) (val) >> 8) |                      \
+    (dbus_uint16_t) ((dbus_uint16_t) (val) << 8)))
+
+#define DBUS_UINT32_SWAP_LE_BE_CONSTANT(val)   ((dbus_uint32_t) (      \
+    (((dbus_uint32_t) (val) & (dbus_uint32_t) 0x000000ffU) << 24) |     \
+    (((dbus_uint32_t) (val) & (dbus_uint32_t) 0x0000ff00U) <<  8) |     \
+    (((dbus_uint32_t) (val) & (dbus_uint32_t) 0x00ff0000U) >>  8) |     \
+    (((dbus_uint32_t) (val) & (dbus_uint32_t) 0xff000000U) >> 24)))
+
+#ifdef DBUS_HAVE_INT64
+
+#define DBUS_UINT64_SWAP_LE_BE_CONSTANT(val)   ((dbus_uint64_t) (              \
+      (((dbus_uint64_t) (val) &                                                 \
+       (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x00000000000000ff)) << 56) |    \
+      (((dbus_uint64_t) (val) &                                                 \
+       (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x000000000000ff00)) << 40) |    \
+      (((dbus_uint64_t) (val) &                                                 \
+       (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x0000000000ff0000)) << 24) |    \
+      (((dbus_uint64_t) (val) &                                                 \
+       (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x00000000ff000000)) <<  8) |    \
+      (((dbus_uint64_t) (val) &                                                 \
+       (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x000000ff00000000)) >>  8) |    \
+      (((dbus_uint64_t) (val) &                                                 \
+       (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x0000ff0000000000)) >> 24) |    \
+      (((dbus_uint64_t) (val) &                                                 \
+       (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x00ff000000000000)) >> 40) |    \
+      (((dbus_uint64_t) (val) &                                                 \
+       (dbus_uint64_t) DBUS_UINT64_CONSTANT (0xff00000000000000)) >> 56)))
+#endif /* DBUS_HAVE_INT64 */
+
+#define DBUS_UINT16_SWAP_LE_BE(val) (DBUS_UINT16_SWAP_LE_BE_CONSTANT (val))
+#define DBUS_INT16_SWAP_LE_BE(val)  ((dbus_int16_t)DBUS_UINT16_SWAP_LE_BE_CONSTANT (val))
+
+#define DBUS_UINT32_SWAP_LE_BE(val) (DBUS_UINT32_SWAP_LE_BE_CONSTANT (val))
+#define DBUS_INT32_SWAP_LE_BE(val)  ((dbus_int32_t)DBUS_UINT32_SWAP_LE_BE_CONSTANT (val))
+
+#ifdef DBUS_HAVE_INT64
+#  define DBUS_UINT64_SWAP_LE_BE(val) (DBUS_UINT64_SWAP_LE_BE_CONSTANT (val))
+#  define DBUS_INT64_SWAP_LE_BE(val)  ((dbus_int64_t)DBUS_UINT64_SWAP_LE_BE_CONSTANT (val))
+#endif /* DBUS_HAVE_INT64 */
+
+#ifdef WORDS_BIGENDIAN
+
+#  define DBUS_INT16_TO_BE(val)        ((dbus_int16_t) (val))
+#  define DBUS_UINT16_TO_BE(val)       ((dbus_uint16_t) (val))
+#  define DBUS_INT16_TO_LE(val)        (DBUS_INT16_SWAP_LE_BE (val))
+#  define DBUS_UINT16_TO_LE(val)       (DBUS_UINT16_SWAP_LE_BE (val))
+#  define DBUS_INT32_TO_BE(val)        ((dbus_int32_t) (val))
+#  define DBUS_UINT32_TO_BE(val)       ((dbus_uint32_t) (val))
+#  define DBUS_INT32_TO_LE(val)        (DBUS_INT32_SWAP_LE_BE (val))
+#  define DBUS_UINT32_TO_LE(val)       (DBUS_UINT32_SWAP_LE_BE (val))
+#  ifdef DBUS_HAVE_INT64
+#    define DBUS_INT64_TO_BE(val)      ((dbus_int64_t) (val))
+#    define DBUS_UINT64_TO_BE(val)     ((dbus_uint64_t) (val))
+#    define DBUS_INT64_TO_LE(val)      (DBUS_INT64_SWAP_LE_BE (val))
+#    define DBUS_UINT64_TO_LE(val)     (DBUS_UINT64_SWAP_LE_BE (val))
+#  endif /* DBUS_HAVE_INT64 */
+
+#else /* WORDS_BIGENDIAN */
+
+#  define DBUS_INT16_TO_LE(val)        ((dbus_int16_t) (val))
+#  define DBUS_UINT16_TO_LE(val)       ((dbus_uint16_t) (val))
+#  define DBUS_INT16_TO_BE(val)        ((dbus_int16_t) DBUS_UINT16_SWAP_LE_BE (val))
+#  define DBUS_UINT16_TO_BE(val)       (DBUS_UINT16_SWAP_LE_BE (val))
+#  define DBUS_INT32_TO_LE(val)        ((dbus_int32_t) (val))
+#  define DBUS_UINT32_TO_LE(val)       ((dbus_uint32_t) (val))
+#  define DBUS_INT32_TO_BE(val)        ((dbus_int32_t) DBUS_UINT32_SWAP_LE_BE (val))
+#  define DBUS_UINT32_TO_BE(val)       (DBUS_UINT32_SWAP_LE_BE (val))
+#  ifdef DBUS_HAVE_INT64
+#    define DBUS_INT64_TO_LE(val)      ((dbus_int64_t) (val))
+#    define DBUS_UINT64_TO_LE(val)     ((dbus_uint64_t) (val))
+#    define DBUS_INT64_TO_BE(val)      ((dbus_int64_t) DBUS_UINT64_SWAP_LE_BE (val))
+#    define DBUS_UINT64_TO_BE(val)     (DBUS_UINT64_SWAP_LE_BE (val))
+#  endif /* DBUS_HAVE_INT64 */
+#endif
+
+/* The transformation is symmetric, so the FROM just maps to the TO. */
+#define DBUS_INT16_FROM_LE(val)         (DBUS_INT16_TO_LE (val))
+#define DBUS_UINT16_FROM_LE(val) (DBUS_UINT16_TO_LE (val))
+#define DBUS_INT16_FROM_BE(val)         (DBUS_INT16_TO_BE (val))
+#define DBUS_UINT16_FROM_BE(val) (DBUS_UINT16_TO_BE (val))
+#define DBUS_INT32_FROM_LE(val)         (DBUS_INT32_TO_LE (val))
+#define DBUS_UINT32_FROM_LE(val) (DBUS_UINT32_TO_LE (val))
+#define DBUS_INT32_FROM_BE(val)         (DBUS_INT32_TO_BE (val))
+#define DBUS_UINT32_FROM_BE(val) (DBUS_UINT32_TO_BE (val))
+#ifdef DBUS_HAVE_INT64
+#  define DBUS_INT64_FROM_LE(val)       (DBUS_INT64_TO_LE (val))
+#  define DBUS_UINT64_FROM_LE(val) (DBUS_UINT64_TO_LE (val))
+#  define DBUS_INT64_FROM_BE(val)       (DBUS_INT64_TO_BE (val))
+#  define DBUS_UINT64_FROM_BE(val) (DBUS_UINT64_TO_BE (val))
+#endif /* DBUS_HAVE_INT64 */
+
+#ifndef DBUS_HAVE_INT64
+/**
+ * An 8-byte struct you could use to access int64 without having
+ * int64 support
+ */
+typedef struct
+{
+  dbus_uint32_t first32;  /**< first 32 bits in the 8 bytes (beware endian issues) */
+  dbus_uint32_t second32; /**< second 32 bits in the 8 bytes (beware endian issues) */
+} DBus8ByteStruct;
+#endif /* DBUS_HAVE_INT64 */
+
+/**
+ * A simple 8-byte value union that lets you access 8 bytes as if they
+ * were various types; useful when dealing with basic types via
+ * void pointers and varargs.
+ */
+typedef union
+{
+  dbus_int16_t  i16;   /**< as int16 */
+  dbus_uint16_t u16;   /**< as int16 */
+  dbus_int32_t  i32;   /**< as int32 */
+  dbus_uint32_t u32;   /**< as int32 */
+#ifdef DBUS_HAVE_INT64
+  dbus_int64_t  i64;   /**< as int64 */
+  dbus_uint64_t u64;   /**< as int64 */
+#else
+  DBus8ByteStruct u64; /**< as 8-byte-struct */
+#endif
+  double dbl;          /**< as double */
+  unsigned char byt;   /**< as byte */
+  char *str;           /**< as char* */
+} DBusBasicValue;
+
+#ifdef DBUS_DISABLE_ASSERT
+#define _dbus_unpack_uint16(byte_order, data)           \
+   (((byte_order) == DBUS_LITTLE_ENDIAN) ?              \
+     DBUS_UINT16_FROM_LE (*(dbus_uint16_t*)(data)) :    \
+     DBUS_UINT16_FROM_BE (*(dbus_uint16_t*)(data)))
+
+#define _dbus_unpack_uint32(byte_order, data)           \
+   (((byte_order) == DBUS_LITTLE_ENDIAN) ?              \
+     DBUS_UINT32_FROM_LE (*(dbus_uint32_t*)(data)) :    \
+     DBUS_UINT32_FROM_BE (*(dbus_uint32_t*)(data)))
+#endif
+
+#ifndef _dbus_unpack_uint16
+dbus_uint16_t _dbus_unpack_uint16 (int                  byte_order,
+                                   const unsigned char *data);
+#endif
+
+void          _dbus_pack_uint32   (dbus_uint32_t        value,
+                                   int                  byte_order,
+                                   unsigned char       *data);
+#ifndef _dbus_unpack_uint32
+dbus_uint32_t _dbus_unpack_uint32 (int                  byte_order,
+                                   const unsigned char *data);
+#endif
+
+dbus_bool_t   _dbus_marshal_set_basic         (DBusString       *str,
+                                               int               pos,
+                                               int               type,
+                                               const void       *value,
+                                               int               byte_order,
+                                               int              *old_end_pos,
+                                               int              *new_end_pos);
+dbus_bool_t   _dbus_marshal_write_basic       (DBusString       *str,
+                                               int               insert_at,
+                                               int               type,
+                                               const void       *value,
+                                               int               byte_order,
+                                               int              *pos_after);
+dbus_bool_t   _dbus_marshal_write_fixed_multi (DBusString       *str,
+                                               int               insert_at,
+                                               int               element_type,
+                                               const void       *value,
+                                               int               n_elements,
+                                               int               byte_order,
+                                               int              *pos_after);
+void          _dbus_marshal_read_basic        (const DBusString *str,
+                                               int               pos,
+                                               int               type,
+                                               void             *value,
+                                               int               byte_order,
+                                               int              *new_pos);
+void          _dbus_marshal_read_fixed_multi  (const DBusString *str,
+                                               int               pos,
+                                               int               element_type,
+                                               void             *value,
+                                               int               n_elements,
+                                               int               byte_order,
+                                               int              *new_pos);
+void          _dbus_marshal_skip_basic        (const DBusString *str,
+                                               int               type,
+                                               int               byte_order,
+                                               int              *pos);
+void          _dbus_marshal_skip_array        (const DBusString *str,
+                                               int               element_type,
+                                               int               byte_order,
+                                               int              *pos);
+void          _dbus_marshal_set_uint32        (DBusString       *str,
+                                               int               pos,
+                                               dbus_uint32_t     value,
+                                               int               byte_order);
+dbus_uint32_t _dbus_marshal_read_uint32       (const DBusString *str,
+                                               int               pos,
+                                               int               byte_order,
+                                               int              *new_pos);
+dbus_bool_t   _dbus_type_is_valid             (int               typecode);
+int           _dbus_type_get_alignment        (int               typecode);
+dbus_bool_t   _dbus_type_is_fixed             (int               typecode);
+int           _dbus_type_get_alignment        (int               typecode);
+const char*   _dbus_type_to_string            (int               typecode);
+
+int           _dbus_first_type_in_signature   (const DBusString *str,
+                                               int               pos);
+
+int           _dbus_first_type_in_signature_c_str   (const char       *str,
+                                                    int               pos);
+
+void _dbus_swap_array (unsigned char *data,
+                       int            n_elements,
+                       int            alignment);
+
+#endif /* DBUS_MARSHAL_BASIC_H */
diff --git a/src/dbus/dbus-marshal-byteswap-util.c b/src/dbus/dbus-marshal-byteswap-util.c
new file mode 100644 (file)
index 0000000..135852e
--- /dev/null
@@ -0,0 +1,105 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-byteswap-util.c  Would be in dbus-marshal-byteswap.c but tests/bus only
+ *
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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 <config.h>
+
+#ifdef DBUS_BUILD_TESTS 
+#include "dbus-marshal-byteswap.h"
+#include "dbus-test.h"
+#include <stdio.h>
+
+static void
+do_byteswap_test (int byte_order)
+{
+  int sequence;
+  DBusString signature;
+  DBusString body;
+  int opposite_order;
+
+  if (!_dbus_string_init (&signature) || !_dbus_string_init (&body))
+    _dbus_assert_not_reached ("oom");
+
+  opposite_order = byte_order == DBUS_LITTLE_ENDIAN ? DBUS_BIG_ENDIAN : DBUS_LITTLE_ENDIAN;
+  
+  sequence = 0;
+  while (dbus_internal_do_not_use_generate_bodies (sequence,
+                                                   byte_order,
+                                                   &signature, &body))
+    {
+      DBusString copy;
+      DBusTypeReader body_reader;
+      DBusTypeReader copy_reader;
+
+      if (!_dbus_string_init (&copy))
+        _dbus_assert_not_reached ("oom");
+
+      if (!_dbus_string_copy (&body, 0, &copy, 0))
+        _dbus_assert_not_reached ("oom");
+
+      _dbus_marshal_byteswap (&signature, 0,
+                              byte_order,
+                              opposite_order,
+                              &copy, 0);
+
+      _dbus_type_reader_init (&body_reader, byte_order, &signature, 0,
+                              &body, 0);
+      _dbus_type_reader_init (&copy_reader, opposite_order, &signature, 0,
+                              &copy, 0);
+      
+      if (!_dbus_type_reader_equal_values (&body_reader, &copy_reader))
+        {
+          _dbus_verbose_bytes_of_string (&signature, 0,
+                                         _dbus_string_get_length (&signature));
+          _dbus_verbose_bytes_of_string (&body, 0,
+                                         _dbus_string_get_length (&body));
+          _dbus_verbose_bytes_of_string (&copy, 0,
+                                         _dbus_string_get_length (&copy));
+
+          _dbus_warn ("Byte-swapped data did not have same values as original data\n");
+          _dbus_assert_not_reached ("test failed");
+        }
+      
+      _dbus_string_free (&copy);
+      
+      _dbus_string_set_length (&signature, 0);
+      _dbus_string_set_length (&body, 0);
+      ++sequence;
+    }
+
+  _dbus_string_free (&signature);
+  _dbus_string_free (&body);
+
+  printf ("  %d blocks swapped from order '%c' to '%c'\n",
+          sequence, byte_order, opposite_order);
+}
+
+dbus_bool_t
+_dbus_marshal_byteswap_test (void)
+{
+  do_byteswap_test (DBUS_LITTLE_ENDIAN);
+  do_byteswap_test (DBUS_BIG_ENDIAN);
+
+  return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-marshal-byteswap.c b/src/dbus/dbus-marshal-byteswap.c
new file mode 100644 (file)
index 0000000..6c9fff5
--- /dev/null
@@ -0,0 +1,246 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-byteswap.c  Swap a block of marshaled data
+ *
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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-marshal-byteswap.h"
+#include "dbus-marshal-basic.h"
+#include "dbus-signature.h"
+
+/**
+ * @addtogroup DBusMarshal
+ * @{
+ */
+
+static void
+byteswap_body_helper (DBusTypeReader       *reader,
+                      dbus_bool_t           walk_reader_to_end,
+                      int                   old_byte_order,
+                      int                   new_byte_order,
+                      unsigned char        *p,
+                      unsigned char       **new_p)
+{
+  int current_type;
+
+  while ((current_type = _dbus_type_reader_get_current_type (reader)) != DBUS_TYPE_INVALID)
+    {
+      switch (current_type)
+        {
+        case DBUS_TYPE_BYTE:
+          ++p;
+          break;
+
+        case DBUS_TYPE_INT16:
+        case DBUS_TYPE_UINT16:
+          {
+            p = _DBUS_ALIGN_ADDRESS (p, 2);
+            *((dbus_uint16_t*)p) = DBUS_UINT16_SWAP_LE_BE (*((dbus_uint16_t*)p));
+            p += 2;
+          }
+          break;
+          
+        case DBUS_TYPE_BOOLEAN:
+        case DBUS_TYPE_INT32:
+        case DBUS_TYPE_UINT32:
+          {
+            p = _DBUS_ALIGN_ADDRESS (p, 4);
+            *((dbus_uint32_t*)p) = DBUS_UINT32_SWAP_LE_BE (*((dbus_uint32_t*)p));
+            p += 4;
+          }
+          break;
+          
+        case DBUS_TYPE_INT64:
+        case DBUS_TYPE_UINT64:
+        case DBUS_TYPE_DOUBLE:
+          {
+            p = _DBUS_ALIGN_ADDRESS (p, 8);
+#ifdef DBUS_HAVE_INT64
+            *((dbus_uint64_t*)p) = DBUS_UINT64_SWAP_LE_BE (*((dbus_uint64_t*)p));
+#else
+            _dbus_swap_array (p, 1, 8);
+#endif
+            p += 8;
+          }
+          break;
+
+        case DBUS_TYPE_ARRAY:
+        case DBUS_TYPE_STRING:
+        case DBUS_TYPE_OBJECT_PATH:
+          {
+            dbus_uint32_t array_len;
+            
+            p = _DBUS_ALIGN_ADDRESS (p, 4);
+
+            array_len = _dbus_unpack_uint32 (old_byte_order, p);
+
+            *((dbus_uint32_t*)p) = DBUS_UINT32_SWAP_LE_BE (*((dbus_uint32_t*)p));
+            p += 4;
+
+            if (current_type == DBUS_TYPE_ARRAY)
+              {
+                int elem_type;
+                int alignment;
+
+                elem_type = _dbus_type_reader_get_element_type (reader);
+                alignment = _dbus_type_get_alignment (elem_type);
+
+               _dbus_assert ((array_len / alignment) < DBUS_MAXIMUM_ARRAY_LENGTH);
+
+                p = _DBUS_ALIGN_ADDRESS (p, alignment);
+                
+                if (dbus_type_is_fixed (elem_type))
+                  {
+                    if (alignment > 1)
+                     _dbus_swap_array (p, array_len / alignment, alignment);
+                   p += array_len;
+                  }
+                else
+                  {
+                    DBusTypeReader sub;
+                    const unsigned char *array_end;
+
+                    array_end = p + array_len;
+                    
+                    _dbus_type_reader_recurse (reader, &sub);
+
+                    while (p < array_end)
+                      {
+                        byteswap_body_helper (&sub,
+                                              FALSE,
+                                              old_byte_order,
+                                              new_byte_order,
+                                              p, &p);
+                      }
+                  }
+              }
+            else
+              {
+                _dbus_assert (current_type == DBUS_TYPE_STRING ||
+                              current_type == DBUS_TYPE_OBJECT_PATH);
+                
+                p += (array_len + 1); /* + 1 for nul */
+              }
+          }
+          break;
+
+        case DBUS_TYPE_SIGNATURE:
+          {
+            dbus_uint32_t sig_len;
+
+            sig_len = *p;
+            
+            p += (sig_len + 2); /* +2 for len and nul */
+          }
+          break;
+
+        case DBUS_TYPE_VARIANT:
+          {
+            /* 1 byte sig len, sig typecodes, align to
+             * contained-type-boundary, values.
+             */
+            dbus_uint32_t sig_len;
+            DBusString sig;
+            DBusTypeReader sub;
+            int contained_alignment;
+
+            sig_len = *p;
+            ++p;
+
+            _dbus_string_init_const_len (&sig, p, sig_len);
+
+            p += (sig_len + 1); /* 1 for nul */
+
+            contained_alignment = _dbus_type_get_alignment (_dbus_first_type_in_signature (&sig, 0));
+            
+            p = _DBUS_ALIGN_ADDRESS (p, contained_alignment);
+
+            _dbus_type_reader_init_types_only (&sub, &sig, 0);
+
+            byteswap_body_helper (&sub, FALSE, old_byte_order, new_byte_order, p, &p);
+          }
+          break;
+
+        case DBUS_TYPE_STRUCT:
+        case DBUS_TYPE_DICT_ENTRY:
+          {
+            DBusTypeReader sub;
+
+            p = _DBUS_ALIGN_ADDRESS (p, 8);
+            
+            _dbus_type_reader_recurse (reader, &sub);
+            
+            byteswap_body_helper (&sub, TRUE, old_byte_order, new_byte_order, p, &p);
+          }
+          break;
+
+        default:
+          _dbus_assert_not_reached ("invalid typecode in supposedly-validated signature");
+          break;
+        }
+
+      if (walk_reader_to_end)
+        _dbus_type_reader_next (reader);
+      else
+        break;
+    }
+
+  if (new_p)
+    *new_p = p;
+}
+
+/**
+ * Byteswaps the marshaled data in the given value_str.
+ *
+ * @param signature the types in the value_str
+ * @param signature_start where in signature is the signature
+ * @param old_byte_order the old byte order
+ * @param new_byte_order the new byte order
+ * @param value_str the string containing the body
+ * @param value_pos where the values start
+ */
+void
+_dbus_marshal_byteswap (const DBusString *signature,
+                        int               signature_start,
+                        int               old_byte_order,
+                        int               new_byte_order,
+                        DBusString       *value_str,
+                        int               value_pos)
+{
+  DBusTypeReader reader;
+
+  _dbus_assert (value_pos >= 0);
+  _dbus_assert (value_pos <= _dbus_string_get_length (value_str));
+
+  if (old_byte_order == new_byte_order)
+    return;
+  
+  _dbus_type_reader_init_types_only (&reader,
+                                     signature, signature_start);
+
+  byteswap_body_helper (&reader, TRUE,
+                        old_byte_order, new_byte_order,
+                        _dbus_string_get_data_len (value_str, value_pos, 0),
+                        NULL);
+}
+
+/** @} */
+
+/* Tests in dbus-marshal-byteswap-util.c */
diff --git a/src/dbus/dbus-marshal-byteswap.h b/src/dbus/dbus-marshal-byteswap.h
new file mode 100644 (file)
index 0000000..880c837
--- /dev/null
@@ -0,0 +1,42 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-byteswap.h  Swap a block of marshaled data
+ *
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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_MARSHAL_BYTESWAP_H
+#define DBUS_MARSHAL_BYTESWAP_H
+
+#include <config.h>
+#include <dbus/dbus-protocol.h>
+#include <dbus/dbus-marshal-recursive.h>
+
+#ifndef PACKAGE
+#error "config.h not included here"
+#endif
+
+void _dbus_marshal_byteswap (const DBusString *signature,
+                             int               signature_start,
+                             int               old_byte_order,
+                             int               new_byte_order,
+                             DBusString       *value_str,
+                             int               value_pos);
+
+#endif /* DBUS_MARSHAL_BYTESWAP_H */
diff --git a/src/dbus/dbus-marshal-header.c b/src/dbus/dbus-marshal-header.c
new file mode 100644 (file)
index 0000000..8aba6a9
--- /dev/null
@@ -0,0 +1,1489 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-header.c  Managing marshaling/demarshaling of message headers
+ *
+ * Copyright (C) 2005  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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/dbus-shared.h"
+#include "dbus-marshal-header.h"
+#include "dbus-marshal-recursive.h"
+#include "dbus-marshal-byteswap.h"
+
+/**
+ * @addtogroup DBusMarshal
+ *
+ * @{
+ */
+
+
+/* Not thread locked, but strictly const/read-only so should be OK
+ */
+/** Static #DBusString containing the signature of a message header */
+_DBUS_STRING_DEFINE_STATIC(_dbus_header_signature_str, DBUS_HEADER_SIGNATURE);
+/** Static #DBusString containing the local interface */
+_DBUS_STRING_DEFINE_STATIC(_dbus_local_interface_str,  DBUS_INTERFACE_LOCAL);
+/** Static #DBusString containing the local path */
+_DBUS_STRING_DEFINE_STATIC(_dbus_local_path_str,       DBUS_PATH_LOCAL);
+
+/** Offset from start of _dbus_header_signature_str to the signature of the fields array */
+#define FIELDS_ARRAY_SIGNATURE_OFFSET 6
+/** Offset from start of _dbus_header_signature_str to the signature of an element of the fields array */
+#define FIELDS_ARRAY_ELEMENT_SIGNATURE_OFFSET 7
+
+
+/** Offset to byte order from start of header */
+#define BYTE_ORDER_OFFSET    0
+/** Offset to type from start of header */
+#define TYPE_OFFSET          1
+/** Offset to flags from start of header */
+#define FLAGS_OFFSET         2
+/** Offset to version from start of header */
+#define VERSION_OFFSET       3
+/** Offset to body length from start of header */
+#define BODY_LENGTH_OFFSET 4
+/** Offset to client serial from start of header */
+#define SERIAL_OFFSET 8
+/** Offset to fields array length from start of header */
+#define FIELDS_ARRAY_LENGTH_OFFSET 12
+/** Offset to first field in header */
+#define FIRST_FIELD_OFFSET 16
+
+typedef struct
+{
+  unsigned char code; /**< the field code */
+  unsigned char type; /**< the value type */
+} HeaderFieldType;
+
+static const HeaderFieldType
+_dbus_header_field_types[DBUS_HEADER_FIELD_LAST+1] = {
+  { DBUS_HEADER_FIELD_INVALID, DBUS_TYPE_INVALID },
+  { DBUS_HEADER_FIELD_PATH, DBUS_TYPE_OBJECT_PATH },
+  { DBUS_HEADER_FIELD_INTERFACE, DBUS_TYPE_STRING },
+  { DBUS_HEADER_FIELD_MEMBER, DBUS_TYPE_STRING },
+  { DBUS_HEADER_FIELD_ERROR_NAME, DBUS_TYPE_STRING },
+  { DBUS_HEADER_FIELD_REPLY_SERIAL, DBUS_TYPE_UINT32 },
+  { DBUS_HEADER_FIELD_DESTINATION, DBUS_TYPE_STRING },
+  { DBUS_HEADER_FIELD_SENDER, DBUS_TYPE_STRING },
+  { DBUS_HEADER_FIELD_SIGNATURE, DBUS_TYPE_SIGNATURE }
+};
+
+/** Macro to look up the correct type for a field */
+#define EXPECTED_TYPE_OF_FIELD(field) (_dbus_header_field_types[field].type)
+
+/** The most padding we could ever need for a header */
+#define MAX_POSSIBLE_HEADER_PADDING 7
+static dbus_bool_t
+reserve_header_padding (DBusHeader *header)
+{
+  _dbus_assert (header->padding <= MAX_POSSIBLE_HEADER_PADDING);
+
+  if (!_dbus_string_lengthen (&header->data,
+                              MAX_POSSIBLE_HEADER_PADDING - header->padding))
+    return FALSE;
+  header->padding = MAX_POSSIBLE_HEADER_PADDING;
+  return TRUE;
+}
+
+static void
+correct_header_padding (DBusHeader *header)
+{
+  int unpadded_len;
+
+  _dbus_assert (header->padding == 7);
+
+  _dbus_string_shorten (&header->data, header->padding);
+  unpadded_len = _dbus_string_get_length (&header->data);
+
+  if (!_dbus_string_align_length (&header->data, 8))
+    _dbus_assert_not_reached ("couldn't pad header though enough padding was preallocated");
+
+  header->padding = _dbus_string_get_length (&header->data) - unpadded_len;
+}
+
+/** Compute the end of the header, ignoring padding */
+#define HEADER_END_BEFORE_PADDING(header) \
+  (_dbus_string_get_length (&(header)->data) - (header)->padding)
+
+/**
+ * Invalidates all fields in the cache. This may be used when the
+ * cache is totally uninitialized (contains junk) so should not
+ * look at what's in there now.
+ *
+ * @param header the header
+ */
+static void
+_dbus_header_cache_invalidate_all (DBusHeader *header)
+{
+  int i;
+
+  i = 0;
+  while (i <= DBUS_HEADER_FIELD_LAST)
+    {
+      header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_UNKNOWN;
+      ++i;
+    }
+}
+
+/**
+ * Caches one field
+ *
+ * @param header the header
+ * @param field_code the field
+ * @param variant_reader the reader for the variant in the field
+ */
+static void
+_dbus_header_cache_one (DBusHeader     *header,
+                        int             field_code,
+                        DBusTypeReader *variant_reader)
+{
+  header->fields[field_code].value_pos =
+    _dbus_type_reader_get_value_pos (variant_reader);
+
+#if 0
+  _dbus_verbose ("cached value_pos %d for field %d\n",
+                 header->fields[field_code].value_pos, field_code)
+#endif
+}
+
+/**
+ * Revalidates the fields cache
+ *
+ * @param header the header
+ */
+static void
+_dbus_header_cache_revalidate (DBusHeader *header)
+{
+  DBusTypeReader array;
+  DBusTypeReader reader;
+  int i;
+
+  i = 0;
+  while (i <= DBUS_HEADER_FIELD_LAST)
+    {
+      header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_NONEXISTENT;
+      ++i;
+    }
+
+  _dbus_type_reader_init (&reader,
+                          header->byte_order,
+                          &_dbus_header_signature_str,
+                          FIELDS_ARRAY_SIGNATURE_OFFSET,
+                          &header->data,
+                          FIELDS_ARRAY_LENGTH_OFFSET);
+
+  _dbus_type_reader_recurse (&reader, &array);
+
+  while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID)
+    {
+      DBusTypeReader sub;
+      DBusTypeReader variant;
+      unsigned char field_code;
+
+      _dbus_type_reader_recurse (&array, &sub);
+
+      _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE);
+      _dbus_type_reader_read_basic (&sub, &field_code);
+
+      /* Unknown fields should be ignored */
+      if (field_code > DBUS_HEADER_FIELD_LAST)
+        goto next_field;
+
+      _dbus_type_reader_next (&sub);
+
+      _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_VARIANT);
+      _dbus_type_reader_recurse (&sub, &variant);
+
+      _dbus_header_cache_one (header, field_code, &variant);
+
+    next_field:
+      _dbus_type_reader_next (&array);
+    }
+}
+
+/**
+ * Checks for a field, updating the cache if required.
+ *
+ * @param header the header
+ * @param field the field to check
+ * @returns #FALSE if the field doesn't exist
+ */
+static dbus_bool_t
+_dbus_header_cache_check (DBusHeader    *header,
+                          int            field)
+{
+  _dbus_assert (field <= DBUS_HEADER_FIELD_LAST);
+
+  if (header->fields[field].value_pos == _DBUS_HEADER_FIELD_VALUE_UNKNOWN)
+    _dbus_header_cache_revalidate (header);
+
+  if (header->fields[field].value_pos == _DBUS_HEADER_FIELD_VALUE_NONEXISTENT)
+    return FALSE;
+
+  return TRUE;
+}
+
+/**
+ * Checks whether a field is known not to exist. It may exist
+ * even if it's not known to exist.
+ *
+ * @param header the header
+ * @param field the field to check
+ * @returns #FALSE if the field definitely doesn't exist
+ */
+static dbus_bool_t
+_dbus_header_cache_known_nonexistent (DBusHeader    *header,
+                                      int            field)
+{
+  _dbus_assert (field <= DBUS_HEADER_FIELD_LAST);
+
+  return (header->fields[field].value_pos == _DBUS_HEADER_FIELD_VALUE_NONEXISTENT);
+}
+
+/**
+ * Writes a struct of { byte, variant } with the given basic type.
+ *
+ * @param writer the writer (should be ready to write a struct)
+ * @param type the type of the value
+ * @param value the value as for _dbus_marshal_set_basic()
+ * @returns #FALSE if no memory
+ */
+static dbus_bool_t
+write_basic_field (DBusTypeWriter *writer,
+                   int             field,
+                   int             type,
+                   const void     *value)
+{
+  DBusTypeWriter sub;
+  DBusTypeWriter variant;
+  int start;
+  int padding;
+  unsigned char field_byte;
+  DBusString contained_type;
+  char buf[2];
+
+  start = writer->value_pos;
+  padding = _dbus_string_get_length (writer->value_str) - start;
+
+  if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_STRUCT,
+                                  NULL, 0, &sub))
+    goto append_failed;
+
+  field_byte = field;
+  if (!_dbus_type_writer_write_basic (&sub, DBUS_TYPE_BYTE,
+                                      &field_byte))
+    goto append_failed;
+
+  buf[0] = type;
+  buf[1] = '\0';
+  _dbus_string_init_const_len (&contained_type, buf, 1);
+
+  if (!_dbus_type_writer_recurse (&sub, DBUS_TYPE_VARIANT,
+                                  &contained_type, 0, &variant))
+    goto append_failed;
+
+  if (!_dbus_type_writer_write_basic (&variant, type, value))
+    goto append_failed;
+
+  if (!_dbus_type_writer_unrecurse (&sub, &variant))
+    goto append_failed;
+
+  if (!_dbus_type_writer_unrecurse (writer, &sub))
+    goto append_failed;
+
+  return TRUE;
+
+ append_failed:
+  _dbus_string_delete (writer->value_str,
+                       start,
+                       _dbus_string_get_length (writer->value_str) - start - padding);
+  return FALSE;
+}
+
+/**
+ * Sets a struct of { byte, variant } with the given basic type.
+ *
+ * @param reader the reader (should be iterating over the array pointing at the field to set)
+ * @param type the type of the value
+ * @param value the value as for _dbus_marshal_set_basic()
+ * @param realign_root where to realign from
+ * @returns #FALSE if no memory
+ */
+static dbus_bool_t
+set_basic_field (DBusTypeReader       *reader,
+                 int                   field,
+                 int                   type,
+                 const void           *value,
+                 const DBusTypeReader *realign_root)
+{
+  DBusTypeReader sub;
+  DBusTypeReader variant;
+
+  _dbus_type_reader_recurse (reader, &sub);
+
+  _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE);
+#ifndef DBUS_DISABLE_ASSERT
+ {
+   unsigned char v_BYTE;
+   _dbus_type_reader_read_basic (&sub, &v_BYTE);
+   _dbus_assert (((int) v_BYTE) == field);
+ }
+#endif
+
+  if (!_dbus_type_reader_next (&sub))
+    _dbus_assert_not_reached ("no variant field?");
+
+  _dbus_type_reader_recurse (&sub, &variant);
+  _dbus_assert (_dbus_type_reader_get_current_type (&variant) == type);
+
+  if (!_dbus_type_reader_set_basic (&variant, value, realign_root))
+    return FALSE;
+
+  return TRUE;
+}
+
+/**
+ * Gets the type of the message.
+ *
+ * @param header the header
+ * @returns the type
+ */
+int
+_dbus_header_get_message_type (DBusHeader *header)
+{
+  int type;
+
+  type = _dbus_string_get_byte (&header->data, TYPE_OFFSET);
+  _dbus_assert (type != DBUS_MESSAGE_TYPE_INVALID);
+
+  return type;
+}
+
+/**
+ * Sets the serial number of a header.  This can only be done once on
+ * a header.
+ *
+ * @param header the header
+ * @param serial the serial
+ */
+void
+_dbus_header_set_serial (DBusHeader    *header,
+                         dbus_uint32_t  serial)
+{
+  /* we use this function to set the serial on outgoing
+   * messages, and to reset the serial in dbus_message_copy;
+   * this assertion should catch a double-set on outgoing.
+   */
+  _dbus_assert (_dbus_header_get_serial (header) == 0 ||
+                serial == 0);
+
+  _dbus_marshal_set_uint32 (&header->data,
+                            SERIAL_OFFSET,
+                           serial,
+                            header->byte_order);
+}
+
+/**
+ * See dbus_message_get_serial()
+ *
+ * @param header the header
+ * @returns the client serial
+ */
+dbus_uint32_t
+_dbus_header_get_serial (DBusHeader *header)
+{
+  return _dbus_marshal_read_uint32 (&header->data,
+                                    SERIAL_OFFSET,
+                                    header->byte_order,
+                                    NULL);
+}
+
+/**
+ * Re-initializes a header that was previously initialized and never
+ * freed.  After this, to make the header valid you have to call
+ * _dbus_header_create().
+ *
+ * @param header header to re-initialize
+ * @param byte_order byte order of the header
+ */
+void
+_dbus_header_reinit (DBusHeader *header,
+                     int         byte_order)
+{
+  _dbus_string_set_length (&header->data, 0);
+
+  header->byte_order = byte_order;
+  header->padding = 0;
+
+  _dbus_header_cache_invalidate_all (header);
+}
+
+/**
+ * Initializes a header, but doesn't prepare it for use;
+ * to make the header valid, you have to call _dbus_header_create().
+ *
+ * @param header header to initialize
+ * @param byte_order byte order of the header
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_header_init (DBusHeader *header,
+                   int         byte_order)
+{
+  if (!_dbus_string_init_preallocated (&header->data, 32))
+    return FALSE;
+
+  _dbus_header_reinit (header, byte_order);
+
+  return TRUE;
+}
+
+/**
+ * Frees a header.
+ *
+ * @param header the header
+ */
+void
+_dbus_header_free (DBusHeader *header)
+{
+  _dbus_string_free (&header->data);
+}
+
+/**
+ * Initializes dest with a copy of the given header.
+ * Resets the message serial to 0 on the copy.
+ *
+ * @param header header to copy
+ * @param dest destination for copy
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_header_copy (const DBusHeader *header,
+                   DBusHeader       *dest)
+{
+  *dest = *header;
+
+  if (!_dbus_string_init_preallocated (&dest->data,
+                                       _dbus_string_get_length (&header->data)))
+    return FALSE;
+
+  if (!_dbus_string_copy (&header->data, 0, &dest->data, 0))
+    {
+      _dbus_string_free (&dest->data);
+      return FALSE;
+    }
+
+  /* Reset the serial */
+  _dbus_header_set_serial (dest, 0);
+
+  return TRUE;
+}
+
+/**
+ * Fills in the primary fields of the header, so the header is ready
+ * for use. #NULL may be specified for some or all of the fields to
+ * avoid adding those fields. Some combinations of fields don't make
+ * sense, and passing them in will trigger an assertion failure.
+ *
+ * @param header the header
+ * @param message_type the message type
+ * @param destination destination field or #NULL
+ * @param path path field or #NULL
+ * @param interface interface field or #NULL
+ * @param member member field or #NULL
+ * @param error_name error name or #NULL
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_header_create (DBusHeader  *header,
+                     int          message_type,
+                     const char  *destination,
+                     const char  *path,
+                     const char  *interface,
+                     const char  *member,
+                     const char  *error_name)
+{
+  unsigned char v_BYTE;
+  dbus_uint32_t v_UINT32;
+  DBusTypeWriter writer;
+  DBusTypeWriter array;
+
+  _dbus_assert (((interface || message_type != DBUS_MESSAGE_TYPE_SIGNAL) && member) ||
+                (error_name) ||
+                !(interface || member || error_name));
+  _dbus_assert (_dbus_string_get_length (&header->data) == 0);
+
+  if (!reserve_header_padding (header))
+    return FALSE;
+
+  _dbus_type_writer_init_values_only (&writer, header->byte_order,
+                                      &_dbus_header_signature_str, 0,
+                                      &header->data,
+                                      HEADER_END_BEFORE_PADDING (header));
+
+  v_BYTE = header->byte_order;
+  if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE,
+                                      &v_BYTE))
+    goto oom;
+
+  v_BYTE = message_type;
+  if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE,
+                                      &v_BYTE))
+    goto oom;
+
+  v_BYTE = 0; /* flags */
+  if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE,
+                                      &v_BYTE))
+    goto oom;
+
+  v_BYTE = DBUS_MAJOR_PROTOCOL_VERSION;
+  if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE,
+                                      &v_BYTE))
+    goto oom;
+
+  v_UINT32 = 0; /* body length */
+  if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_UINT32,
+                                      &v_UINT32))
+    goto oom;
+
+  v_UINT32 = 0; /* serial */
+  if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_UINT32,
+                                      &v_UINT32))
+    goto oom;
+
+  if (!_dbus_type_writer_recurse (&writer, DBUS_TYPE_ARRAY,
+                                  &_dbus_header_signature_str,
+                                  FIELDS_ARRAY_SIGNATURE_OFFSET,
+                                  &array))
+    goto oom;
+
+  /* Marshal all the fields (Marshall Fields?) */
+
+  if (path != NULL)
+    {
+      if (!write_basic_field (&array,
+                              DBUS_HEADER_FIELD_PATH,
+                              DBUS_TYPE_OBJECT_PATH,
+                              &path))
+        goto oom;
+    }
+
+  if (destination != NULL)
+    {
+      if (!write_basic_field (&array,
+                              DBUS_HEADER_FIELD_DESTINATION,
+                              DBUS_TYPE_STRING,
+                              &destination))
+        goto oom;
+    }
+
+  if (interface != NULL)
+    {
+      if (!write_basic_field (&array,
+                              DBUS_HEADER_FIELD_INTERFACE,
+                              DBUS_TYPE_STRING,
+                              &interface))
+        goto oom;
+    }
+
+  if (member != NULL)
+    {
+      if (!write_basic_field (&array,
+                              DBUS_HEADER_FIELD_MEMBER,
+                              DBUS_TYPE_STRING,
+                              &member))
+        goto oom;
+    }
+
+  if (error_name != NULL)
+    {
+      if (!write_basic_field (&array,
+                              DBUS_HEADER_FIELD_ERROR_NAME,
+                              DBUS_TYPE_STRING,
+                              &error_name))
+        goto oom;
+    }
+
+  if (!_dbus_type_writer_unrecurse (&writer, &array))
+    goto oom;
+
+  correct_header_padding (header);
+
+  return TRUE;
+
+ oom:
+  _dbus_string_delete (&header->data, 0,
+                       _dbus_string_get_length (&header->data) - header->padding);
+  correct_header_padding (header);
+
+  return FALSE;
+}
+
+/**
+ * Given data long enough to contain the length of the message body
+ * and the fields array, check whether the data is long enough to
+ * contain the entire message (assuming the claimed lengths are
+ * accurate). Also checks that the lengths are in sanity parameters.
+ *
+ * @param max_message_length maximum length of a valid message
+ * @param validity return location for why the data is invalid if it is
+ * @param byte_order return location for byte order
+ * @param fields_array_len return location for claimed fields array length
+ * @param header_len return location for claimed header length
+ * @param body_len return location for claimed body length
+ * @param str the data
+ * @param start start of data, 8-aligned
+ * @param len length of data
+ * @returns #TRUE if the data is long enough for the claimed length, and the lengths were valid
+ */
+dbus_bool_t
+_dbus_header_have_message_untrusted (int                max_message_length,
+                                     DBusValidity      *validity,
+                                     int               *byte_order,
+                                     int               *fields_array_len,
+                                     int               *header_len,
+                                     int               *body_len,
+                                     const DBusString  *str,
+                                     int                start,
+                                     int                len)
+
+{
+  dbus_uint32_t header_len_unsigned;
+  dbus_uint32_t fields_array_len_unsigned;
+  dbus_uint32_t body_len_unsigned;
+
+  _dbus_assert (start >= 0);
+  _dbus_assert (start < _DBUS_INT32_MAX / 2);
+  _dbus_assert (len >= 0);
+
+  _dbus_assert (start == (int) _DBUS_ALIGN_VALUE (start, 8));
+
+  *byte_order = _dbus_string_get_byte (str, start + BYTE_ORDER_OFFSET);
+
+  if (*byte_order != DBUS_LITTLE_ENDIAN && *byte_order != DBUS_BIG_ENDIAN)
+    {
+      *validity = DBUS_INVALID_BAD_BYTE_ORDER;
+      return FALSE;
+    }
+
+  _dbus_assert (FIELDS_ARRAY_LENGTH_OFFSET + 4 <= len);
+  fields_array_len_unsigned = _dbus_marshal_read_uint32 (str, start + FIELDS_ARRAY_LENGTH_OFFSET,
+                                                         *byte_order, NULL);
+
+  if (fields_array_len_unsigned > (unsigned) max_message_length)
+    {
+      *validity = DBUS_INVALID_INSANE_FIELDS_ARRAY_LENGTH;
+      return FALSE;
+    }
+
+  _dbus_assert (BODY_LENGTH_OFFSET + 4 < len);
+  body_len_unsigned = _dbus_marshal_read_uint32 (str, start + BODY_LENGTH_OFFSET,
+                                                 *byte_order, NULL);
+
+  if (body_len_unsigned > (unsigned) max_message_length)
+    {
+      *validity = DBUS_INVALID_INSANE_BODY_LENGTH;
+      return FALSE;
+    }
+
+  header_len_unsigned = FIRST_FIELD_OFFSET + fields_array_len_unsigned;
+  header_len_unsigned = _DBUS_ALIGN_VALUE (header_len_unsigned, 8);
+
+  /* overflow should be impossible since the lengths aren't allowed to
+   * be huge.
+   */
+  _dbus_assert (max_message_length < _DBUS_INT32_MAX / 2);
+  if (body_len_unsigned + header_len_unsigned > (unsigned) max_message_length)
+    {
+      *validity = DBUS_INVALID_MESSAGE_TOO_LONG;
+      return FALSE;
+    }
+
+  _dbus_assert (body_len_unsigned < (unsigned) _DBUS_INT32_MAX);
+  _dbus_assert (fields_array_len_unsigned < (unsigned) _DBUS_INT32_MAX);
+  _dbus_assert (header_len_unsigned < (unsigned) _DBUS_INT32_MAX);
+
+  *body_len = body_len_unsigned;
+  *fields_array_len = fields_array_len_unsigned;
+  *header_len = header_len_unsigned;
+
+  *validity = DBUS_VALID;
+
+  _dbus_verbose ("have %d bytes, need body %u + header %u = %u\n",
+                 len, body_len_unsigned, header_len_unsigned,
+                 body_len_unsigned + header_len_unsigned);
+
+  return (body_len_unsigned + header_len_unsigned) <= (unsigned) len;
+}
+
+static DBusValidity
+check_mandatory_fields (DBusHeader *header)
+{
+#define REQUIRE_FIELD(name) do { if (header->fields[DBUS_HEADER_FIELD_##name].value_pos < 0) return DBUS_INVALID_MISSING_##name; } while (0)
+
+  switch (_dbus_header_get_message_type (header))
+    {
+    case DBUS_MESSAGE_TYPE_SIGNAL:
+      REQUIRE_FIELD (INTERFACE);
+      /* FALL THRU - signals also require the path and member */
+    case DBUS_MESSAGE_TYPE_METHOD_CALL:
+      REQUIRE_FIELD (PATH);
+      REQUIRE_FIELD (MEMBER);
+      break;
+    case DBUS_MESSAGE_TYPE_ERROR:
+      REQUIRE_FIELD (ERROR_NAME);
+      REQUIRE_FIELD (REPLY_SERIAL);
+      break;
+    case DBUS_MESSAGE_TYPE_METHOD_RETURN:
+      REQUIRE_FIELD (REPLY_SERIAL);
+      break;
+    default:
+      /* other message types allowed but ignored */
+      break;
+    }
+
+  return DBUS_VALID;
+}
+
+static DBusValidity
+load_and_validate_field (DBusHeader     *header,
+                         int             field,
+                         DBusTypeReader *variant_reader)
+{
+  int type;
+  int expected_type;
+  const DBusString *value_str;
+  int value_pos;
+  int str_data_pos;
+  dbus_uint32_t v_UINT32;
+  int bad_string_code;
+  dbus_bool_t (* string_validation_func) (const DBusString *str,
+                                          int start, int len);
+
+  /* Supposed to have been checked already */
+  _dbus_assert (field <= DBUS_HEADER_FIELD_LAST);
+  _dbus_assert (field != DBUS_HEADER_FIELD_INVALID);
+
+  /* Before we can cache a field, we need to know it has the right type */
+  type = _dbus_type_reader_get_current_type (variant_reader);
+
+  _dbus_assert (_dbus_header_field_types[field].code == field);
+
+  expected_type = EXPECTED_TYPE_OF_FIELD (field);
+  if (type != expected_type)
+    {
+      _dbus_verbose ("Field %d should have type %d but has %d\n",
+                     field, expected_type, type);
+      return DBUS_INVALID_HEADER_FIELD_HAS_WRONG_TYPE;
+    }
+
+  /* If the field was provided twice, we aren't happy */
+  if (header->fields[field].value_pos >= 0)
+    {
+      _dbus_verbose ("Header field %d seen a second time\n", field);
+      return DBUS_INVALID_HEADER_FIELD_APPEARS_TWICE;
+    }
+
+  /* Now we can cache and look at the field content */
+  _dbus_verbose ("initially caching field %d\n", field);
+  _dbus_header_cache_one (header, field, variant_reader);
+
+  string_validation_func = NULL;
+
+  /* make compiler happy that all this is initialized */
+  v_UINT32 = 0;
+  value_str = NULL;
+  value_pos = -1;
+  str_data_pos = -1;
+  bad_string_code = DBUS_VALID;
+
+  if (expected_type == DBUS_TYPE_UINT32)
+    {
+      _dbus_header_get_field_basic (header, field, expected_type,
+                                    &v_UINT32);
+    }
+  else if (expected_type == DBUS_TYPE_STRING ||
+           expected_type == DBUS_TYPE_OBJECT_PATH ||
+           expected_type == DBUS_TYPE_SIGNATURE)
+    {
+      _dbus_header_get_field_raw (header, field,
+                                  &value_str, &value_pos);
+      str_data_pos = _DBUS_ALIGN_VALUE (value_pos, 4) + 4;
+    }
+  else
+    {
+      _dbus_assert_not_reached ("none of the known fields should have this type");
+    }
+
+  switch (field)
+    {
+    case DBUS_HEADER_FIELD_DESTINATION:
+      string_validation_func = _dbus_validate_bus_name;
+      bad_string_code = DBUS_INVALID_BAD_DESTINATION;
+      break;
+    case DBUS_HEADER_FIELD_INTERFACE:
+      string_validation_func = _dbus_validate_interface;
+      bad_string_code = DBUS_INVALID_BAD_INTERFACE;
+
+      if (_dbus_string_equal_substring (&_dbus_local_interface_str,
+                                        0,
+                                        _dbus_string_get_length (&_dbus_local_interface_str),
+                                        value_str, str_data_pos))
+        {
+          _dbus_verbose ("Message is on the local interface\n");
+          return DBUS_INVALID_USES_LOCAL_INTERFACE;
+        }
+      break;
+
+    case DBUS_HEADER_FIELD_MEMBER:
+      string_validation_func = _dbus_validate_member;
+      bad_string_code = DBUS_INVALID_BAD_MEMBER;
+      break;
+
+    case DBUS_HEADER_FIELD_ERROR_NAME:
+      string_validation_func = _dbus_validate_error_name;
+      bad_string_code = DBUS_INVALID_BAD_ERROR_NAME;
+      break;
+
+    case DBUS_HEADER_FIELD_SENDER:
+      string_validation_func = _dbus_validate_bus_name;
+      bad_string_code = DBUS_INVALID_BAD_SENDER;
+      break;
+
+    case DBUS_HEADER_FIELD_PATH:
+      /* OBJECT_PATH was validated generically due to its type */
+      string_validation_func = NULL;
+
+      if (_dbus_string_equal_substring (&_dbus_local_path_str,
+                                        0,
+                                        _dbus_string_get_length (&_dbus_local_path_str),
+                                        value_str, str_data_pos))
+        {
+          _dbus_verbose ("Message is from the local path\n");
+          return DBUS_INVALID_USES_LOCAL_PATH;
+        }
+      break;
+
+    case DBUS_HEADER_FIELD_REPLY_SERIAL:
+      /* Can't be 0 */
+      if (v_UINT32 == 0)
+        {
+          return DBUS_INVALID_BAD_SERIAL;
+        }
+      break;
+
+    case DBUS_HEADER_FIELD_SIGNATURE:
+      /* SIGNATURE validated generically due to its type */
+      string_validation_func = NULL;
+      break;
+
+    default:
+      _dbus_assert_not_reached ("unknown field shouldn't be seen here");
+      break;
+    }
+
+  if (string_validation_func)
+    {
+      dbus_uint32_t len;
+
+      _dbus_assert (bad_string_code != DBUS_VALID);
+
+      len = _dbus_marshal_read_uint32 (value_str, value_pos,
+                                       header->byte_order, NULL);
+
+#if 0
+      _dbus_verbose ("Validating string header field; code %d if fails\n",
+                     bad_string_code);
+#endif
+      if (!(*string_validation_func) (value_str, str_data_pos, len))
+        return bad_string_code;
+    }
+
+  return DBUS_VALID;
+}
+
+/**
+ * Creates a message header from potentially-untrusted data. The
+ * return value is #TRUE if there was enough memory and the data was
+ * valid. If it returns #TRUE, the header will be created. If it
+ * returns #FALSE and *validity == #DBUS_VALIDITY_UNKNOWN_OOM_ERROR, 
+ * then there wasn't enough memory.  If it returns #FALSE 
+ * and *validity != #DBUS_VALIDITY_UNKNOWN_OOM_ERROR then the data was 
+ * invalid.
+ *
+ * The byte_order, fields_array_len, and body_len args should be from
+ * _dbus_header_have_message_untrusted(). Validation performed in
+ * _dbus_header_have_message_untrusted() is assumed to have been
+ * already done.
+ *
+ * @param header the header (must be initialized)
+ * @param mode whether to do validation
+ * @param validity return location for invalidity reason
+ * @param byte_order byte order from header
+ * @param fields_array_len claimed length of fields array
+ * @param body_len claimed length of body
+ * @param header_len claimed length of header
+ * @param str a string
+ * @param start start of header, 8-aligned
+ * @param len length of string to look at
+ * @returns #FALSE if no memory or data was invalid, #TRUE otherwise
+ */
+dbus_bool_t
+_dbus_header_load (DBusHeader        *header,
+                   DBusValidationMode mode,
+                   DBusValidity      *validity,
+                   int                byte_order,
+                   int                fields_array_len,
+                   int                header_len,
+                   int                body_len,
+                   const DBusString  *str,
+                   int                start,
+                   int                len)
+{
+  int leftover;
+  DBusValidity v;
+  DBusTypeReader reader;
+  DBusTypeReader array_reader;
+  unsigned char v_byte;
+  dbus_uint32_t v_uint32;
+  dbus_uint32_t serial;
+  int padding_start;
+  int padding_len;
+  int i;
+
+  _dbus_assert (start == (int) _DBUS_ALIGN_VALUE (start, 8));
+  _dbus_assert (header_len <= len);
+  _dbus_assert (_dbus_string_get_length (&header->data) == 0);
+
+  if (!_dbus_string_copy_len (str, start, header_len, &header->data, 0))
+    {
+      _dbus_verbose ("Failed to copy buffer into new header\n");
+      *validity = DBUS_VALIDITY_UNKNOWN_OOM_ERROR;
+      return FALSE;
+    }
+
+  if (mode == DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY)
+    {
+      leftover = len - header_len - body_len - start;
+    }
+  else
+    {
+      v = _dbus_validate_body_with_reason (&_dbus_header_signature_str, 0,
+                                           byte_order,
+                                           &leftover,
+                                           str, start, len);
+      
+      if (v != DBUS_VALID)
+        {
+          *validity = v;
+          goto invalid;
+        }
+    }
+
+  _dbus_assert (leftover < len);
+
+  padding_len = header_len - (FIRST_FIELD_OFFSET + fields_array_len);
+  padding_start = start + FIRST_FIELD_OFFSET + fields_array_len;
+  _dbus_assert (start + header_len == (int) _DBUS_ALIGN_VALUE (padding_start, 8));
+  _dbus_assert (start + header_len == padding_start + padding_len);
+
+  if (mode != DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY)
+    {
+      if (!_dbus_string_validate_nul (str, padding_start, padding_len))
+        {
+          *validity = DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL;
+          goto invalid;
+        }
+    }
+
+  header->padding = padding_len;
+
+  if (mode == DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY)
+    {
+      *validity = DBUS_VALID;
+      return TRUE;
+    }
+
+  /* We now know the data is well-formed, but we have to check that
+   * it's valid.
+   */
+
+  _dbus_type_reader_init (&reader,
+                          byte_order,
+                          &_dbus_header_signature_str, 0,
+                          str, start);
+
+  /* BYTE ORDER */
+  _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE);
+  _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == BYTE_ORDER_OFFSET);
+  _dbus_type_reader_read_basic (&reader, &v_byte);
+  _dbus_type_reader_next (&reader);
+
+  _dbus_assert (v_byte == byte_order);
+  header->byte_order = byte_order;
+
+  /* MESSAGE TYPE */
+  _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE);
+  _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == TYPE_OFFSET);
+  _dbus_type_reader_read_basic (&reader, &v_byte);
+  _dbus_type_reader_next (&reader);
+
+  /* unknown message types are supposed to be ignored, so only validation here is
+   * that it isn't invalid
+   */
+  if (v_byte == DBUS_MESSAGE_TYPE_INVALID)
+    {
+      *validity = DBUS_INVALID_BAD_MESSAGE_TYPE;
+      goto invalid;
+    }
+
+  /* FLAGS */
+  _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE);
+  _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == FLAGS_OFFSET);
+  _dbus_type_reader_read_basic (&reader, &v_byte);
+  _dbus_type_reader_next (&reader);
+
+  /* unknown flags should be ignored */
+
+  /* PROTOCOL VERSION */
+  _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE);
+  _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == VERSION_OFFSET);
+  _dbus_type_reader_read_basic (&reader, &v_byte);
+  _dbus_type_reader_next (&reader);
+
+  if (v_byte != DBUS_MAJOR_PROTOCOL_VERSION)
+    {
+      *validity = DBUS_INVALID_BAD_PROTOCOL_VERSION;
+      goto invalid;
+    }
+
+  /* BODY LENGTH */
+  _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_UINT32);
+  _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == BODY_LENGTH_OFFSET);
+  _dbus_type_reader_read_basic (&reader, &v_uint32);
+  _dbus_type_reader_next (&reader);
+
+  _dbus_assert (body_len == (signed) v_uint32);
+
+  /* SERIAL */
+  _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_UINT32);
+  _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == SERIAL_OFFSET);
+  _dbus_type_reader_read_basic (&reader, &serial);
+  _dbus_type_reader_next (&reader);
+
+  if (serial == 0)
+    {
+      *validity = DBUS_INVALID_BAD_SERIAL;
+      goto invalid;
+    }
+
+  _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_ARRAY);
+  _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == FIELDS_ARRAY_LENGTH_OFFSET);
+
+  _dbus_type_reader_recurse (&reader, &array_reader);
+  while (_dbus_type_reader_get_current_type (&array_reader) != DBUS_TYPE_INVALID)
+    {
+      DBusTypeReader struct_reader;
+      DBusTypeReader variant_reader;
+      unsigned char field_code;
+
+      _dbus_assert (_dbus_type_reader_get_current_type (&array_reader) == DBUS_TYPE_STRUCT);
+
+      _dbus_type_reader_recurse (&array_reader, &struct_reader);
+
+      _dbus_assert (_dbus_type_reader_get_current_type (&struct_reader) == DBUS_TYPE_BYTE);
+      _dbus_type_reader_read_basic (&struct_reader, &field_code);
+      _dbus_type_reader_next (&struct_reader);
+
+      if (field_code == DBUS_HEADER_FIELD_INVALID)
+        {
+          _dbus_verbose ("invalid header field code\n");
+          *validity = DBUS_INVALID_HEADER_FIELD_CODE;
+          goto invalid;
+        }
+
+      if (field_code > DBUS_HEADER_FIELD_LAST)
+        {
+          _dbus_verbose ("unknown header field code %d, skipping\n",
+                         field_code);
+          goto next_field;
+        }
+
+      _dbus_assert (_dbus_type_reader_get_current_type (&struct_reader) == DBUS_TYPE_VARIANT);
+      _dbus_type_reader_recurse (&struct_reader, &variant_reader);
+
+      v = load_and_validate_field (header, field_code, &variant_reader);
+      if (v != DBUS_VALID)
+        {
+          _dbus_verbose ("Field %d was invalid\n", field_code);
+          *validity = v;
+          goto invalid;
+        }
+
+    next_field:
+      _dbus_type_reader_next (&array_reader);
+    }
+
+  /* Anything we didn't fill in is now known not to exist */
+  i = 0;
+  while (i <= DBUS_HEADER_FIELD_LAST)
+    {
+      if (header->fields[i].value_pos == _DBUS_HEADER_FIELD_VALUE_UNKNOWN)
+        header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_NONEXISTENT;
+      ++i;
+    }
+
+  v = check_mandatory_fields (header);
+  if (v != DBUS_VALID)
+    {
+      _dbus_verbose ("Mandatory fields were missing, code %d\n", v);
+      *validity = v;
+      goto invalid;
+    }
+
+  *validity = DBUS_VALID;
+  return TRUE;
+
+ invalid:
+  _dbus_string_set_length (&header->data, 0);
+  return FALSE;
+}
+
+/**
+ * Fills in the correct body length.
+ *
+ * @param header the header
+ * @param body_len the length of the body
+ */
+void
+_dbus_header_update_lengths (DBusHeader *header,
+                             int         body_len)
+{
+  _dbus_marshal_set_uint32 (&header->data,
+                            BODY_LENGTH_OFFSET,
+                            body_len,
+                            header->byte_order);
+}
+
+static dbus_bool_t
+find_field_for_modification (DBusHeader     *header,
+                             int             field,
+                             DBusTypeReader *reader,
+                             DBusTypeReader *realign_root)
+{
+  dbus_bool_t retval;
+
+  retval = FALSE;
+
+  _dbus_type_reader_init (realign_root,
+                          header->byte_order,
+                          &_dbus_header_signature_str,
+                          FIELDS_ARRAY_SIGNATURE_OFFSET,
+                          &header->data,
+                          FIELDS_ARRAY_LENGTH_OFFSET);
+
+  _dbus_type_reader_recurse (realign_root, reader);
+
+  while (_dbus_type_reader_get_current_type (reader) != DBUS_TYPE_INVALID)
+    {
+      DBusTypeReader sub;
+      unsigned char field_code;
+
+      _dbus_type_reader_recurse (reader, &sub);
+
+      _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE);
+      _dbus_type_reader_read_basic (&sub, &field_code);
+
+      if (field_code == (unsigned) field)
+        {
+          _dbus_assert (_dbus_type_reader_get_current_type (reader) == DBUS_TYPE_STRUCT);
+          retval = TRUE;
+          goto done;
+        }
+
+      _dbus_type_reader_next (reader);
+    }
+
+ done:
+  return retval;
+}
+
+/**
+ * Sets the value of a field with basic type. If the value is a string
+ * value, it isn't allowed to be #NULL. If the field doesn't exist,
+ * it will be created.
+ *
+ * @param header the header
+ * @param field the field to set
+ * @param type the type of the value
+ * @param value the value as for _dbus_marshal_set_basic()
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_header_set_field_basic (DBusHeader       *header,
+                              int               field,
+                              int               type,
+                              const void       *value)
+{
+  _dbus_assert (field <= DBUS_HEADER_FIELD_LAST);
+
+  if (!reserve_header_padding (header))
+    return FALSE;
+
+  /* If the field exists we set, otherwise we append */
+  if (_dbus_header_cache_check (header, field))
+    {
+      DBusTypeReader reader;
+      DBusTypeReader realign_root;
+
+      if (!find_field_for_modification (header, field,
+                                        &reader, &realign_root))
+        _dbus_assert_not_reached ("field was marked present in cache but wasn't found");
+
+      if (!set_basic_field (&reader, field, type, value, &realign_root))
+        return FALSE;
+    }
+  else
+    {
+      DBusTypeWriter writer;
+      DBusTypeWriter array;
+
+      _dbus_type_writer_init_values_only (&writer,
+                                          header->byte_order,
+                                          &_dbus_header_signature_str,
+                                          FIELDS_ARRAY_SIGNATURE_OFFSET,
+                                          &header->data,
+                                          FIELDS_ARRAY_LENGTH_OFFSET);
+
+      /* recurse into array without creating a new length, and jump to
+       * end of array.
+       */
+      if (!_dbus_type_writer_append_array (&writer,
+                                           &_dbus_header_signature_str,
+                                           FIELDS_ARRAY_ELEMENT_SIGNATURE_OFFSET,
+                                           &array))
+        _dbus_assert_not_reached ("recurse into ARRAY should not have used memory");
+
+      _dbus_assert (array.u.array.len_pos == FIELDS_ARRAY_LENGTH_OFFSET);
+      _dbus_assert (array.u.array.start_pos == FIRST_FIELD_OFFSET);
+      _dbus_assert (array.value_pos == HEADER_END_BEFORE_PADDING (header));
+
+      if (!write_basic_field (&array,
+                              field, type, value))
+        return FALSE;
+
+      if (!_dbus_type_writer_unrecurse (&writer, &array))
+        _dbus_assert_not_reached ("unrecurse from ARRAY should not have used memory");
+    }
+
+  correct_header_padding (header);
+
+  /* We could be smarter about this (only invalidate fields after the
+   * one we modified, or even only if the one we modified changed
+   * length). But this hack is a start.
+   */
+  _dbus_header_cache_invalidate_all (header);
+
+  return TRUE;
+}
+
+/**
+ * Gets the value of a field with basic type. If the field
+ * doesn't exist, returns #FALSE, otherwise returns #TRUE.
+ *
+ * @param header the header
+ * @param field the field to get
+ * @param type the type of the value
+ * @param value the value as for _dbus_marshal_read_basic()
+ * @returns #FALSE if the field doesn't exist
+ */
+dbus_bool_t
+_dbus_header_get_field_basic (DBusHeader    *header,
+                              int            field,
+                              int            type,
+                              void          *value)
+{
+  _dbus_assert (field != DBUS_HEADER_FIELD_INVALID);
+  _dbus_assert (field <= DBUS_HEADER_FIELD_LAST);
+  _dbus_assert (_dbus_header_field_types[field].code == field);
+  /* in light of this you might ask why the type is passed in;
+   * the only rationale I can think of is so the caller has
+   * to specify its expectation and breaks if we change it
+   */
+  _dbus_assert (type == EXPECTED_TYPE_OF_FIELD (field));
+
+  if (!_dbus_header_cache_check (header, field))
+    return FALSE;
+
+  _dbus_assert (header->fields[field].value_pos >= 0);
+
+  _dbus_marshal_read_basic (&header->data,
+                            header->fields[field].value_pos,
+                            type, value, header->byte_order,
+                            NULL);
+
+  return TRUE;
+}
+
+/**
+ * Gets the raw marshaled data for a field. If the field doesn't
+ * exist, returns #FALSE, otherwise returns #TRUE.  Returns the start
+ * of the marshaled data, i.e. usually the byte where the length
+ * starts (for strings and arrays) or for basic types just the value
+ * itself.
+ *
+ * @param header the header
+ * @param field the field to get
+ * @param str return location for the data string
+ * @param pos return location for start of field value
+ * @returns #FALSE if the field doesn't exist
+ */
+dbus_bool_t
+_dbus_header_get_field_raw (DBusHeader        *header,
+                            int                field,
+                            const DBusString **str,
+                            int               *pos)
+{
+  if (!_dbus_header_cache_check (header, field))
+    return FALSE;
+
+  if (str)
+    *str = &header->data;
+  if (pos)
+    *pos = header->fields[field].value_pos;
+
+  return TRUE;
+}
+
+/**
+ * Deletes a field, if it exists.
+ *
+ * @param header the header
+ * @param field the field to delete
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_header_delete_field (DBusHeader *header,
+                           int         field)
+{
+  DBusTypeReader reader;
+  DBusTypeReader realign_root;
+
+  if (_dbus_header_cache_known_nonexistent (header, field))
+    return TRUE; /* nothing to do */
+
+  /* Scan to the field we want, delete and realign, reappend
+   * padding. Field may turn out not to exist.
+   */
+  if (!find_field_for_modification (header, field,
+                                    &reader, &realign_root))
+    return TRUE; /* nothing to do */
+
+  if (!reserve_header_padding (header))
+    return FALSE;
+
+  if (!_dbus_type_reader_delete (&reader,
+                                 &realign_root))
+    return FALSE;
+
+  correct_header_padding (header);
+
+  _dbus_header_cache_invalidate_all (header);
+
+  _dbus_assert (!_dbus_header_cache_check (header, field)); /* Expensive assertion ... */
+
+  return TRUE;
+}
+
+/**
+ * Toggles a message flag bit, turning on the bit if value = TRUE and
+ * flipping it off if value = FALSE.
+ *
+ * @param header the header
+ * @param flag the message flag to toggle
+ * @param value toggle on or off
+ */
+void
+_dbus_header_toggle_flag (DBusHeader   *header,
+                          dbus_uint32_t flag,
+                          dbus_bool_t   value)
+{
+  unsigned char *flags_p;
+
+  flags_p = _dbus_string_get_data_len (&header->data, FLAGS_OFFSET, 1);
+
+  if (value)
+    *flags_p |= flag;
+  else
+    *flags_p &= ~flag;
+}
+
+/**
+ * Gets a message flag bit, returning TRUE if the bit is set.
+ *
+ * @param header the header
+ * @param flag the message flag to get
+ * @returns #TRUE if the flag is set
+ */
+dbus_bool_t
+_dbus_header_get_flag (DBusHeader   *header,
+                       dbus_uint32_t flag)
+{
+  const unsigned char *flags_p;
+
+  flags_p = _dbus_string_get_const_data_len (&header->data, FLAGS_OFFSET, 1);
+
+  return (*flags_p & flag) != 0;
+}
+
+/**
+ * Swaps the header into the given order if required.
+ *
+ * @param header the header
+ * @param new_order the new byte order
+ */
+void
+_dbus_header_byteswap (DBusHeader *header,
+                       int         new_order)
+{
+  if (header->byte_order == new_order)
+    return;
+
+  _dbus_marshal_byteswap (&_dbus_header_signature_str,
+                          0, header->byte_order,
+                          new_order,
+                          &header->data, 0);
+
+  header->byte_order = new_order;
+}
+
+/** @} */
+
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-test.h"
+#include <stdio.h>
+
+dbus_bool_t
+_dbus_marshal_header_test (void)
+{
+
+  return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-marshal-header.h b/src/dbus/dbus-marshal-header.h
new file mode 100644 (file)
index 0000000..52b6c73
--- /dev/null
@@ -0,0 +1,133 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-header.h  Managing marshaling/demarshaling of message headers
+ *
+ * Copyright (C) 2005  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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_MARSHAL_HEADER_H
+#define DBUS_MARSHAL_HEADER_H
+
+#include <config.h>
+#include <dbus/dbus-marshal-basic.h>
+#include <dbus/dbus-marshal-validate.h>
+
+#ifndef PACKAGE
+#error "config.h not included here"
+#endif
+
+typedef struct DBusHeader      DBusHeader;
+typedef struct DBusHeaderField DBusHeaderField;
+
+#define _DBUS_HEADER_FIELD_VALUE_UNKNOWN -1
+#define _DBUS_HEADER_FIELD_VALUE_NONEXISTENT -2
+
+/**
+ * Cached information about a header field in the message
+ */
+struct DBusHeaderField
+{
+  int            value_pos; /**< Position of field value, or -1/-2 */
+};
+
+/**
+ * Message header data and some cached details of it.
+ */
+struct DBusHeader
+{
+  DBusString data; /**< Header network data, stored
+                    * separately from body so we can
+                    * independently realloc it.
+                    */
+
+  DBusHeaderField fields[DBUS_HEADER_FIELD_LAST + 1]; /**< Track the location
+                                                       * of each field in header
+                                                       */
+
+  dbus_uint32_t padding : 3;        /**< bytes of alignment in header */
+  dbus_uint32_t byte_order : 8;     /**< byte order of header */
+};
+
+dbus_bool_t   _dbus_header_init                   (DBusHeader        *header,
+                                                   int                byte_order);
+void          _dbus_header_free                   (DBusHeader        *header);
+void          _dbus_header_reinit                 (DBusHeader        *header,
+                                                   int                byte_order);
+dbus_bool_t   _dbus_header_create                 (DBusHeader        *header,
+                                                   int                type,
+                                                   const char        *destination,
+                                                   const char        *path,
+                                                   const char        *interface,
+                                                   const char        *member,
+                                                   const char        *error_name);
+dbus_bool_t   _dbus_header_copy                   (const DBusHeader  *header,
+                                                   DBusHeader        *dest);
+int           _dbus_header_get_message_type       (DBusHeader        *header);
+void          _dbus_header_set_serial             (DBusHeader        *header,
+                                                   dbus_uint32_t      serial);
+dbus_uint32_t _dbus_header_get_serial             (DBusHeader        *header);
+void          _dbus_header_update_lengths         (DBusHeader        *header,
+                                                   int                body_len);
+dbus_bool_t   _dbus_header_set_field_basic        (DBusHeader        *header,
+                                                   int                field,
+                                                   int                type,
+                                                   const void        *value);
+dbus_bool_t   _dbus_header_get_field_basic        (DBusHeader        *header,
+                                                   int                field,
+                                                   int                type,
+                                                   void              *value);
+dbus_bool_t   _dbus_header_get_field_raw          (DBusHeader        *header,
+                                                   int                field,
+                                                   const DBusString **str,
+                                                   int               *pos);
+dbus_bool_t   _dbus_header_delete_field           (DBusHeader        *header,
+                                                   int                field);
+void          _dbus_header_toggle_flag            (DBusHeader        *header,
+                                                   dbus_uint32_t      flag,
+                                                   dbus_bool_t        value);
+dbus_bool_t   _dbus_header_get_flag               (DBusHeader        *header,
+                                                   dbus_uint32_t      flag);
+dbus_bool_t   _dbus_header_ensure_signature       (DBusHeader        *header,
+                                                   DBusString       **type_str,
+                                                   int               *type_pos);
+dbus_bool_t   _dbus_header_have_message_untrusted (int                max_message_length,
+                                                   DBusValidity      *validity,
+                                                   int               *byte_order,
+                                                   int               *fields_array_len,
+                                                   int               *header_len,
+                                                   int               *body_len,
+                                                   const DBusString  *str,
+                                                   int                start,
+                                                   int                len);
+dbus_bool_t   _dbus_header_load                   (DBusHeader        *header,
+                                                   DBusValidationMode mode,
+                                                   DBusValidity      *validity,
+                                                   int                byte_order,
+                                                   int                fields_array_len,
+                                                   int                header_len,
+                                                   int                body_len,
+                                                   const DBusString  *str,
+                                                   int                start,
+                                                   int                len);
+void          _dbus_header_byteswap               (DBusHeader        *header,
+                                                   int                new_order);
+
+
+
+#endif /* DBUS_MARSHAL_HEADER_H */
diff --git a/src/dbus/dbus-marshal-recursive-util.c b/src/dbus/dbus-marshal-recursive-util.c
new file mode 100644 (file)
index 0000000..c8ae93d
--- /dev/null
@@ -0,0 +1,3565 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-recursive-util.c  Would be in dbus-marshal-recursive.c, but only used in bus/tests
+ *
+ * Copyright (C) 2004, 2005 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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 <config.h>
+
+#ifdef DBUS_BUILD_TESTS
+
+#include "dbus-marshal-recursive.h"
+#include "dbus-marshal-basic.h"
+#include "dbus-signature.h"
+#include "dbus-internals.h"
+#include <string.h>
+
+static void
+basic_value_zero (DBusBasicValue *value)
+{
+
+#ifdef DBUS_HAVE_INT64
+  value->u64 = 0;
+#else
+  value->u64.first32 = 0;
+  value->u64.second32 = 0;
+#endif
+}
+
+static dbus_bool_t
+basic_value_equal (int             type,
+                   DBusBasicValue *lhs,
+                   DBusBasicValue *rhs)
+{
+  if (type == DBUS_TYPE_STRING ||
+      type == DBUS_TYPE_SIGNATURE ||
+      type == DBUS_TYPE_OBJECT_PATH)
+    {
+      return strcmp (lhs->str, rhs->str) == 0;
+    }
+  else
+    {
+#ifdef DBUS_HAVE_INT64
+      return lhs->u64 == rhs->u64;
+#else
+      return lhs->u64.first32 == rhs->u64.first32 &&
+        lhs->u64.second32 == rhs->u64.second32;
+#endif
+    }
+}
+
+static dbus_bool_t
+equal_values_helper (DBusTypeReader *lhs,
+                     DBusTypeReader *rhs)
+{
+  int lhs_type;
+  int rhs_type;
+
+  lhs_type = _dbus_type_reader_get_current_type (lhs);
+  rhs_type = _dbus_type_reader_get_current_type (rhs);
+
+  if (lhs_type != rhs_type)
+    return FALSE;
+
+  if (lhs_type == DBUS_TYPE_INVALID)
+    return TRUE;
+
+  if (dbus_type_is_basic (lhs_type))
+    {
+      DBusBasicValue lhs_value;
+      DBusBasicValue rhs_value;
+
+      basic_value_zero (&lhs_value);
+      basic_value_zero (&rhs_value);
+      
+      _dbus_type_reader_read_basic (lhs, &lhs_value);
+      _dbus_type_reader_read_basic (rhs, &rhs_value);
+
+      return basic_value_equal (lhs_type, &lhs_value, &rhs_value);
+    }
+  else
+    {
+      DBusTypeReader lhs_sub;
+      DBusTypeReader rhs_sub;
+
+      _dbus_type_reader_recurse (lhs, &lhs_sub);
+      _dbus_type_reader_recurse (rhs, &rhs_sub);
+
+      return equal_values_helper (&lhs_sub, &rhs_sub);
+    }
+}
+
+/**
+ * See whether the two readers point to identical data blocks.
+ *
+ * @param lhs reader 1
+ * @param rhs reader 2
+ * @returns #TRUE if the data blocks have the same values
+ */
+dbus_bool_t
+_dbus_type_reader_equal_values (const DBusTypeReader *lhs,
+                                const DBusTypeReader *rhs)
+{
+  DBusTypeReader copy_lhs = *lhs;
+  DBusTypeReader copy_rhs = *rhs;
+
+  return equal_values_helper (&copy_lhs, &copy_rhs);
+}
+
+/* TESTS */
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+#include "dbus-test.h"
+#include "dbus-list.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Whether to do the OOM stuff (only with other expensive tests) */
+#define TEST_OOM_HANDLING 0
+/* We do start offset 0 through 9, to get various alignment cases. Still this
+ * obviously makes the test suite run 10x as slow.
+ */
+#define MAX_INITIAL_OFFSET 9
+
+/* Largest iteration count to test copying, realignment,
+ * etc. with. i.e. we only test this stuff with some of the smaller
+ * data sets.
+ */
+#define MAX_ITERATIONS_FOR_EXPENSIVE_TESTS 1000
+
+typedef struct
+{
+  int byte_order;
+  int initial_offset;
+  DBusString signature;
+  DBusString body;
+} DataBlock;
+
+typedef struct
+{
+  int saved_sig_len;
+  int saved_body_len;
+} DataBlockState;
+
+#define N_FENCE_BYTES 5
+#define FENCE_BYTES_STR "abcde"
+#define INITIAL_PADDING_BYTE '\0'
+
+static dbus_bool_t
+data_block_init (DataBlock *block,
+                 int        byte_order,
+                 int        initial_offset)
+{
+  if (!_dbus_string_init (&block->signature))
+    return FALSE;
+
+  if (!_dbus_string_init (&block->body))
+    {
+      _dbus_string_free (&block->signature);
+      return FALSE;
+    }
+
+  if (!_dbus_string_insert_bytes (&block->signature, 0, initial_offset,
+                                  INITIAL_PADDING_BYTE) ||
+      !_dbus_string_insert_bytes (&block->body, 0, initial_offset,
+                                  INITIAL_PADDING_BYTE) ||
+      !_dbus_string_append (&block->signature, FENCE_BYTES_STR) ||
+      !_dbus_string_append (&block->body, FENCE_BYTES_STR))
+    {
+      _dbus_string_free (&block->signature);
+      _dbus_string_free (&block->body);
+      return FALSE;
+    }
+
+  block->byte_order = byte_order;
+  block->initial_offset = initial_offset;
+
+  return TRUE;
+}
+
+static void
+data_block_save (DataBlock      *block,
+                 DataBlockState *state)
+{
+  state->saved_sig_len = _dbus_string_get_length (&block->signature) - N_FENCE_BYTES;
+  state->saved_body_len = _dbus_string_get_length (&block->body) - N_FENCE_BYTES;
+}
+
+static void
+data_block_restore (DataBlock      *block,
+                    DataBlockState *state)
+{
+  _dbus_string_delete (&block->signature,
+                       state->saved_sig_len,
+                       _dbus_string_get_length (&block->signature) - state->saved_sig_len - N_FENCE_BYTES);
+  _dbus_string_delete (&block->body,
+                       state->saved_body_len,
+                       _dbus_string_get_length (&block->body) - state->saved_body_len - N_FENCE_BYTES);
+}
+
+static void
+data_block_verify (DataBlock *block)
+{
+  if (!_dbus_string_ends_with_c_str (&block->signature,
+                                     FENCE_BYTES_STR))
+    {
+      int offset;
+
+      offset = _dbus_string_get_length (&block->signature) - N_FENCE_BYTES - 8;
+      if (offset < 0)
+        offset = 0;
+
+      _dbus_verbose_bytes_of_string (&block->signature,
+                                     offset,
+                                     _dbus_string_get_length (&block->signature) - offset);
+      _dbus_assert_not_reached ("block did not verify: bad bytes at end of signature");
+    }
+  if (!_dbus_string_ends_with_c_str (&block->body,
+                                     FENCE_BYTES_STR))
+    {
+      int offset;
+
+      offset = _dbus_string_get_length (&block->body) - N_FENCE_BYTES - 8;
+      if (offset < 0)
+        offset = 0;
+
+      _dbus_verbose_bytes_of_string (&block->body,
+                                     offset,
+                                     _dbus_string_get_length (&block->body) - offset);
+      _dbus_assert_not_reached ("block did not verify: bad bytes at end of body");
+    }
+
+  _dbus_assert (_dbus_string_validate_nul (&block->signature,
+                                           0, block->initial_offset));
+  _dbus_assert (_dbus_string_validate_nul (&block->body,
+                                           0, block->initial_offset));
+}
+
+static void
+data_block_free (DataBlock *block)
+{
+  data_block_verify (block);
+
+  _dbus_string_free (&block->signature);
+  _dbus_string_free (&block->body);
+}
+
+static void
+data_block_reset (DataBlock *block)
+{
+  data_block_verify (block);
+
+  _dbus_string_delete (&block->signature,
+                       block->initial_offset,
+                       _dbus_string_get_length (&block->signature) - N_FENCE_BYTES - block->initial_offset);
+  _dbus_string_delete (&block->body,
+                       block->initial_offset,
+                       _dbus_string_get_length (&block->body) - N_FENCE_BYTES - block->initial_offset);
+
+  data_block_verify (block);
+}
+
+static void
+data_block_init_reader_writer (DataBlock      *block,
+                               DBusTypeReader *reader,
+                               DBusTypeWriter *writer)
+{
+  if (reader)
+    _dbus_type_reader_init (reader,
+                            block->byte_order,
+                            &block->signature,
+                            block->initial_offset,
+                            &block->body,
+                            block->initial_offset);
+
+  if (writer)
+    _dbus_type_writer_init (writer,
+                            block->byte_order,
+                            &block->signature,
+                            _dbus_string_get_length (&block->signature) - N_FENCE_BYTES,
+                            &block->body,
+                            _dbus_string_get_length (&block->body) - N_FENCE_BYTES);
+}
+
+static void
+real_check_expected_type (DBusTypeReader *reader,
+                          int             expected,
+                          const char     *funcname,
+                          int             line)
+{
+  int t;
+
+  t = _dbus_type_reader_get_current_type (reader);
+
+  if (t != expected)
+    {
+      _dbus_warn ("Read type %s while expecting %s at %s line %d\n",
+                  _dbus_type_to_string (t),
+                  _dbus_type_to_string (expected),
+                  funcname, line);
+
+      _dbus_assert_not_reached ("read wrong type");
+    }
+}
+
+#define check_expected_type(reader, expected) real_check_expected_type (reader, expected, _DBUS_FUNCTION_NAME, __LINE__)
+
+#define NEXT_EXPECTING_TRUE(reader)  do { if (!_dbus_type_reader_next (reader))         \
+ {                                                                                      \
+    _dbus_warn ("_dbus_type_reader_next() should have returned TRUE at %s %d\n",        \
+                              _DBUS_FUNCTION_NAME, __LINE__);                           \
+    _dbus_assert_not_reached ("test failed");                                           \
+ }                                                                                      \
+} while (0)
+
+#define NEXT_EXPECTING_FALSE(reader) do { if (_dbus_type_reader_next (reader))          \
+ {                                                                                      \
+    _dbus_warn ("_dbus_type_reader_next() should have returned FALSE at %s %d\n",       \
+                              _DBUS_FUNCTION_NAME, __LINE__);                           \
+    _dbus_assert_not_reached ("test failed");                                           \
+ }                                                                                      \
+ check_expected_type (reader, DBUS_TYPE_INVALID);                                       \
+} while (0)
+
+typedef struct TestTypeNode               TestTypeNode;
+typedef struct TestTypeNodeClass          TestTypeNodeClass;
+typedef struct TestTypeNodeContainer      TestTypeNodeContainer;
+typedef struct TestTypeNodeContainerClass TestTypeNodeContainerClass;
+
+struct TestTypeNode
+{
+  const TestTypeNodeClass *klass;
+};
+
+struct TestTypeNodeContainer
+{
+  TestTypeNode base;
+  DBusList    *children;
+};
+
+struct TestTypeNodeClass
+{
+  int typecode;
+
+  int instance_size;
+
+  int subclass_detail; /* a bad hack to avoid a bunch of subclass casting */
+
+  dbus_bool_t   (* construct)     (TestTypeNode   *node);
+  void          (* destroy)       (TestTypeNode   *node);
+
+  dbus_bool_t (* write_value)     (TestTypeNode   *node,
+                                   DataBlock      *block,
+                                   DBusTypeWriter *writer,
+                                   int             seed);
+  dbus_bool_t (* read_value)      (TestTypeNode   *node,
+                                   DBusTypeReader *reader,
+                                   int             seed);
+  dbus_bool_t (* set_value)       (TestTypeNode   *node,
+                                   DBusTypeReader *reader,
+                                   DBusTypeReader *realign_root,
+                                   int             seed);
+  dbus_bool_t (* build_signature) (TestTypeNode   *node,
+                                   DBusString     *str);
+  dbus_bool_t (* write_multi)     (TestTypeNode   *node,
+                                   DataBlock      *block,
+                                   DBusTypeWriter *writer,
+                                   int             seed,
+                                   int             count);
+  dbus_bool_t (* read_multi)      (TestTypeNode   *node,
+                                   DBusTypeReader *reader,
+                                   int             seed,
+                                   int             count);
+};
+
+struct TestTypeNodeContainerClass
+{
+  TestTypeNodeClass base;
+};
+
+/* FIXME this could be chilled out substantially by unifying
+ * the basic types into basic_write_value/basic_read_value
+ * and by merging read_value and set_value into one function
+ * taking a flag argument.
+ */
+static dbus_bool_t int16_write_value       (TestTypeNode   *node,
+                                            DataBlock      *block,
+                                            DBusTypeWriter *writer,
+                                            int             seed);
+static dbus_bool_t int16_read_value        (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            int             seed);
+static dbus_bool_t int16_set_value         (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            DBusTypeReader *realign_root,
+                                            int             seed);
+static dbus_bool_t int16_write_multi       (TestTypeNode   *node,
+                                            DataBlock      *block,
+                                            DBusTypeWriter *writer,
+                                            int             seed,
+                                            int             count);
+static dbus_bool_t int16_read_multi        (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            int             seed,
+                                            int             count);
+static dbus_bool_t int32_write_value       (TestTypeNode   *node,
+                                            DataBlock      *block,
+                                            DBusTypeWriter *writer,
+                                            int             seed);
+static dbus_bool_t int32_read_value        (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            int             seed);
+static dbus_bool_t int32_set_value         (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            DBusTypeReader *realign_root,
+                                            int             seed);
+static dbus_bool_t int32_write_multi       (TestTypeNode   *node,
+                                            DataBlock      *block,
+                                            DBusTypeWriter *writer,
+                                            int             seed,
+                                            int             count);
+static dbus_bool_t int32_read_multi        (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            int             seed,
+                                            int             count);
+static dbus_bool_t int64_write_value       (TestTypeNode   *node,
+                                            DataBlock      *block,
+                                            DBusTypeWriter *writer,
+                                            int             seed);
+static dbus_bool_t int64_read_value        (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            int             seed);
+static dbus_bool_t int64_set_value         (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            DBusTypeReader *realign_root,
+                                            int             seed);
+static dbus_bool_t string_write_value      (TestTypeNode   *node,
+                                            DataBlock      *block,
+                                            DBusTypeWriter *writer,
+                                            int             seed);
+static dbus_bool_t string_read_value       (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            int             seed);
+static dbus_bool_t string_set_value        (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            DBusTypeReader *realign_root,
+                                            int             seed);
+static dbus_bool_t bool_write_value        (TestTypeNode   *node,
+                                            DataBlock      *block,
+                                            DBusTypeWriter *writer,
+                                            int             seed);
+static dbus_bool_t bool_read_value         (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            int             seed);
+static dbus_bool_t bool_set_value          (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            DBusTypeReader *realign_root,
+                                            int             seed);
+static dbus_bool_t byte_write_value        (TestTypeNode   *node,
+                                            DataBlock      *block,
+                                            DBusTypeWriter *writer,
+                                            int             seed);
+static dbus_bool_t byte_read_value         (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            int             seed);
+static dbus_bool_t byte_set_value          (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            DBusTypeReader *realign_root,
+                                            int             seed);
+static dbus_bool_t double_write_value      (TestTypeNode   *node,
+                                            DataBlock      *block,
+                                            DBusTypeWriter *writer,
+                                            int             seed);
+static dbus_bool_t double_read_value       (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            int             seed);
+static dbus_bool_t double_set_value        (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            DBusTypeReader *realign_root,
+                                            int             seed);
+static dbus_bool_t object_path_write_value (TestTypeNode   *node,
+                                            DataBlock      *block,
+                                            DBusTypeWriter *writer,
+                                            int             seed);
+static dbus_bool_t object_path_read_value  (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            int             seed);
+static dbus_bool_t object_path_set_value   (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            DBusTypeReader *realign_root,
+                                            int             seed);
+static dbus_bool_t signature_write_value   (TestTypeNode   *node,
+                                            DataBlock      *block,
+                                            DBusTypeWriter *writer,
+                                            int             seed);
+static dbus_bool_t signature_read_value    (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            int             seed);
+static dbus_bool_t signature_set_value     (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            DBusTypeReader *realign_root,
+                                            int             seed);
+static dbus_bool_t struct_write_value      (TestTypeNode   *node,
+                                            DataBlock      *block,
+                                            DBusTypeWriter *writer,
+                                            int             seed);
+static dbus_bool_t struct_read_value       (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            int             seed);
+static dbus_bool_t struct_set_value        (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            DBusTypeReader *realign_root,
+                                            int             seed);
+static dbus_bool_t struct_build_signature  (TestTypeNode   *node,
+                                            DBusString     *str);
+static dbus_bool_t dict_write_value        (TestTypeNode   *node,
+                                            DataBlock      *block,
+                                            DBusTypeWriter *writer,
+                                            int             seed);
+static dbus_bool_t dict_read_value         (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            int             seed);
+static dbus_bool_t dict_set_value          (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            DBusTypeReader *realign_root,
+                                            int             seed);
+static dbus_bool_t dict_build_signature    (TestTypeNode   *node,
+                                            DBusString     *str);
+static dbus_bool_t array_write_value       (TestTypeNode   *node,
+                                            DataBlock      *block,
+                                            DBusTypeWriter *writer,
+                                            int             seed);
+static dbus_bool_t array_read_value        (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            int             seed);
+static dbus_bool_t array_set_value         (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            DBusTypeReader *realign_root,
+                                            int             seed);
+static dbus_bool_t array_build_signature   (TestTypeNode   *node,
+                                            DBusString     *str);
+static dbus_bool_t variant_write_value     (TestTypeNode   *node,
+                                            DataBlock      *block,
+                                            DBusTypeWriter *writer,
+                                            int             seed);
+static dbus_bool_t variant_read_value      (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            int             seed);
+static dbus_bool_t variant_set_value       (TestTypeNode   *node,
+                                            DBusTypeReader *reader,
+                                            DBusTypeReader *realign_root,
+                                            int             seed);
+static void        container_destroy       (TestTypeNode   *node);
+
+
+
+static const TestTypeNodeClass int16_class = {
+  DBUS_TYPE_INT16,
+  sizeof (TestTypeNode),
+  0,
+  NULL,
+  NULL,
+  int16_write_value,
+  int16_read_value,
+  int16_set_value,
+  NULL,
+  int16_write_multi,
+  int16_read_multi
+};
+
+static const TestTypeNodeClass uint16_class = {
+  DBUS_TYPE_UINT16,
+  sizeof (TestTypeNode),
+  0,
+  NULL,
+  NULL,
+  int16_write_value, /* recycle from int16 */
+  int16_read_value,  /* recycle from int16 */
+  int16_set_value,   /* recycle from int16 */
+  NULL,
+  int16_write_multi, /* recycle from int16 */
+  int16_read_multi   /* recycle from int16 */
+};
+
+static const TestTypeNodeClass int32_class = {
+  DBUS_TYPE_INT32,
+  sizeof (TestTypeNode),
+  0,
+  NULL,
+  NULL,
+  int32_write_value,
+  int32_read_value,
+  int32_set_value,
+  NULL,
+  int32_write_multi,
+  int32_read_multi
+};
+
+static const TestTypeNodeClass uint32_class = {
+  DBUS_TYPE_UINT32,
+  sizeof (TestTypeNode),
+  0,
+  NULL,
+  NULL,
+  int32_write_value, /* recycle from int32 */
+  int32_read_value,  /* recycle from int32 */
+  int32_set_value,   /* recycle from int32 */
+  NULL,
+  int32_write_multi, /* recycle from int32 */
+  int32_read_multi   /* recycle from int32 */
+};
+
+static const TestTypeNodeClass int64_class = {
+  DBUS_TYPE_INT64,
+  sizeof (TestTypeNode),
+  0,
+  NULL,
+  NULL,
+  int64_write_value,
+  int64_read_value,
+  int64_set_value,
+  NULL,
+  NULL, /* FIXME */
+  NULL  /* FIXME */
+};
+
+static const TestTypeNodeClass uint64_class = {
+  DBUS_TYPE_UINT64,
+  sizeof (TestTypeNode),
+  0,
+  NULL,
+  NULL,
+  int64_write_value, /* recycle from int64 */
+  int64_read_value,  /* recycle from int64 */
+  int64_set_value,   /* recycle from int64 */
+  NULL,
+  NULL, /* FIXME */
+  NULL  /* FIXME */
+};
+
+static const TestTypeNodeClass string_0_class = {
+  DBUS_TYPE_STRING,
+  sizeof (TestTypeNode),
+  0, /* string length */
+  NULL,
+  NULL,
+  string_write_value,
+  string_read_value,
+  string_set_value,
+  NULL,
+  NULL,
+  NULL
+};
+
+static const TestTypeNodeClass string_1_class = {
+  DBUS_TYPE_STRING,
+  sizeof (TestTypeNode),
+  1, /* string length */
+  NULL,
+  NULL,
+  string_write_value,
+  string_read_value,
+  string_set_value,
+  NULL,
+  NULL,
+  NULL
+};
+
+/* with nul, a len 3 string should fill 4 bytes and thus is "special" */
+static const TestTypeNodeClass string_3_class = {
+  DBUS_TYPE_STRING,
+  sizeof (TestTypeNode),
+  3, /* string length */
+  NULL,
+  NULL,
+  string_write_value,
+  string_read_value,
+  string_set_value,
+  NULL,
+  NULL,
+  NULL
+};
+
+/* with nul, a len 8 string should fill 9 bytes and thus is "special" (far-fetched I suppose) */
+static const TestTypeNodeClass string_8_class = {
+  DBUS_TYPE_STRING,
+  sizeof (TestTypeNode),
+  8, /* string length */
+  NULL,
+  NULL,
+  string_write_value,
+  string_read_value,
+  string_set_value,
+  NULL,
+  NULL,
+  NULL
+};
+
+static const TestTypeNodeClass bool_class = {
+  DBUS_TYPE_BOOLEAN,
+  sizeof (TestTypeNode),
+  0,
+  NULL,
+  NULL,
+  bool_write_value,
+  bool_read_value,
+  bool_set_value,
+  NULL,
+  NULL, /* FIXME */
+  NULL  /* FIXME */
+};
+
+static const TestTypeNodeClass byte_class = {
+  DBUS_TYPE_BYTE,
+  sizeof (TestTypeNode),
+  0,
+  NULL,
+  NULL,
+  byte_write_value,
+  byte_read_value,
+  byte_set_value,
+  NULL,
+  NULL, /* FIXME */
+  NULL  /* FIXME */
+};
+
+static const TestTypeNodeClass double_class = {
+  DBUS_TYPE_DOUBLE,
+  sizeof (TestTypeNode),
+  0,
+  NULL,
+  NULL,
+  double_write_value,
+  double_read_value,
+  double_set_value,
+  NULL,
+  NULL, /* FIXME */
+  NULL  /* FIXME */
+};
+
+static const TestTypeNodeClass object_path_class = {
+  DBUS_TYPE_OBJECT_PATH,
+  sizeof (TestTypeNode),
+  0,
+  NULL,
+  NULL,
+  object_path_write_value,
+  object_path_read_value,
+  object_path_set_value,
+  NULL,
+  NULL,
+  NULL
+};
+
+static const TestTypeNodeClass signature_class = {
+  DBUS_TYPE_SIGNATURE,
+  sizeof (TestTypeNode),
+  0,
+  NULL,
+  NULL,
+  signature_write_value,
+  signature_read_value,
+  signature_set_value,
+  NULL,
+  NULL,
+  NULL
+};
+
+static const TestTypeNodeClass struct_1_class = {
+  DBUS_TYPE_STRUCT,
+  sizeof (TestTypeNodeContainer),
+  1, /* number of times children appear as fields */
+  NULL,
+  container_destroy,
+  struct_write_value,
+  struct_read_value,
+  struct_set_value,
+  struct_build_signature,
+  NULL,
+  NULL
+};
+
+static const TestTypeNodeClass struct_2_class = {
+  DBUS_TYPE_STRUCT,
+  sizeof (TestTypeNodeContainer),
+  2, /* number of times children appear as fields */
+  NULL,
+  container_destroy,
+  struct_write_value,
+  struct_read_value,
+  struct_set_value,
+  struct_build_signature,
+  NULL,
+  NULL
+};
+
+static const TestTypeNodeClass dict_1_class = {
+  DBUS_TYPE_ARRAY, /* this is correct, a dict is an array of dict entry */
+  sizeof (TestTypeNodeContainer),
+  1, /* number of entries */
+  NULL,
+  container_destroy,
+  dict_write_value,
+  dict_read_value,
+  dict_set_value,
+  dict_build_signature,
+  NULL,
+  NULL
+};
+
+static dbus_bool_t arrays_write_fixed_in_blocks = FALSE;
+
+static const TestTypeNodeClass array_0_class = {
+  DBUS_TYPE_ARRAY,
+  sizeof (TestTypeNodeContainer),
+  0, /* number of array elements */
+  NULL,
+  container_destroy,
+  array_write_value,
+  array_read_value,
+  array_set_value,
+  array_build_signature,
+  NULL,
+  NULL
+};
+
+static const TestTypeNodeClass array_1_class = {
+  DBUS_TYPE_ARRAY,
+  sizeof (TestTypeNodeContainer),
+  1, /* number of array elements */
+  NULL,
+  container_destroy,
+  array_write_value,
+  array_read_value,
+  array_set_value,
+  array_build_signature,
+  NULL,
+  NULL
+};
+
+static const TestTypeNodeClass array_2_class = {
+  DBUS_TYPE_ARRAY,
+  sizeof (TestTypeNodeContainer),
+  2, /* number of array elements */
+  NULL,
+  container_destroy,
+  array_write_value,
+  array_read_value,
+  array_set_value,
+  array_build_signature,
+  NULL,
+  NULL
+};
+
+static const TestTypeNodeClass array_9_class = {
+  DBUS_TYPE_ARRAY,
+  sizeof (TestTypeNodeContainer),
+  9, /* number of array elements */
+  NULL,
+  container_destroy,
+  array_write_value,
+  array_read_value,
+  array_set_value,
+  array_build_signature,
+  NULL,
+  NULL
+};
+
+static const TestTypeNodeClass variant_class = {
+  DBUS_TYPE_VARIANT,
+  sizeof (TestTypeNodeContainer),
+  0,
+  NULL,
+  container_destroy,
+  variant_write_value,
+  variant_read_value,
+  variant_set_value,
+  NULL,
+  NULL,
+  NULL
+};
+
+static const TestTypeNodeClass* const
+basic_nodes[] = {
+  &int16_class,
+  &uint16_class,
+  &int32_class,
+  &uint32_class,
+  &int64_class,
+  &uint64_class,
+  &bool_class,
+  &byte_class,
+  &double_class,
+  &string_0_class,
+  &string_1_class,
+  &string_3_class,
+  &string_8_class,
+  &object_path_class,
+  &signature_class
+};
+#define N_BASICS (_DBUS_N_ELEMENTS (basic_nodes))
+
+static const TestTypeNodeClass* const
+container_nodes[] = {
+  &struct_1_class,
+  &array_1_class,
+  &struct_2_class,
+  &array_0_class,
+  &array_2_class,
+  &variant_class,
+  &dict_1_class /* last since we want struct and array before it */
+  /* array_9_class is omitted on purpose, it's too slow;
+   * we only use it in one hardcoded test below
+   */
+};
+#define N_CONTAINERS (_DBUS_N_ELEMENTS (container_nodes))
+
+static TestTypeNode*
+node_new (const TestTypeNodeClass *klass)
+{
+  TestTypeNode *node;
+
+  node = dbus_malloc0 (klass->instance_size);
+  if (node == NULL)
+    return NULL;
+
+  node->klass = klass;
+
+  if (klass->construct)
+    {
+      if (!(* klass->construct) (node))
+        {
+          dbus_free (node);
+          return NULL;
+        }
+    }
+
+  return node;
+}
+
+static void
+node_destroy (TestTypeNode *node)
+{
+  if (node->klass->destroy)
+    (* node->klass->destroy) (node);
+  dbus_free (node);
+}
+
+static dbus_bool_t
+node_write_value (TestTypeNode   *node,
+                  DataBlock      *block,
+                  DBusTypeWriter *writer,
+                  int             seed)
+{
+  dbus_bool_t retval;
+
+  retval = (* node->klass->write_value) (node, block, writer, seed);
+
+#if 0
+  /* Handy to see where things break, but too expensive to do all the time */
+  data_block_verify (block);
+#endif
+
+  return retval;
+}
+
+static dbus_bool_t
+node_read_value (TestTypeNode   *node,
+                 DBusTypeReader *reader,
+                 int             seed)
+{
+  /* DBusTypeReader restored; */
+
+  if (!(* node->klass->read_value) (node, reader, seed))
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Warning: if this one fails due to OOM, it has side effects (can
+ * modify only some of the sub-values). OK in a test suite, but we
+ * never do this in real code.
+ */
+static dbus_bool_t
+node_set_value (TestTypeNode   *node,
+                DBusTypeReader *reader,
+                DBusTypeReader *realign_root,
+                int             seed)
+{
+  if (!(* node->klass->set_value) (node, reader, realign_root, seed))
+    return FALSE;
+
+  return TRUE;
+}
+
+static dbus_bool_t
+node_build_signature (TestTypeNode *node,
+                      DBusString   *str)
+{
+  if (node->klass->build_signature)
+    return (* node->klass->build_signature) (node, str);
+  else
+    return _dbus_string_append_byte (str, node->klass->typecode);
+}
+
+static dbus_bool_t
+node_append_child (TestTypeNode *node,
+                   TestTypeNode *child)
+{
+  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+
+  _dbus_assert (node->klass->instance_size >= (int) sizeof (TestTypeNodeContainer));
+
+  if (!_dbus_list_append (&container->children, child))
+    _dbus_assert_not_reached ("no memory"); /* we never check the return value on node_append_child anyhow - it's run from outside the malloc-failure test code */
+
+  return TRUE;
+}
+
+static dbus_bool_t
+node_write_multi (TestTypeNode   *node,
+                  DataBlock      *block,
+                  DBusTypeWriter *writer,
+                  int             seed,
+                  int             n_copies)
+{
+  dbus_bool_t retval;
+
+  _dbus_assert (node->klass->write_multi != NULL);
+  retval = (* node->klass->write_multi) (node, block, writer, seed, n_copies);
+
+#if 0
+  /* Handy to see where things break, but too expensive to do all the time */
+  data_block_verify (block);
+#endif
+
+  return retval;
+}
+
+static dbus_bool_t
+node_read_multi (TestTypeNode   *node,
+                 DBusTypeReader *reader,
+                 int             seed,
+                 int             n_copies)
+{
+  _dbus_assert (node->klass->read_multi != NULL);
+
+  if (!(* node->klass->read_multi) (node, reader, seed, n_copies))
+    return FALSE;
+
+  return TRUE;
+}
+
+static int n_iterations_completed_total = 0;
+static int n_iterations_completed_this_test = 0;
+static int n_iterations_expected_this_test = 0;
+
+typedef struct
+{
+  const DBusString   *signature;
+  DataBlock          *block;
+  int                 type_offset;
+  TestTypeNode      **nodes;
+  int                 n_nodes;
+} NodeIterationData;
+
+static dbus_bool_t
+run_test_copy (NodeIterationData *nid)
+{
+  DataBlock *src;
+  DataBlock dest;
+  dbus_bool_t retval;
+  DBusTypeReader reader;
+  DBusTypeWriter writer;
+
+  _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME);
+
+  src = nid->block;
+
+  retval = FALSE;
+
+  if (!data_block_init (&dest, src->byte_order, src->initial_offset))
+    return FALSE;
+
+  data_block_init_reader_writer (src, &reader, NULL);
+  data_block_init_reader_writer (&dest, NULL, &writer);
+
+  /* DBusTypeWriter assumes it's writing into an existing signature,
+   * so doesn't add nul on its own. We have to do that.
+   */
+  if (!_dbus_string_insert_byte (&dest.signature,
+                                 dest.initial_offset, '\0'))
+    goto out;
+
+  if (!_dbus_type_writer_write_reader (&writer, &reader))
+    goto out;
+
+  /* Data blocks should now be identical */
+  if (!_dbus_string_equal (&src->signature, &dest.signature))
+    {
+      _dbus_verbose ("SOURCE\n");
+      _dbus_verbose_bytes_of_string (&src->signature, 0,
+                                     _dbus_string_get_length (&src->signature));
+      _dbus_verbose ("DEST\n");
+      _dbus_verbose_bytes_of_string (&dest.signature, 0,
+                                     _dbus_string_get_length (&dest.signature));
+      _dbus_assert_not_reached ("signatures did not match");
+    }
+
+  if (!_dbus_string_equal (&src->body, &dest.body))
+    {
+      _dbus_verbose ("SOURCE\n");
+      _dbus_verbose_bytes_of_string (&src->body, 0,
+                                     _dbus_string_get_length (&src->body));
+      _dbus_verbose ("DEST\n");
+      _dbus_verbose_bytes_of_string (&dest.body, 0,
+                                     _dbus_string_get_length (&dest.body));
+      _dbus_assert_not_reached ("bodies did not match");
+    }
+
+  retval = TRUE;
+
+ out:
+
+  data_block_free (&dest);
+
+  return retval;
+}
+
+static dbus_bool_t
+run_test_values_only_write (NodeIterationData *nid)
+{
+  DBusTypeReader reader;
+  DBusTypeWriter writer;
+  int i;
+  dbus_bool_t retval;
+  int sig_len;
+
+  _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME);
+
+  retval = FALSE;
+
+  data_block_reset (nid->block);
+
+  sig_len = _dbus_string_get_length (nid->signature);
+
+  _dbus_type_writer_init_values_only (&writer,
+                                      nid->block->byte_order,
+                                      nid->signature, 0,
+                                      &nid->block->body,
+                                      _dbus_string_get_length (&nid->block->body) - N_FENCE_BYTES);
+  _dbus_type_reader_init (&reader,
+                          nid->block->byte_order,
+                          nid->signature, 0,
+                          &nid->block->body,
+                          nid->block->initial_offset);
+
+  i = 0;
+  while (i < nid->n_nodes)
+    {
+      if (!node_write_value (nid->nodes[i], nid->block, &writer, i))
+        goto out;
+
+      ++i;
+    }
+
+  /* if we wrote any typecodes then this would fail */
+  _dbus_assert (sig_len == _dbus_string_get_length (nid->signature));
+
+  /* But be sure we wrote out the values correctly */
+  i = 0;
+  while (i < nid->n_nodes)
+    {
+      if (!node_read_value (nid->nodes[i], &reader, i))
+        goto out;
+
+      if (i + 1 == nid->n_nodes)
+        NEXT_EXPECTING_FALSE (&reader);
+      else
+        NEXT_EXPECTING_TRUE (&reader);
+
+      ++i;
+    }
+
+  retval = TRUE;
+
+ out:
+  data_block_reset (nid->block);
+  return retval;
+}
+
+/* offset the seed for setting, so we set different numbers than
+ * we originally wrote. Don't offset by a huge number since in
+ * some cases it's value = possibilities[seed % n_possibilities]
+ * and we don't want to wrap around. bool_from_seed
+ * is just seed % 2 even.
+ */
+#define SET_SEED 1
+static dbus_bool_t
+run_test_set_values (NodeIterationData *nid)
+{
+  DBusTypeReader reader;
+  DBusTypeReader realign_root;
+  dbus_bool_t retval;
+  int i;
+
+  _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME);
+
+  retval = FALSE;
+
+  data_block_init_reader_writer (nid->block,
+                                 &reader, NULL);
+
+  realign_root = reader;
+
+  i = 0;
+  while (i < nid->n_nodes)
+    {
+      if (!node_set_value (nid->nodes[i],
+                           &reader, &realign_root,
+                           i + SET_SEED))
+        goto out;
+
+      if (i + 1 == nid->n_nodes)
+        NEXT_EXPECTING_FALSE (&reader);
+      else
+        NEXT_EXPECTING_TRUE (&reader);
+
+      ++i;
+    }
+
+  /* Check that the new values were set */
+
+  reader = realign_root;
+
+  i = 0;
+  while (i < nid->n_nodes)
+    {
+      if (!node_read_value (nid->nodes[i], &reader,
+                            i + SET_SEED))
+        goto out;
+
+      if (i + 1 == nid->n_nodes)
+        NEXT_EXPECTING_FALSE (&reader);
+      else
+        NEXT_EXPECTING_TRUE (&reader);
+
+      ++i;
+    }
+
+  retval = TRUE;
+
+ out:
+  return retval;
+}
+
+static dbus_bool_t
+run_test_delete_values (NodeIterationData *nid)
+{
+  DBusTypeReader reader;
+  dbus_bool_t retval;
+  int t;
+
+  _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME);
+
+  retval = FALSE;
+
+  data_block_init_reader_writer (nid->block,
+                                 &reader, NULL);
+
+  while ((t = _dbus_type_reader_get_current_type (&reader)) != DBUS_TYPE_INVALID)
+    {
+      /* Right now, deleting only works on array elements.  We delete
+       * all array elements, and then verify that there aren't any
+       * left.
+       */
+      if (t == DBUS_TYPE_ARRAY)
+        {
+          DBusTypeReader array;
+          int n_elements;
+          int elem_type;
+
+          _dbus_type_reader_recurse (&reader, &array);
+          n_elements = 0;
+          while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID)
+            {
+              n_elements += 1;
+              _dbus_type_reader_next (&array);
+            }
+
+          /* reset to start of array */
+          _dbus_type_reader_recurse (&reader, &array);
+          _dbus_verbose ("recursing into deletion loop reader.value_pos = %d array.value_pos = %d array.u.start_pos = %d\n",
+                         reader.value_pos, array.value_pos, array.u.array.start_pos);
+          while ((elem_type = _dbus_type_reader_get_current_type (&array)) != DBUS_TYPE_INVALID)
+            {
+              /* We don't want to always delete from the same part of the array. */
+              static int cycle = 0;
+              int elem;
+
+              _dbus_assert (n_elements > 0);
+
+              elem = cycle;
+              if (elem == 3 || elem >= n_elements) /* end of array */
+                elem = n_elements - 1;
+
+              _dbus_verbose ("deleting array element %d of %d type %s cycle %d reader pos %d elem pos %d\n",
+                             elem, n_elements, _dbus_type_to_string (elem_type),
+                             cycle, reader.value_pos, array.value_pos);
+              while (elem > 0)
+                {
+                  if (!_dbus_type_reader_next (&array))
+                    _dbus_assert_not_reached ("should have had another element\n");
+                  --elem;
+                }
+
+              if (!_dbus_type_reader_delete (&array, &reader))
+                goto out;
+
+              n_elements -= 1;
+
+              /* reset */
+              _dbus_type_reader_recurse (&reader, &array);
+
+              if (cycle > 2)
+                cycle = 0;
+              else
+                cycle += 1;
+            }
+        }
+      _dbus_type_reader_next (&reader);
+    }
+
+  /* Check that there are no array elements left */
+  data_block_init_reader_writer (nid->block,
+                                 &reader, NULL);
+
+  while ((t = _dbus_type_reader_get_current_type (&reader)) != DBUS_TYPE_INVALID)
+    {
+      _dbus_type_reader_next (&reader);
+    }
+
+  retval = TRUE;
+
+ out:
+  return retval;
+}
+
+static dbus_bool_t
+run_test_nodes_iteration (void *data)
+{
+  NodeIterationData *nid = data;
+  DBusTypeReader reader;
+  DBusTypeWriter writer;
+  int i;
+  dbus_bool_t retval;
+
+  /* Stuff to do:
+   * 1. write the value
+   * 2. strcmp-compare with the signature we built
+   * 3. read the value
+   * 4. type-iterate the signature and the value and see if they are the same type-wise
+   */
+  retval = FALSE;
+
+  data_block_init_reader_writer (nid->block,
+                                 &reader, &writer);
+
+  /* DBusTypeWriter assumes it's writing into an existing signature,
+   * so doesn't add nul on its own. We have to do that.
+   */
+  if (!_dbus_string_insert_byte (&nid->block->signature,
+                                 nid->type_offset, '\0'))
+    goto out;
+
+  i = 0;
+  while (i < nid->n_nodes)
+    {
+      if (!node_write_value (nid->nodes[i], nid->block, &writer, i))
+        goto out;
+
+      ++i;
+    }
+
+  if (!_dbus_string_equal_substring (nid->signature, 0, _dbus_string_get_length (nid->signature),
+                                     &nid->block->signature, nid->type_offset))
+    {
+      _dbus_warn ("Expected signature '%s' and got '%s' with initial offset %d\n",
+                  _dbus_string_get_const_data (nid->signature),
+                  _dbus_string_get_const_data_len (&nid->block->signature, nid->type_offset, 0),
+                  nid->type_offset);
+      _dbus_assert_not_reached ("wrong signature");
+    }
+
+  i = 0;
+  while (i < nid->n_nodes)
+    {
+      if (!node_read_value (nid->nodes[i], &reader, i))
+        goto out;
+
+      if (i + 1 == nid->n_nodes)
+        NEXT_EXPECTING_FALSE (&reader);
+      else
+        NEXT_EXPECTING_TRUE (&reader);
+
+      ++i;
+    }
+
+  if (n_iterations_expected_this_test <= MAX_ITERATIONS_FOR_EXPENSIVE_TESTS)
+    {
+      /* this set values test uses code from copy and
+       * values_only_write so would ideally be last so you get a
+       * simpler test case for problems with copying or values_only
+       * writing; but it also needs an already-written DataBlock so it
+       * has to go first. Comment it out if it breaks, and see if the
+       * later tests also break - debug them first if so.
+       */
+      if (!run_test_set_values (nid))
+        goto out;
+
+      if (!run_test_delete_values (nid))
+        goto out;
+
+      if (!run_test_copy (nid))
+        goto out;
+
+      if (!run_test_values_only_write (nid))
+        goto out;
+    }
+
+  /* FIXME type-iterate both signature and value and compare the resulting
+   * tree to the node tree perhaps
+   */
+
+  retval = TRUE;
+
+ out:
+
+  data_block_reset (nid->block);
+
+  return retval;
+}
+
+static void
+run_test_nodes_in_one_configuration (TestTypeNode    **nodes,
+                                     int               n_nodes,
+                                     const DBusString *signature,
+                                     int               byte_order,
+                                     int               initial_offset)
+{
+  DataBlock block;
+  NodeIterationData nid;
+
+  if (!data_block_init (&block, byte_order, initial_offset))
+    _dbus_assert_not_reached ("no memory");
+
+  nid.signature = signature;
+  nid.block = &block;
+  nid.type_offset = initial_offset;
+  nid.nodes = nodes;
+  nid.n_nodes = n_nodes;
+
+  if (TEST_OOM_HANDLING &&
+      n_iterations_expected_this_test <= MAX_ITERATIONS_FOR_EXPENSIVE_TESTS)
+    {
+      _dbus_test_oom_handling ("running test node",
+                               run_test_nodes_iteration,
+                               &nid);
+    }
+  else
+    {
+      if (!run_test_nodes_iteration (&nid))
+        _dbus_assert_not_reached ("no memory");
+    }
+
+  data_block_free (&block);
+}
+
+static void
+run_test_nodes (TestTypeNode **nodes,
+                int            n_nodes)
+{
+  int i;
+  DBusString signature;
+
+  if (!_dbus_string_init (&signature))
+    _dbus_assert_not_reached ("no memory");
+
+  i = 0;
+  while (i < n_nodes)
+    {
+      if (! node_build_signature (nodes[i], &signature))
+        _dbus_assert_not_reached ("no memory");
+
+      ++i;
+    }
+
+  _dbus_verbose (">>> test nodes with signature '%s'\n",
+                 _dbus_string_get_const_data (&signature));
+
+  i = 0;
+  while (i <= MAX_INITIAL_OFFSET)
+    {
+      run_test_nodes_in_one_configuration (nodes, n_nodes, &signature,
+                                           DBUS_LITTLE_ENDIAN, i);
+      run_test_nodes_in_one_configuration (nodes, n_nodes, &signature,
+                                           DBUS_BIG_ENDIAN, i);
+
+      ++i;
+    }
+
+  n_iterations_completed_this_test += 1;
+  n_iterations_completed_total += 1;
+
+  if (n_iterations_completed_this_test == n_iterations_expected_this_test)
+    {
+      fprintf (stderr, " 100%% %d this test (%d cumulative)\n",
+               n_iterations_completed_this_test,
+               n_iterations_completed_total);
+    }
+  /* this happens to turn out well with mod == 1 */
+  else if ((n_iterations_completed_this_test %
+            (int)(n_iterations_expected_this_test / 10.0)) == 1)
+    {
+      fprintf (stderr, " %d%% ", (int) (n_iterations_completed_this_test / (double) n_iterations_expected_this_test * 100));
+    }
+
+  _dbus_string_free (&signature);
+}
+
+#define N_VALUES (N_BASICS * N_CONTAINERS + N_BASICS)
+
+static TestTypeNode*
+value_generator (int *ip)
+{
+  int i = *ip;
+  const TestTypeNodeClass *child_klass;
+  const TestTypeNodeClass *container_klass;
+  TestTypeNode *child;
+  TestTypeNode *node;
+
+  _dbus_assert (i <= N_VALUES);
+
+  if (i == N_VALUES)
+    {
+      return NULL;
+    }
+  else if (i < N_BASICS)
+    {
+      node = node_new (basic_nodes[i]);
+    }
+  else
+    {
+      /* imagine an array:
+       * container 0 of basic 0
+       * container 0 of basic 1
+       * container 0 of basic 2
+       * container 1 of basic 0
+       * container 1 of basic 1
+       * container 1 of basic 2
+       */
+      i -= N_BASICS;
+
+      container_klass = container_nodes[i / N_BASICS];
+      child_klass = basic_nodes[i % N_BASICS];
+
+      node = node_new (container_klass);
+      child = node_new (child_klass);
+
+      node_append_child (node, child);
+    }
+
+  *ip += 1; /* increment the generator */
+
+  return node;
+}
+
+static void
+build_body (TestTypeNode **nodes,
+            int            n_nodes,
+            int            byte_order,
+            DBusString    *signature,
+            DBusString    *body)
+{
+  int i;
+  DataBlock block;
+  DBusTypeReader reader;
+  DBusTypeWriter writer;
+
+  i = 0;
+  while (i < n_nodes)
+    {
+      if (! node_build_signature (nodes[i], signature))
+        _dbus_assert_not_reached ("no memory");
+      
+      ++i;
+    }
+
+  if (!data_block_init (&block, byte_order, 0))
+    _dbus_assert_not_reached ("no memory");
+  
+  data_block_init_reader_writer (&block,
+                                 &reader, &writer);
+  
+  /* DBusTypeWriter assumes it's writing into an existing signature,
+   * so doesn't add nul on its own. We have to do that.
+   */
+  if (!_dbus_string_insert_byte (&block.signature,
+                                 0, '\0'))
+    _dbus_assert_not_reached ("no memory");
+
+  i = 0;
+  while (i < n_nodes)
+    {
+      if (!node_write_value (nodes[i], &block, &writer, i))
+        _dbus_assert_not_reached ("no memory");
+
+      ++i;
+    }
+
+  if (!_dbus_string_copy_len (&block.body, 0,
+                              _dbus_string_get_length (&block.body) - N_FENCE_BYTES,
+                              body, 0))
+    _dbus_assert_not_reached ("oom");
+
+  data_block_free (&block);  
+}
+
+dbus_bool_t
+dbus_internal_do_not_use_generate_bodies (int           sequence,
+                                          int           byte_order,
+                                          DBusString   *signature,
+                                          DBusString   *body)
+{
+  TestTypeNode *nodes[1];
+  int i;
+  int n_nodes;
+
+  nodes[0] = value_generator (&sequence);
+
+  if (nodes[0] == NULL)
+    return FALSE;
+
+  n_nodes = 1;
+  
+  build_body (nodes, n_nodes, byte_order, signature, body);
+
+
+  i = 0;
+  while (i < n_nodes)
+    {
+      node_destroy (nodes[i]);
+      ++i;
+    }
+  
+  return TRUE;
+}
+
+static void
+make_and_run_values_inside_container (const TestTypeNodeClass *container_klass,
+                                      int                      n_nested)
+{
+  TestTypeNode *root;
+  TestTypeNode *container;
+  TestTypeNode *child;
+  int i;
+
+  root = node_new (container_klass);
+  container = root;
+  for (i = 1; i < n_nested; i++)
+    {
+      child = node_new (container_klass);
+      node_append_child (container, child);
+      container = child;
+    }
+
+  /* container should now be the most-nested container */
+
+  i = 0;
+  while ((child = value_generator (&i)))
+    {
+      node_append_child (container, child);
+
+      run_test_nodes (&root, 1);
+
+      _dbus_list_clear (&((TestTypeNodeContainer*)container)->children);
+      node_destroy (child);
+    }
+
+  node_destroy (root);
+}
+
+static void
+start_next_test (const char *format,
+                 int         expected)
+{
+  n_iterations_completed_this_test = 0;
+  n_iterations_expected_this_test = expected;
+
+  fprintf (stderr, ">>> >>> ");
+  fprintf (stderr, format,
+           n_iterations_expected_this_test);
+}
+
+static void
+make_and_run_test_nodes (void)
+{
+  int i, j, k, m;
+
+  /* We try to do this in order of "complicatedness" so that test
+   * failures tend to show up in the simplest test case that
+   * demonstrates the failure.  There are also some tests that run
+   * more than once for this reason, first while going through simple
+   * cases, second while going through a broader range of complex
+   * cases.
+   */
+  /* Each basic node. The basic nodes should include:
+   *
+   * - each fixed-size type (in such a way that it has different values each time,
+   *                         so we can tell if we mix two of them up)
+   * - strings of various lengths
+   * - object path
+   * - signature
+   */
+  /* Each container node. The container nodes should include:
+   *
+   *  struct with 1 and 2 copies of the contained item
+   *  array with 0, 1, 2 copies of the contained item
+   *  variant
+   */
+  /*  Let a "value" be a basic node, or a container containing a single basic node.
+   *  Let n_values be the number of such values i.e. (n_container * n_basic + n_basic)
+   *  When iterating through all values to make combinations, do the basic types
+   *  first and the containers second.
+   */
+  /* Each item is shown with its number of iterations to complete so
+   * we can keep a handle on this unit test
+   */
+
+  /* FIXME test just an empty body, no types at all */
+
+  start_next_test ("Each value by itself %d iterations\n", N_VALUES);
+  {
+    TestTypeNode *node;
+    i = 0;
+    while ((node = value_generator (&i)))
+      {
+        run_test_nodes (&node, 1);
+
+        node_destroy (node);
+      }
+  }
+
+  start_next_test ("Each value by itself with arrays as blocks %d iterations\n", N_VALUES);
+  arrays_write_fixed_in_blocks = TRUE;
+  {
+    TestTypeNode *node;
+    i = 0;
+    while ((node = value_generator (&i)))
+      {
+        run_test_nodes (&node, 1);
+
+        node_destroy (node);
+      }
+  }
+  arrays_write_fixed_in_blocks = FALSE;
+
+  start_next_test ("All values in one big toplevel %d iteration\n", 1);
+  {
+    TestTypeNode *nodes[N_VALUES];
+
+    i = 0;
+    while ((nodes[i] = value_generator (&i)))
+      ;
+
+    run_test_nodes (nodes, N_VALUES);
+
+    for (i = 0; i < N_VALUES; i++)
+      node_destroy (nodes[i]);
+  }
+
+  start_next_test ("Each value,value pair combination as toplevel, in both orders %d iterations\n",
+                   N_VALUES * N_VALUES);
+  {
+    TestTypeNode *nodes[2];
+
+    i = 0;
+    while ((nodes[0] = value_generator (&i)))
+      {
+        j = 0;
+        while ((nodes[1] = value_generator (&j)))
+          {
+            run_test_nodes (nodes, 2);
+
+            node_destroy (nodes[1]);
+          }
+
+        node_destroy (nodes[0]);
+      }
+  }
+
+  start_next_test ("Each container containing each value %d iterations\n",
+                   N_CONTAINERS * N_VALUES);
+  for (i = 0; i < N_CONTAINERS; i++)
+    {
+      const TestTypeNodeClass *container_klass = container_nodes[i];
+
+      make_and_run_values_inside_container (container_klass, 1);
+    }
+
+  start_next_test ("Each container containing each value with arrays as blocks %d iterations\n",
+                   N_CONTAINERS * N_VALUES);
+  arrays_write_fixed_in_blocks = TRUE;
+  for (i = 0; i < N_CONTAINERS; i++)
+    {
+      const TestTypeNodeClass *container_klass = container_nodes[i];
+
+      make_and_run_values_inside_container (container_klass, 1);
+    }
+  arrays_write_fixed_in_blocks = FALSE;
+
+  start_next_test ("Each container of same container of each value %d iterations\n",
+                   N_CONTAINERS * N_VALUES);
+  for (i = 0; i < N_CONTAINERS; i++)
+    {
+      const TestTypeNodeClass *container_klass = container_nodes[i];
+
+      make_and_run_values_inside_container (container_klass, 2);
+    }
+
+  start_next_test ("Each container of same container of same container of each value %d iterations\n",
+                   N_CONTAINERS * N_VALUES);
+  for (i = 0; i < N_CONTAINERS; i++)
+    {
+      const TestTypeNodeClass *container_klass = container_nodes[i];
+
+      make_and_run_values_inside_container (container_klass, 3);
+    }
+
+  start_next_test ("Each value,value pair inside a struct %d iterations\n",
+                   N_VALUES * N_VALUES);
+  {
+    TestTypeNode *val1, *val2;
+    TestTypeNode *node;
+
+    node = node_new (&struct_1_class);
+
+    i = 0;
+    while ((val1 = value_generator (&i)))
+      {
+        j = 0;
+        while ((val2 = value_generator (&j)))
+          {
+            TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+
+            node_append_child (node, val1);
+            node_append_child (node, val2);
+
+            run_test_nodes (&node, 1);
+
+            _dbus_list_clear (&container->children);
+            node_destroy (val2);
+          }
+        node_destroy (val1);
+      }
+    node_destroy (node);
+  }
+
+  start_next_test ("All values in one big struct %d iteration\n",
+                   1);
+  {
+    TestTypeNode *node;
+    TestTypeNode *child;
+
+    node = node_new (&struct_1_class);
+
+    i = 0;
+    while ((child = value_generator (&i)))
+      node_append_child (node, child);
+
+    run_test_nodes (&node, 1);
+
+    node_destroy (node);
+  }
+
+  start_next_test ("Each value in a large array %d iterations\n",
+                   N_VALUES);
+  {
+    TestTypeNode *val;
+    TestTypeNode *node;
+
+    node = node_new (&array_9_class);
+
+    i = 0;
+    while ((val = value_generator (&i)))
+      {
+        TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+
+        node_append_child (node, val);
+
+        run_test_nodes (&node, 1);
+
+        _dbus_list_clear (&container->children);
+        node_destroy (val);
+      }
+
+    node_destroy (node);
+  }
+
+  start_next_test ("Each container of each container of each value %d iterations\n",
+                   N_CONTAINERS * N_CONTAINERS * N_VALUES);
+  for (i = 0; i < N_CONTAINERS; i++)
+    {
+      const TestTypeNodeClass *outer_container_klass = container_nodes[i];
+      TestTypeNode *outer_container = node_new (outer_container_klass);
+
+      for (j = 0; j < N_CONTAINERS; j++)
+        {
+          TestTypeNode *child;
+          const TestTypeNodeClass *inner_container_klass = container_nodes[j];
+          TestTypeNode *inner_container = node_new (inner_container_klass);
+
+          node_append_child (outer_container, inner_container);
+
+          m = 0;
+          while ((child = value_generator (&m)))
+            {
+              node_append_child (inner_container, child);
+
+              run_test_nodes (&outer_container, 1);
+
+              _dbus_list_clear (&((TestTypeNodeContainer*)inner_container)->children);
+              node_destroy (child);
+            }
+          _dbus_list_clear (&((TestTypeNodeContainer*)outer_container)->children);
+          node_destroy (inner_container);
+        }
+      node_destroy (outer_container);
+    }
+
+  start_next_test ("Each container of each container of each container of each value %d iterations\n",
+                   N_CONTAINERS * N_CONTAINERS * N_CONTAINERS * N_VALUES);
+  for (i = 0; i < N_CONTAINERS; i++)
+    {
+      const TestTypeNodeClass *outer_container_klass = container_nodes[i];
+      TestTypeNode *outer_container = node_new (outer_container_klass);
+
+      for (j = 0; j < N_CONTAINERS; j++)
+        {
+          const TestTypeNodeClass *inner_container_klass = container_nodes[j];
+          TestTypeNode *inner_container = node_new (inner_container_klass);
+
+          node_append_child (outer_container, inner_container);
+
+          for (k = 0; k < N_CONTAINERS; k++)
+            {
+              TestTypeNode *child;
+              const TestTypeNodeClass *center_container_klass = container_nodes[k];
+              TestTypeNode *center_container = node_new (center_container_klass);
+
+              node_append_child (inner_container, center_container);
+
+              m = 0;
+              while ((child = value_generator (&m)))
+                {
+                  node_append_child (center_container, child);
+
+                  run_test_nodes (&outer_container, 1);
+
+                  _dbus_list_clear (&((TestTypeNodeContainer*)center_container)->children);
+                  node_destroy (child);
+                }
+              _dbus_list_clear (&((TestTypeNodeContainer*)inner_container)->children);
+              node_destroy (center_container);
+            }
+          _dbus_list_clear (&((TestTypeNodeContainer*)outer_container)->children);
+          node_destroy (inner_container);
+        }
+      node_destroy (outer_container);
+    }
+
+#if 0
+  /* This one takes a really long time, so comment it out for now */
+  start_next_test ("Each value,value,value triplet combination as toplevel, in all orders %d iterations\n",
+                   N_VALUES * N_VALUES * N_VALUES);
+  {
+    TestTypeNode *nodes[3];
+
+    i = 0;
+    while ((nodes[0] = value_generator (&i)))
+      {
+        j = 0;
+        while ((nodes[1] = value_generator (&j)))
+          {
+            k = 0;
+            while ((nodes[2] = value_generator (&k)))
+              {
+                run_test_nodes (nodes, 3);
+
+                node_destroy (nodes[2]);
+              }
+            node_destroy (nodes[1]);
+          }
+        node_destroy (nodes[0]);
+      }
+  }
+#endif /* #if 0 expensive test */
+
+  fprintf (stderr, "%d total iterations of recursive marshaling tests\n",
+           n_iterations_completed_total);
+  fprintf (stderr, "each iteration ran at initial offsets 0 through %d in both big and little endian\n",
+           MAX_INITIAL_OFFSET);
+  fprintf (stderr, "out of memory handling %s tested\n",
+           TEST_OOM_HANDLING ? "was" : "was not");
+}
+
+dbus_bool_t
+_dbus_marshal_recursive_test (void)
+{
+  make_and_run_test_nodes ();
+
+  return TRUE;
+}
+
+/*
+ *
+ *
+ *         Implementations of each type node class
+ *
+ *
+ *
+ */
+#define MAX_MULTI_COUNT 5
+
+#define SAMPLE_INT16           1234
+#define SAMPLE_INT16_ALTERNATE 6785
+static dbus_int16_t
+int16_from_seed (int seed)
+{
+  /* Generate an integer value that's predictable from seed.  We could
+   * just use seed itself, but that would only ever touch one byte of
+   * the int so would miss some kinds of bug.
+   */
+  dbus_int16_t v;
+
+  v = 42; /* just to quiet compiler afaik */
+  switch (seed % 5)
+    {
+    case 0:
+      v = SAMPLE_INT16;
+      break;
+    case 1:
+      v = SAMPLE_INT16_ALTERNATE;
+      break;
+    case 2:
+      v = -1;
+      break;
+    case 3:
+      v = _DBUS_INT16_MAX;
+      break;
+    case 4:
+      v = 1;
+      break;
+    }
+
+  if (seed > 1)
+    v *= seed; /* wraps around eventually, which is fine */
+
+  return v;
+}
+
+static dbus_bool_t
+int16_write_value (TestTypeNode   *node,
+                   DataBlock      *block,
+                   DBusTypeWriter *writer,
+                   int             seed)
+{
+  /* also used for uint16 */
+  dbus_int16_t v;
+
+  v = int16_from_seed (seed);
+
+  return _dbus_type_writer_write_basic (writer,
+                                        node->klass->typecode,
+                                        &v);
+}
+
+static dbus_bool_t
+int16_read_value (TestTypeNode   *node,
+                  DBusTypeReader *reader,
+                  int             seed)
+{
+  /* also used for uint16 */
+  dbus_int16_t v;
+
+  check_expected_type (reader, node->klass->typecode);
+
+  _dbus_type_reader_read_basic (reader,
+                                (dbus_int16_t*) &v);
+
+  _dbus_assert (v == int16_from_seed (seed));
+
+  return TRUE;
+}
+
+static dbus_bool_t
+int16_set_value (TestTypeNode   *node,
+                 DBusTypeReader *reader,
+                 DBusTypeReader *realign_root,
+                 int             seed)
+{
+  /* also used for uint16 */
+  dbus_int16_t v;
+
+  v = int16_from_seed (seed);
+
+  return _dbus_type_reader_set_basic (reader,
+                                      &v,
+                                      realign_root);
+}
+
+static dbus_bool_t
+int16_write_multi (TestTypeNode   *node,
+                   DataBlock      *block,
+                   DBusTypeWriter *writer,
+                   int             seed,
+                   int             count)
+{
+  /* also used for uint16 */
+  dbus_int16_t values[MAX_MULTI_COUNT];
+  dbus_int16_t *v_ARRAY_INT16 = values;
+  int i;
+
+  for (i = 0; i < count; ++i)
+    values[i] = int16_from_seed (seed + i);
+
+  return _dbus_type_writer_write_fixed_multi (writer,
+                                              node->klass->typecode,
+                                              &v_ARRAY_INT16, count);
+}
+
+static dbus_bool_t
+int16_read_multi (TestTypeNode   *node,
+                  DBusTypeReader *reader,
+                  int             seed,
+                  int             count)
+{
+  /* also used for uint16 */
+  dbus_int16_t *values;
+  int n_elements;
+  int i;
+
+  check_expected_type (reader, node->klass->typecode);
+
+  _dbus_type_reader_read_fixed_multi (reader,
+                                      &values,
+                                      &n_elements);
+
+  if (n_elements != count)
+    _dbus_warn ("got %d elements expected %d\n", n_elements, count);
+  _dbus_assert (n_elements == count);
+
+  for (i = 0; i < count; i++)
+    _dbus_assert (((dbus_int16_t)_dbus_unpack_uint16 (reader->byte_order,
+                                                      (const unsigned char*)values + (i * 2))) ==
+                  int16_from_seed (seed + i));
+
+  return TRUE;
+}
+
+
+#define SAMPLE_INT32           12345678
+#define SAMPLE_INT32_ALTERNATE 53781429
+static dbus_int32_t
+int32_from_seed (int seed)
+{
+  /* Generate an integer value that's predictable from seed.  We could
+   * just use seed itself, but that would only ever touch one byte of
+   * the int so would miss some kinds of bug.
+   */
+  dbus_int32_t v;
+
+  v = 42; /* just to quiet compiler afaik */
+  switch (seed % 5)
+    {
+    case 0:
+      v = SAMPLE_INT32;
+      break;
+    case 1:
+      v = SAMPLE_INT32_ALTERNATE;
+      break;
+    case 2:
+      v = -1;
+      break;
+    case 3:
+      v = _DBUS_INT_MAX;
+      break;
+    case 4:
+      v = 1;
+      break;
+    }
+
+  if (seed > 1)
+    v *= seed; /* wraps around eventually, which is fine */
+
+  return v;
+}
+
+static dbus_bool_t
+int32_write_value (TestTypeNode   *node,
+                   DataBlock      *block,
+                   DBusTypeWriter *writer,
+                   int             seed)
+{
+  /* also used for uint32 */
+  dbus_int32_t v;
+
+  v = int32_from_seed (seed);
+
+  return _dbus_type_writer_write_basic (writer,
+                                        node->klass->typecode,
+                                        &v);
+}
+
+static dbus_bool_t
+int32_read_value (TestTypeNode   *node,
+                  DBusTypeReader *reader,
+                  int             seed)
+{
+  /* also used for uint32 */
+  dbus_int32_t v;
+
+  check_expected_type (reader, node->klass->typecode);
+
+  _dbus_type_reader_read_basic (reader,
+                                (dbus_int32_t*) &v);
+
+  _dbus_assert (v == int32_from_seed (seed));
+
+  return TRUE;
+}
+
+static dbus_bool_t
+int32_set_value (TestTypeNode   *node,
+                 DBusTypeReader *reader,
+                 DBusTypeReader *realign_root,
+                 int             seed)
+{
+  /* also used for uint32 */
+  dbus_int32_t v;
+
+  v = int32_from_seed (seed);
+
+  return _dbus_type_reader_set_basic (reader,
+                                      &v,
+                                      realign_root);
+}
+
+static dbus_bool_t
+int32_write_multi (TestTypeNode   *node,
+                   DataBlock      *block,
+                   DBusTypeWriter *writer,
+                   int             seed,
+                   int             count)
+{
+  /* also used for uint32 */
+  dbus_int32_t values[MAX_MULTI_COUNT];
+  dbus_int32_t *v_ARRAY_INT32 = values;
+  int i;
+
+  for (i = 0; i < count; ++i)
+    values[i] = int32_from_seed (seed + i);
+
+  return _dbus_type_writer_write_fixed_multi (writer,
+                                              node->klass->typecode,
+                                              &v_ARRAY_INT32, count);
+}
+
+static dbus_bool_t
+int32_read_multi (TestTypeNode   *node,
+                  DBusTypeReader *reader,
+                  int             seed,
+                  int             count)
+{
+  /* also used for uint32 */
+  dbus_int32_t *values;
+  int n_elements;
+  int i;
+
+  check_expected_type (reader, node->klass->typecode);
+
+  _dbus_type_reader_read_fixed_multi (reader,
+                                      &values,
+                                      &n_elements);
+
+  if (n_elements != count)
+    _dbus_warn ("got %d elements expected %d\n", n_elements, count);
+  _dbus_assert (n_elements == count);
+
+  for (i = 0; i < count; i++)
+    _dbus_assert (((int)_dbus_unpack_uint32 (reader->byte_order,
+                                             (const unsigned char*)values + (i * 4))) ==
+                  int32_from_seed (seed + i));
+
+  return TRUE;
+}
+
+#ifdef DBUS_HAVE_INT64
+static dbus_int64_t
+int64_from_seed (int seed)
+{
+  dbus_int32_t v32;
+  dbus_int64_t v;
+
+  v32 = int32_from_seed (seed);
+
+  v = - (dbus_int32_t) ~ v32;
+  v |= (((dbus_int64_t)v32) << 32);
+
+  return v;
+}
+#endif
+
+static dbus_bool_t
+int64_write_value (TestTypeNode   *node,
+                   DataBlock      *block,
+                   DBusTypeWriter *writer,
+                   int             seed)
+{
+#ifdef DBUS_HAVE_INT64
+  /* also used for uint64 */
+  dbus_int64_t v;
+
+  v = int64_from_seed (seed);
+
+  return _dbus_type_writer_write_basic (writer,
+                                        node->klass->typecode,
+                                        &v);
+#else
+  return TRUE;
+#endif
+}
+
+static dbus_bool_t
+int64_read_value (TestTypeNode   *node,
+                  DBusTypeReader *reader,
+                  int             seed)
+{
+#ifdef DBUS_HAVE_INT64
+  /* also used for uint64 */
+  dbus_int64_t v;
+
+  check_expected_type (reader, node->klass->typecode);
+
+  _dbus_type_reader_read_basic (reader,
+                                (dbus_int64_t*) &v);
+
+  _dbus_assert (v == int64_from_seed (seed));
+
+  return TRUE;
+#else
+  return TRUE;
+#endif
+}
+
+static dbus_bool_t
+int64_set_value (TestTypeNode   *node,
+                 DBusTypeReader *reader,
+                 DBusTypeReader *realign_root,
+                 int             seed)
+{
+#ifdef DBUS_HAVE_INT64
+  /* also used for uint64 */
+  dbus_int64_t v;
+
+  v = int64_from_seed (seed);
+
+  return _dbus_type_reader_set_basic (reader,
+                                      &v,
+                                      realign_root);
+#else
+  return TRUE;
+#endif
+}
+
+#define MAX_SAMPLE_STRING_LEN 10
+static void
+string_from_seed (char *buf,
+                  int   len,
+                  int   seed)
+{
+  int i;
+  unsigned char v;
+
+  _dbus_assert (len < MAX_SAMPLE_STRING_LEN);
+
+  /* vary the length slightly, though we also have multiple string
+   * value types for this, varying it here tests the set_value code
+   */
+  switch (seed % 3)
+    {
+    case 1:
+      len += 2;
+      break;
+    case 2:
+      len -= 2;
+      break;
+    }
+  if (len < 0)
+    len = 0;
+
+  v = (unsigned char) ('A' + seed);
+
+  i = 0;
+  while (i < len)
+    {
+      if (v < 'A' || v > 'z')
+        v = 'A';
+
+      buf[i] = v;
+
+      v += 1;
+      ++i;
+    }
+
+  buf[i] = '\0';
+}
+
+static dbus_bool_t
+string_write_value (TestTypeNode   *node,
+                    DataBlock      *block,
+                    DBusTypeWriter *writer,
+                    int             seed)
+{
+  char buf[MAX_SAMPLE_STRING_LEN + 1]="";
+  const char *v_string = buf;
+
+
+  string_from_seed (buf, node->klass->subclass_detail,
+                    seed);
+
+  return _dbus_type_writer_write_basic (writer,
+                                        node->klass->typecode,
+                                        &v_string);
+}
+
+static dbus_bool_t
+string_read_value (TestTypeNode   *node,
+                   DBusTypeReader *reader,
+                   int             seed)
+{
+  const char *v;
+  char buf[MAX_SAMPLE_STRING_LEN + 1];
+  v = buf;
+
+  check_expected_type (reader, node->klass->typecode);
+
+  _dbus_type_reader_read_basic (reader,
+                                (const char **) &v);
+
+  string_from_seed (buf, node->klass->subclass_detail,
+                    seed);
+
+  if (strcmp (buf, v) != 0)
+    {
+      _dbus_warn ("read string '%s' expected '%s'\n",
+                  v, buf);
+      _dbus_assert_not_reached ("test failed");
+    }
+
+  return TRUE;
+}
+
+static dbus_bool_t
+string_set_value (TestTypeNode   *node,
+                  DBusTypeReader *reader,
+                  DBusTypeReader *realign_root,
+                  int             seed)
+{
+  char buf[MAX_SAMPLE_STRING_LEN + 1];
+  const char *v_string = buf;
+
+  string_from_seed (buf, node->klass->subclass_detail,
+                    seed);
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ {
+   const char *old;
+   _dbus_type_reader_read_basic (reader, &old);
+   _dbus_verbose ("SETTING new string '%s' len %d in place of '%s' len %d\n",
+                  v_string, strlen (v_string), old, strlen (old));
+ }
+#endif
+
+  return _dbus_type_reader_set_basic (reader,
+                                      &v_string,
+                                      realign_root);
+}
+
+#define BOOL_FROM_SEED(seed) ((dbus_bool_t)((seed) % 2))
+
+static dbus_bool_t
+bool_write_value (TestTypeNode   *node,
+                  DataBlock      *block,
+                  DBusTypeWriter *writer,
+                  int             seed)
+{
+  dbus_bool_t v;
+
+  v = BOOL_FROM_SEED (seed);
+
+  return _dbus_type_writer_write_basic (writer,
+                                        node->klass->typecode,
+                                        &v);
+}
+
+static dbus_bool_t
+bool_read_value (TestTypeNode   *node,
+                 DBusTypeReader *reader,
+                 int             seed)
+{
+  dbus_bool_t v;
+
+  check_expected_type (reader, node->klass->typecode);
+
+  _dbus_type_reader_read_basic (reader,
+                                (unsigned char*) &v);
+
+  _dbus_assert (v == BOOL_FROM_SEED (seed));
+
+  return TRUE;
+}
+
+static dbus_bool_t
+bool_set_value (TestTypeNode   *node,
+                DBusTypeReader *reader,
+                DBusTypeReader *realign_root,
+                int             seed)
+{
+  dbus_bool_t v;
+
+  v = BOOL_FROM_SEED (seed);
+
+  return _dbus_type_reader_set_basic (reader,
+                                      &v,
+                                      realign_root);
+}
+
+#define BYTE_FROM_SEED(seed) ((unsigned char) int32_from_seed (seed))
+
+static dbus_bool_t
+byte_write_value (TestTypeNode   *node,
+                  DataBlock      *block,
+                  DBusTypeWriter *writer,
+                  int             seed)
+{
+  unsigned char v;
+
+  v = BYTE_FROM_SEED (seed);
+
+  return _dbus_type_writer_write_basic (writer,
+                                        node->klass->typecode,
+                                        &v);
+}
+
+static dbus_bool_t
+byte_read_value (TestTypeNode   *node,
+                 DBusTypeReader *reader,
+                 int             seed)
+{
+  unsigned char v;
+
+  check_expected_type (reader, node->klass->typecode);
+
+  _dbus_type_reader_read_basic (reader,
+                                (unsigned char*) &v);
+
+  _dbus_assert (v == BYTE_FROM_SEED (seed));
+
+  return TRUE;
+}
+
+
+static dbus_bool_t
+byte_set_value (TestTypeNode   *node,
+                DBusTypeReader *reader,
+                DBusTypeReader *realign_root,
+                int             seed)
+{
+  unsigned char v;
+
+  v = BYTE_FROM_SEED (seed);
+
+  return _dbus_type_reader_set_basic (reader,
+                                      &v,
+                                      realign_root);
+}
+
+static double
+double_from_seed (int seed)
+{
+  return SAMPLE_INT32 * (double) seed + 0.3;
+}
+
+static dbus_bool_t
+double_write_value (TestTypeNode   *node,
+                    DataBlock      *block,
+                    DBusTypeWriter *writer,
+                    int             seed)
+{
+  double v;
+
+  v = double_from_seed (seed);
+
+  return _dbus_type_writer_write_basic (writer,
+                                        node->klass->typecode,
+                                        &v);
+}
+
+static dbus_bool_t
+double_read_value (TestTypeNode   *node,
+                   DBusTypeReader *reader,
+                   int             seed)
+{
+  double v;
+  double expected;
+
+  check_expected_type (reader, node->klass->typecode);
+
+  _dbus_type_reader_read_basic (reader,
+                                (double*) &v);
+
+  expected = double_from_seed (seed);
+
+  if (!_DBUS_DOUBLES_BITWISE_EQUAL (v, expected))
+    {
+#ifdef DBUS_HAVE_INT64
+      _dbus_warn ("Expected double %g got %g\n bits = 0x%llx vs.\n bits = 0x%llx)\n",
+                  expected, v,
+                  *(dbus_uint64_t*)(char*)&expected,
+                  *(dbus_uint64_t*)(char*)&v);
+#endif
+      _dbus_assert_not_reached ("test failed");
+    }
+
+  return TRUE;
+}
+
+static dbus_bool_t
+double_set_value (TestTypeNode   *node,
+                DBusTypeReader *reader,
+                DBusTypeReader *realign_root,
+                int             seed)
+{
+  double v;
+
+  v = double_from_seed (seed);
+
+  return _dbus_type_reader_set_basic (reader,
+                                      &v,
+                                      realign_root);
+}
+
+#define MAX_SAMPLE_OBJECT_PATH_LEN 10
+static void
+object_path_from_seed (char *buf,
+                       int   seed)
+{
+  int i;
+  unsigned char v;
+  int len;
+
+  len = seed % 9;
+  _dbus_assert (len < MAX_SAMPLE_OBJECT_PATH_LEN);
+
+  v = (unsigned char) ('A' + seed);
+
+  if (len < 2)
+    {
+      buf[0] = '/';
+      i = 1;
+    }
+  else
+    {
+      i = 0;
+      while (i + 1 < len)
+        {
+          if (v < 'A' || v > 'z')
+            v = 'A';
+
+          buf[i] = '/';
+          ++i;
+          buf[i] = v;
+          ++i;
+          
+          v += 1;
+        }
+    }
+
+  buf[i] = '\0';
+}
+
+static dbus_bool_t
+object_path_write_value (TestTypeNode   *node,
+                         DataBlock      *block,
+                         DBusTypeWriter *writer,
+                         int             seed)
+{
+  char buf[MAX_SAMPLE_OBJECT_PATH_LEN + 1];
+  const char *v_string = buf;
+
+  object_path_from_seed (buf, seed);
+
+  return _dbus_type_writer_write_basic (writer,
+                                        node->klass->typecode,
+                                        &v_string);
+}
+
+static dbus_bool_t
+object_path_read_value (TestTypeNode   *node,
+                        DBusTypeReader *reader,
+                        int             seed)
+{
+  const char *v;
+  char buf[MAX_SAMPLE_OBJECT_PATH_LEN + 1];
+
+  check_expected_type (reader, node->klass->typecode);
+
+  _dbus_type_reader_read_basic (reader,
+                                (const char **) &v);
+
+  object_path_from_seed (buf, seed);
+
+  if (strcmp (buf, v) != 0)
+    {
+      _dbus_warn ("read object path '%s' expected '%s'\n",
+                  v, buf);
+      _dbus_assert_not_reached ("test failed");
+    }
+
+  return TRUE;
+}
+
+static dbus_bool_t
+object_path_set_value (TestTypeNode   *node,
+                       DBusTypeReader *reader,
+                       DBusTypeReader *realign_root,
+                       int             seed)
+{
+  char buf[MAX_SAMPLE_OBJECT_PATH_LEN + 1];
+  const char *v_string = buf;
+
+  object_path_from_seed (buf, seed);
+
+  return _dbus_type_reader_set_basic (reader,
+                                      &v_string,
+                                      realign_root);
+}
+
+#define MAX_SAMPLE_SIGNATURE_LEN 10
+static void
+signature_from_seed (char *buf,
+                     int   seed)
+{
+  /* try to avoid ascending, descending, or alternating length to help find bugs */
+  const char *sample_signatures[] = {
+    "asax"
+    "",
+    "asau(xxxx)",
+    "x",
+    "ai",
+    "a(ii)"
+  };
+
+  strcpy (buf, sample_signatures[seed % _DBUS_N_ELEMENTS(sample_signatures)]);
+}
+
+static dbus_bool_t
+signature_write_value (TestTypeNode   *node,
+                       DataBlock      *block,
+                       DBusTypeWriter *writer,
+                       int             seed)
+{
+  char buf[MAX_SAMPLE_SIGNATURE_LEN + 1];
+  const char *v_string = buf;
+
+  signature_from_seed (buf, seed);
+
+  return _dbus_type_writer_write_basic (writer,
+                                        node->klass->typecode,
+                                        &v_string);
+}
+
+static dbus_bool_t
+signature_read_value (TestTypeNode   *node,
+                      DBusTypeReader *reader,
+                      int             seed)
+{
+  const char *v;
+  char buf[MAX_SAMPLE_SIGNATURE_LEN + 1];
+
+  check_expected_type (reader, node->klass->typecode);
+
+  _dbus_type_reader_read_basic (reader,
+                                (const char **) &v);
+
+  signature_from_seed (buf, seed);
+
+  if (strcmp (buf, v) != 0)
+    {
+      _dbus_warn ("read signature value '%s' expected '%s'\n",
+                  v, buf);
+      _dbus_assert_not_reached ("test failed");
+    }
+
+  return TRUE;
+}
+
+
+static dbus_bool_t
+signature_set_value (TestTypeNode   *node,
+                     DBusTypeReader *reader,
+                     DBusTypeReader *realign_root,
+                     int             seed)
+{
+  char buf[MAX_SAMPLE_SIGNATURE_LEN + 1];
+  const char *v_string = buf;
+
+  signature_from_seed (buf, seed);
+
+  return _dbus_type_reader_set_basic (reader,
+                                      &v_string,
+                                      realign_root);
+}
+
+static dbus_bool_t
+struct_write_value (TestTypeNode   *node,
+                    DataBlock      *block,
+                    DBusTypeWriter *writer,
+                    int             seed)
+{
+  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+  DataBlockState saved;
+  DBusTypeWriter sub;
+  int i;
+  int n_copies;
+
+  n_copies = node->klass->subclass_detail;
+
+  _dbus_assert (container->children != NULL);
+
+  data_block_save (block, &saved);
+
+  if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_STRUCT,
+                                  NULL, 0,
+                                  &sub))
+    return FALSE;
+
+  i = 0;
+  while (i < n_copies)
+    {
+      DBusList *link;
+
+      link = _dbus_list_get_first_link (&container->children);
+      while (link != NULL)
+        {
+          TestTypeNode *child = link->data;
+          DBusList *next = _dbus_list_get_next_link (&container->children, link);
+
+          if (!node_write_value (child, block, &sub, seed + i))
+            {
+              data_block_restore (block, &saved);
+              return FALSE;
+            }
+
+          link = next;
+        }
+
+      ++i;
+    }
+
+  if (!_dbus_type_writer_unrecurse (writer, &sub))
+    {
+      data_block_restore (block, &saved);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static dbus_bool_t
+struct_read_or_set_value (TestTypeNode   *node,
+                          DBusTypeReader *reader,
+                          DBusTypeReader *realign_root,
+                          int             seed)
+{
+  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+  DBusTypeReader sub;
+  int i;
+  int n_copies;
+
+  n_copies = node->klass->subclass_detail;
+
+  check_expected_type (reader, DBUS_TYPE_STRUCT);
+
+  _dbus_type_reader_recurse (reader, &sub);
+
+  i = 0;
+  while (i < n_copies)
+    {
+      DBusList *link;
+
+      link = _dbus_list_get_first_link (&container->children);
+      while (link != NULL)
+        {
+          TestTypeNode *child = link->data;
+          DBusList *next = _dbus_list_get_next_link (&container->children, link);
+
+          if (realign_root == NULL)
+            {
+              if (!node_read_value (child, &sub, seed + i))
+                return FALSE;
+            }
+          else
+            {
+              if (!node_set_value (child, &sub, realign_root, seed + i))
+                return FALSE;
+            }
+
+          if (i == (n_copies - 1) && next == NULL)
+            NEXT_EXPECTING_FALSE (&sub);
+          else
+            NEXT_EXPECTING_TRUE (&sub);
+
+          link = next;
+        }
+
+      ++i;
+    }
+
+  return TRUE;
+}
+
+static dbus_bool_t
+struct_read_value (TestTypeNode   *node,
+                   DBusTypeReader *reader,
+                   int             seed)
+{
+  return struct_read_or_set_value (node, reader, NULL, seed);
+}
+
+static dbus_bool_t
+struct_set_value (TestTypeNode   *node,
+                  DBusTypeReader *reader,
+                  DBusTypeReader *realign_root,
+                  int             seed)
+{
+  return struct_read_or_set_value (node, reader, realign_root, seed);
+}
+
+static dbus_bool_t
+struct_build_signature (TestTypeNode   *node,
+                        DBusString     *str)
+{
+  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+  int i;
+  int orig_len;
+  int n_copies;
+
+  n_copies = node->klass->subclass_detail;
+
+  orig_len = _dbus_string_get_length (str);
+
+  if (!_dbus_string_append_byte (str, DBUS_STRUCT_BEGIN_CHAR))
+    goto oom;
+
+  i = 0;
+  while (i < n_copies)
+    {
+      DBusList *link;
+
+      link = _dbus_list_get_first_link (&container->children);
+      while (link != NULL)
+        {
+          TestTypeNode *child = link->data;
+          DBusList *next = _dbus_list_get_next_link (&container->children, link);
+
+          if (!node_build_signature (child, str))
+            goto oom;
+
+          link = next;
+        }
+
+      ++i;
+    }
+
+  if (!_dbus_string_append_byte (str, DBUS_STRUCT_END_CHAR))
+    goto oom;
+
+  return TRUE;
+
+ oom:
+  _dbus_string_set_length (str, orig_len);
+  return FALSE;
+}
+
+static dbus_bool_t
+array_write_value (TestTypeNode   *node,
+                   DataBlock      *block,
+                   DBusTypeWriter *writer,
+                   int             seed)
+{
+  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+  DataBlockState saved;
+  DBusTypeWriter sub;
+  DBusString element_signature;
+  int i;
+  int n_copies;
+  int element_type;
+  TestTypeNode *child;
+
+  n_copies = node->klass->subclass_detail;
+
+  _dbus_assert (container->children != NULL);
+
+  data_block_save (block, &saved);
+
+  if (!_dbus_string_init (&element_signature))
+    return FALSE;
+
+  child = _dbus_list_get_first (&container->children);
+
+  if (!node_build_signature (child,
+                             &element_signature))
+    goto oom;
+
+  element_type = _dbus_first_type_in_signature (&element_signature, 0);
+
+  if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_ARRAY,
+                                  &element_signature, 0,
+                                  &sub))
+    goto oom;
+
+  if (arrays_write_fixed_in_blocks &&
+      dbus_type_is_fixed (element_type) &&
+      child->klass->write_multi)
+    {
+      if (!node_write_multi (child, block, &sub, seed, n_copies))
+        goto oom;
+    }
+  else
+    {
+      i = 0;
+      while (i < n_copies)
+        {
+          DBusList *link;
+
+          link = _dbus_list_get_first_link (&container->children);
+          while (link != NULL)
+            {
+              TestTypeNode *child = link->data;
+              DBusList *next = _dbus_list_get_next_link (&container->children, link);
+
+              if (!node_write_value (child, block, &sub, seed + i))
+                goto oom;
+
+              link = next;
+            }
+
+          ++i;
+        }
+    }
+
+  if (!_dbus_type_writer_unrecurse (writer, &sub))
+    goto oom;
+
+  _dbus_string_free (&element_signature);
+  return TRUE;
+
+ oom:
+  data_block_restore (block, &saved);
+  _dbus_string_free (&element_signature);
+  return FALSE;
+}
+
+static dbus_bool_t
+array_read_or_set_value (TestTypeNode   *node,
+                         DBusTypeReader *reader,
+                         DBusTypeReader *realign_root,
+                         int             seed)
+{
+  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+  DBusTypeReader sub;
+  int i;
+  int n_copies;
+  TestTypeNode *child;
+
+  n_copies = node->klass->subclass_detail;
+
+  check_expected_type (reader, DBUS_TYPE_ARRAY);
+
+  child = _dbus_list_get_first (&container->children);
+
+  if (n_copies > 0)
+    {
+      _dbus_type_reader_recurse (reader, &sub);
+
+      if (realign_root == NULL && arrays_write_fixed_in_blocks &&
+          dbus_type_is_fixed (_dbus_type_reader_get_element_type (reader)) &&
+          child->klass->read_multi)
+        {
+          if (!node_read_multi (child, &sub, seed, n_copies))
+            return FALSE;
+        }
+      else
+        {
+          i = 0;
+          while (i < n_copies)
+            {
+              DBusList *link;
+
+              link = _dbus_list_get_first_link (&container->children);
+              while (link != NULL)
+                {
+                  TestTypeNode *child = link->data;
+                  DBusList *next = _dbus_list_get_next_link (&container->children, link);
+
+                  _dbus_assert (child->klass->typecode ==
+                                _dbus_type_reader_get_element_type (reader));
+
+                  if (realign_root == NULL)
+                    {
+                      if (!node_read_value (child, &sub, seed + i))
+                        return FALSE;
+                    }
+                  else
+                    {
+                      if (!node_set_value (child, &sub, realign_root, seed + i))
+                        return FALSE;
+                    }
+
+                  if (i == (n_copies - 1) && next == NULL)
+                    NEXT_EXPECTING_FALSE (&sub);
+                  else
+                    NEXT_EXPECTING_TRUE (&sub);
+
+                  link = next;
+                }
+
+              ++i;
+            }
+        }
+    }
+
+  return TRUE;
+}
+
+static dbus_bool_t
+array_read_value (TestTypeNode   *node,
+                  DBusTypeReader *reader,
+                  int             seed)
+{
+  return array_read_or_set_value (node, reader, NULL, seed);
+}
+
+static dbus_bool_t
+array_set_value (TestTypeNode   *node,
+                 DBusTypeReader *reader,
+                 DBusTypeReader *realign_root,
+                 int             seed)
+{
+  return array_read_or_set_value (node, reader, realign_root, seed);
+}
+
+static dbus_bool_t
+array_build_signature (TestTypeNode   *node,
+                       DBusString     *str)
+{
+  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+  int orig_len;
+
+  orig_len = _dbus_string_get_length (str);
+
+  if (!_dbus_string_append_byte (str, DBUS_TYPE_ARRAY))
+    goto oom;
+
+  if (!node_build_signature (_dbus_list_get_first (&container->children),
+                             str))
+    goto oom;
+
+  return TRUE;
+
+ oom:
+  _dbus_string_set_length (str, orig_len);
+  return FALSE;
+}
+
+ /* 10 is random just to add another seed that we use in the suite */
+#define VARIANT_SEED 10
+
+static dbus_bool_t
+variant_write_value (TestTypeNode   *node,
+                     DataBlock      *block,
+                     DBusTypeWriter *writer,
+                     int             seed)
+{
+  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+  DataBlockState saved;
+  DBusTypeWriter sub;
+  DBusString content_signature;
+  TestTypeNode *child;
+
+  _dbus_assert (container->children != NULL);
+  _dbus_assert (_dbus_list_length_is_one (&container->children));
+
+  child = _dbus_list_get_first (&container->children);
+
+  data_block_save (block, &saved);
+
+  if (!_dbus_string_init (&content_signature))
+    return FALSE;
+
+  if (!node_build_signature (child,
+                             &content_signature))
+    goto oom;
+
+  if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_VARIANT,
+                                  &content_signature, 0,
+                                  &sub))
+    goto oom;
+
+  if (!node_write_value (child, block, &sub, seed + VARIANT_SEED))
+    goto oom;
+
+  if (!_dbus_type_writer_unrecurse (writer, &sub))
+    goto oom;
+
+  _dbus_string_free (&content_signature);
+  return TRUE;
+
+ oom:
+  data_block_restore (block, &saved);
+  _dbus_string_free (&content_signature);
+  return FALSE;
+}
+
+static dbus_bool_t
+variant_read_or_set_value (TestTypeNode   *node,
+                           DBusTypeReader *reader,
+                           DBusTypeReader *realign_root,
+                           int             seed)
+{
+  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+  DBusTypeReader sub;
+  TestTypeNode *child;
+
+  _dbus_assert (container->children != NULL);
+  _dbus_assert (_dbus_list_length_is_one (&container->children));
+
+  child = _dbus_list_get_first (&container->children);
+
+  check_expected_type (reader, DBUS_TYPE_VARIANT);
+
+  _dbus_type_reader_recurse (reader, &sub);
+
+  if (realign_root == NULL)
+    {
+      if (!node_read_value (child, &sub, seed + VARIANT_SEED))
+        return FALSE;
+    }
+  else
+    {
+      if (!node_set_value (child, &sub, realign_root, seed + VARIANT_SEED))
+        return FALSE;
+    }
+
+  NEXT_EXPECTING_FALSE (&sub);
+
+  return TRUE;
+}
+
+static dbus_bool_t
+variant_read_value (TestTypeNode   *node,
+                    DBusTypeReader *reader,
+                    int             seed)
+{
+  return variant_read_or_set_value (node, reader, NULL, seed);
+}
+
+static dbus_bool_t
+variant_set_value (TestTypeNode   *node,
+                   DBusTypeReader *reader,
+                   DBusTypeReader *realign_root,
+                   int             seed)
+{
+  return variant_read_or_set_value (node, reader, realign_root, seed);
+}
+
+static dbus_bool_t
+dict_write_value (TestTypeNode   *node,
+                  DataBlock      *block,
+                  DBusTypeWriter *writer,
+                  int             seed)
+{
+  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+  DataBlockState saved;
+  DBusTypeWriter sub;
+  DBusString entry_value_signature;
+  DBusString dict_entry_signature;
+  int i;
+  int n_entries;
+  int entry_value_type;
+  TestTypeNode *child;
+
+  n_entries = node->klass->subclass_detail;
+
+  _dbus_assert (container->children != NULL);
+
+  data_block_save (block, &saved);
+
+  if (!_dbus_string_init (&entry_value_signature))
+    return FALSE;
+
+  if (!_dbus_string_init (&dict_entry_signature))
+    {
+      _dbus_string_free (&entry_value_signature);
+      return FALSE;
+    }
+  
+  child = _dbus_list_get_first (&container->children);
+
+  if (!node_build_signature (child,
+                             &entry_value_signature))
+    goto oom;
+
+  if (!_dbus_string_append (&dict_entry_signature,
+                            DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                            DBUS_TYPE_INT32_AS_STRING))
+    goto oom;
+
+  if (!_dbus_string_copy (&entry_value_signature, 0,
+                          &dict_entry_signature,
+                          _dbus_string_get_length (&dict_entry_signature)))
+    goto oom;
+
+  if (!_dbus_string_append_byte (&dict_entry_signature,
+                                 DBUS_DICT_ENTRY_END_CHAR))
+    goto oom;
+  
+  entry_value_type = _dbus_first_type_in_signature (&entry_value_signature, 0);
+  
+  if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_ARRAY,
+                                  &dict_entry_signature, 0,
+                                  &sub))
+    goto oom;
+
+  i = 0;
+  while (i < n_entries)
+    {
+      DBusTypeWriter entry_sub;
+      dbus_int32_t key;
+
+      if (!_dbus_type_writer_recurse (&sub, DBUS_TYPE_DICT_ENTRY,
+                                      NULL, 0,
+                                      &entry_sub))
+        goto oom;
+
+      key = int32_from_seed (seed + i);
+
+      if (!_dbus_type_writer_write_basic (&entry_sub,
+                                          DBUS_TYPE_INT32,
+                                          &key))
+        goto oom;
+      
+      if (!node_write_value (child, block, &entry_sub, seed + i))
+        goto oom;
+
+      if (!_dbus_type_writer_unrecurse (&sub, &entry_sub))
+        goto oom;
+      
+      ++i;
+    }
+
+  if (!_dbus_type_writer_unrecurse (writer, &sub))
+    goto oom;
+  
+  _dbus_string_free (&entry_value_signature);
+  _dbus_string_free (&dict_entry_signature);
+  return TRUE;
+
+ oom:
+  data_block_restore (block, &saved);
+  _dbus_string_free (&entry_value_signature);
+  _dbus_string_free (&dict_entry_signature);
+  return FALSE;
+}
+
+static dbus_bool_t
+dict_read_or_set_value (TestTypeNode   *node,
+                        DBusTypeReader *reader,
+                        DBusTypeReader *realign_root,
+                        int             seed)
+{
+  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+  DBusTypeReader sub;
+  int i;
+  int n_entries;
+  TestTypeNode *child;
+
+  n_entries = node->klass->subclass_detail;
+
+  check_expected_type (reader, DBUS_TYPE_ARRAY);
+
+  child = _dbus_list_get_first (&container->children);
+
+  if (n_entries > 0)
+    {
+      _dbus_type_reader_recurse (reader, &sub);
+
+      check_expected_type (&sub, DBUS_TYPE_DICT_ENTRY);
+      
+      i = 0;
+      while (i < n_entries)
+        {
+          DBusTypeReader entry_sub;
+
+          check_expected_type (&sub, DBUS_TYPE_DICT_ENTRY);
+          
+          _dbus_type_reader_recurse (&sub, &entry_sub);
+          
+          if (realign_root == NULL)
+            {
+              dbus_int32_t v;
+              
+              check_expected_type (&entry_sub, DBUS_TYPE_INT32);
+
+              _dbus_type_reader_read_basic (&entry_sub,
+                                            (dbus_int32_t*) &v);
+
+              _dbus_assert (v == int32_from_seed (seed + i));
+
+              NEXT_EXPECTING_TRUE (&entry_sub);
+              
+              if (!node_read_value (child, &entry_sub, seed + i))
+                return FALSE;
+
+              NEXT_EXPECTING_FALSE (&entry_sub);
+            }
+          else
+            {
+              dbus_int32_t v;
+              
+              v = int32_from_seed (seed + i);
+              
+              if (!_dbus_type_reader_set_basic (&entry_sub,
+                                                &v,
+                                                realign_root))
+                return FALSE;
+
+              NEXT_EXPECTING_TRUE (&entry_sub);
+              
+              if (!node_set_value (child, &entry_sub, realign_root, seed + i))
+                return FALSE;
+
+              NEXT_EXPECTING_FALSE (&entry_sub);
+            }
+          
+          if (i == (n_entries - 1))
+            NEXT_EXPECTING_FALSE (&sub);
+          else
+            NEXT_EXPECTING_TRUE (&sub);
+
+          ++i;
+        }
+    }
+
+  return TRUE;
+}
+
+static dbus_bool_t
+dict_read_value (TestTypeNode   *node,
+                 DBusTypeReader *reader,
+                 int             seed)
+{
+  return dict_read_or_set_value (node, reader, NULL, seed);
+}
+
+static dbus_bool_t
+dict_set_value (TestTypeNode   *node,
+                DBusTypeReader *reader,
+                DBusTypeReader *realign_root,
+                int             seed)
+{
+  return dict_read_or_set_value (node, reader, realign_root, seed);
+}
+
+static dbus_bool_t
+dict_build_signature (TestTypeNode   *node,
+                      DBusString     *str)
+{
+  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+  int orig_len;
+
+  orig_len = _dbus_string_get_length (str);
+
+  if (!_dbus_string_append_byte (str, DBUS_TYPE_ARRAY))
+    goto oom;
+
+  if (!_dbus_string_append (str, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_INT32_AS_STRING))
+    goto oom;
+  
+  if (!node_build_signature (_dbus_list_get_first (&container->children),
+                             str))
+    goto oom;
+
+  if (!_dbus_string_append_byte (str, DBUS_DICT_ENTRY_END_CHAR))
+    goto oom;
+
+  return TRUE;
+
+ oom:
+  _dbus_string_set_length (str, orig_len);
+  return FALSE;
+}
+
+static void
+container_destroy (TestTypeNode *node)
+{
+  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+  DBusList *link;
+
+  link = _dbus_list_get_first_link (&container->children);
+  while (link != NULL)
+    {
+      TestTypeNode *child = link->data;
+      DBusList *next = _dbus_list_get_next_link (&container->children, link);
+
+      node_destroy (child);
+
+      _dbus_list_free_link (link);
+
+      link = next;
+    }
+}
+
+#endif /* !DOXYGEN_SHOULD_SKIP_THIS */
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-marshal-recursive.c b/src/dbus/dbus-marshal-recursive.c
new file mode 100644 (file)
index 0000000..76ee344
--- /dev/null
@@ -0,0 +1,2739 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-recursive.c  Marshalling routines for recursive types
+ *
+ * Copyright (C) 2004, 2005 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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-marshal-recursive.h"
+#include "dbus-marshal-basic.h"
+#include "dbus-signature.h"
+#include "dbus-internals.h"
+
+/**
+ * @addtogroup DBusMarshal
+ * @{
+ */
+
+/** turn this on to get deluged in TypeReader verbose spam */
+#define RECURSIVE_MARSHAL_READ_TRACE  0
+
+/** turn this on to get deluged in TypeWriter verbose spam */
+#define RECURSIVE_MARSHAL_WRITE_TRACE 0
+
+static void
+free_fixups (DBusList **fixups)
+{
+  DBusList *link;
+
+  link = _dbus_list_get_first_link (fixups);
+  while (link != NULL)
+    {
+      DBusList *next;
+
+      next = _dbus_list_get_next_link (fixups, link);
+
+      dbus_free (link->data);
+      _dbus_list_free_link (link);
+
+      link = next;
+    }
+
+  *fixups = NULL;
+}
+
+static void
+apply_and_free_fixups (DBusList      **fixups,
+                       DBusTypeReader *reader)
+{
+  DBusList *link;
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+  if (*fixups)
+    _dbus_verbose (" %d FIXUPS to apply\n",
+                   _dbus_list_get_length (fixups));
+#endif
+
+  link = _dbus_list_get_first_link (fixups);
+  while (link != NULL)
+    {
+      DBusList *next;
+
+      next = _dbus_list_get_next_link (fixups, link);
+
+      if (reader)
+        {
+          DBusArrayLenFixup *f;
+
+          f = link->data;
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+          _dbus_verbose (" applying FIXUP to reader %p at pos %d new_len = %d old len %d\n",
+                         reader, f->len_pos_in_reader, f->new_len,
+                         _dbus_marshal_read_uint32 (reader->value_str,
+                                                    f->len_pos_in_reader,
+                                                    reader->byte_order, NULL));
+#endif
+
+          _dbus_marshal_set_uint32 ((DBusString*) reader->value_str,
+                                    f->len_pos_in_reader,
+                                    f->new_len,
+                                    reader->byte_order);
+        }
+
+      dbus_free (link->data);
+      _dbus_list_free_link (link);
+
+      link = next;
+    }
+
+  *fixups = NULL;
+}
+
+/**
+ * Virtual table for a type reader.
+ */
+struct DBusTypeReaderClass
+{
+  const char *name;       /**< name for debugging */
+  int         id;         /**< index in all_reader_classes */
+  dbus_bool_t types_only; /**< only iterates over types, not values */
+  void        (* recurse)          (DBusTypeReader        *sub,
+                                    DBusTypeReader        *parent); /**< recurse with this reader as sub */
+  dbus_bool_t (* check_finished)   (const DBusTypeReader  *reader); /**< check whether reader is at the end */
+  void        (* next)             (DBusTypeReader        *reader,
+                                    int                    current_type); /**< go to the next value */
+};
+
+static int
+element_type_get_alignment (const DBusString *str,
+                            int               pos)
+{
+  return _dbus_type_get_alignment (_dbus_first_type_in_signature (str, pos));
+}
+
+static void
+reader_init (DBusTypeReader    *reader,
+             int                byte_order,
+             const DBusString  *type_str,
+             int                type_pos,
+             const DBusString  *value_str,
+             int                value_pos)
+{
+  reader->byte_order = byte_order;
+  reader->finished = FALSE;
+  reader->type_str = type_str;
+  reader->type_pos = type_pos;
+  reader->value_str = value_str;
+  reader->value_pos = value_pos;
+}
+
+static void
+base_reader_recurse (DBusTypeReader *sub,
+                     DBusTypeReader *parent)
+{
+  /* point subreader at the same place as parent */
+  reader_init (sub,
+               parent->byte_order,
+               parent->type_str,
+               parent->type_pos,
+               parent->value_str,
+               parent->value_pos);
+}
+
+static void
+struct_or_dict_entry_types_only_reader_recurse (DBusTypeReader *sub,
+                                                DBusTypeReader *parent)
+{
+  base_reader_recurse (sub, parent);
+  
+  _dbus_assert (_dbus_string_get_byte (sub->type_str,
+                                       sub->type_pos) == DBUS_STRUCT_BEGIN_CHAR ||
+                _dbus_string_get_byte (sub->type_str,
+                                       sub->type_pos) == DBUS_DICT_ENTRY_BEGIN_CHAR);
+
+  sub->type_pos += 1;
+}
+
+static void
+struct_or_dict_entry_reader_recurse (DBusTypeReader *sub,
+                                     DBusTypeReader *parent)
+{
+  struct_or_dict_entry_types_only_reader_recurse (sub, parent);
+
+  /* struct and dict entry have 8 byte alignment */
+  sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 8);
+}
+
+static void
+array_types_only_reader_recurse (DBusTypeReader *sub,
+                                 DBusTypeReader *parent)
+{
+  base_reader_recurse (sub, parent);
+
+  /* point type_pos at the array element type */
+  sub->type_pos += 1;
+
+  /* Init with values likely to crash things if misused */
+  sub->u.array.start_pos = _DBUS_INT_MAX;
+  sub->array_len_offset = 7;
+}
+
+/** compute position of array length given array_len_offset, which is
+    the offset back from start_pos to end of the len */
+#define ARRAY_READER_LEN_POS(reader) \
+  ((reader)->u.array.start_pos - ((int)(reader)->array_len_offset) - 4)
+
+static int
+array_reader_get_array_len (const DBusTypeReader *reader)
+{
+  dbus_uint32_t array_len;
+  int len_pos;
+
+  len_pos = ARRAY_READER_LEN_POS (reader);
+
+  _dbus_assert (_DBUS_ALIGN_VALUE (len_pos, 4) == (unsigned) len_pos);
+  array_len = _dbus_unpack_uint32 (reader->byte_order,
+                                   _dbus_string_get_const_data_len (reader->value_str, len_pos, 4));
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+  _dbus_verbose ("   reader %p len_pos %d array len %u len_offset %d\n",
+                 reader, len_pos, array_len, reader->array_len_offset);
+#endif
+
+  _dbus_assert (reader->u.array.start_pos - len_pos - 4 < 8);
+
+  return array_len;
+}
+
+static void
+array_reader_recurse (DBusTypeReader *sub,
+                      DBusTypeReader *parent)
+{
+  int alignment;
+  int len_pos;
+
+  array_types_only_reader_recurse (sub, parent);
+
+  sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 4);
+
+  len_pos = sub->value_pos;
+
+  sub->value_pos += 4; /* for the length */
+
+  alignment = element_type_get_alignment (sub->type_str,
+                                          sub->type_pos);
+
+  sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, alignment);
+
+  sub->u.array.start_pos = sub->value_pos;
+  _dbus_assert ((sub->u.array.start_pos - (len_pos + 4)) < 8); /* only 3 bits in array_len_offset */
+  sub->array_len_offset = sub->u.array.start_pos - (len_pos + 4);
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+  _dbus_verbose ("    type reader %p array start = %d len_offset = %d array len = %d array element type = %s\n",
+                 sub,
+                 sub->u.array.start_pos,
+                 sub->array_len_offset,
+                 array_reader_get_array_len (sub),
+                 _dbus_type_to_string (_dbus_first_type_in_signature (sub->type_str,
+                                                                sub->type_pos)));
+#endif
+}
+
+static void
+variant_reader_recurse (DBusTypeReader *sub,
+                        DBusTypeReader *parent)
+{
+  int sig_len;
+  int contained_alignment;
+
+  base_reader_recurse (sub, parent);
+
+  /* Variant is 1 byte sig length (without nul), signature with nul,
+   * padding to 8-boundary, then values
+   */
+
+  sig_len = _dbus_string_get_byte (sub->value_str, sub->value_pos);
+
+  sub->type_str = sub->value_str;
+  sub->type_pos = sub->value_pos + 1;
+
+  sub->value_pos = sub->type_pos + sig_len + 1;
+
+  contained_alignment = _dbus_type_get_alignment (_dbus_first_type_in_signature (sub->type_str,
+                                                                           sub->type_pos));
+  
+  sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, contained_alignment);
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+  _dbus_verbose ("    type reader %p variant containing '%s'\n",
+                 sub,
+                 _dbus_string_get_const_data_len (sub->type_str,
+                                                  sub->type_pos, 0));
+#endif
+}
+
+static dbus_bool_t
+array_reader_check_finished (const DBusTypeReader *reader)
+{
+  int end_pos;
+
+  /* return the array element type if elements remain, and
+   * TYPE_INVALID otherwise
+   */
+
+  end_pos = reader->u.array.start_pos + array_reader_get_array_len (reader);
+
+  _dbus_assert (reader->value_pos <= end_pos);
+  _dbus_assert (reader->value_pos >= reader->u.array.start_pos);
+
+  return reader->value_pos == end_pos;
+}
+
+static void
+skip_one_complete_type (const DBusString *type_str,
+                        int              *type_pos)
+{
+  _dbus_type_signature_next (_dbus_string_get_const_data (type_str),
+                            type_pos);
+}
+
+/**
+ * Skips to the next "complete" type inside a type signature.
+ * The signature is read starting at type_pos, and the next
+ * type position is stored in the same variable.
+ *
+ * @param type_str a type signature (must be valid)
+ * @param type_pos an integer position in the type signature (in and out)
+ */
+void
+_dbus_type_signature_next (const char       *type_str,
+                          int              *type_pos)
+{
+  const unsigned char *p;
+  const unsigned char *start;
+
+  _dbus_assert (type_str != NULL);
+  _dbus_assert (type_pos != NULL);
+  
+  start = type_str;
+  p = start + *type_pos;
+
+  _dbus_assert (*p != DBUS_STRUCT_END_CHAR);
+  _dbus_assert (*p != DBUS_DICT_ENTRY_END_CHAR);
+  
+  while (*p == DBUS_TYPE_ARRAY)
+    ++p;
+
+  _dbus_assert (*p != DBUS_STRUCT_END_CHAR);
+  _dbus_assert (*p != DBUS_DICT_ENTRY_END_CHAR);
+  
+  if (*p == DBUS_STRUCT_BEGIN_CHAR)
+    {
+      int depth;
+
+      depth = 1;
+
+      while (TRUE)
+        {
+          _dbus_assert (*p != DBUS_TYPE_INVALID);
+
+          ++p;
+
+          _dbus_assert (*p != DBUS_TYPE_INVALID);
+
+          if (*p == DBUS_STRUCT_BEGIN_CHAR)
+            depth += 1;
+          else if (*p == DBUS_STRUCT_END_CHAR)
+            {
+              depth -= 1;
+              if (depth == 0)
+                {
+                  ++p;
+                  break;
+                }
+            }
+        }
+    }
+  else if (*p == DBUS_DICT_ENTRY_BEGIN_CHAR)
+    {
+      int depth;
+
+      depth = 1;
+
+      while (TRUE)
+        {
+          _dbus_assert (*p != DBUS_TYPE_INVALID);
+
+          ++p;
+
+          _dbus_assert (*p != DBUS_TYPE_INVALID);
+
+          if (*p == DBUS_DICT_ENTRY_BEGIN_CHAR)
+            depth += 1;
+          else if (*p == DBUS_DICT_ENTRY_END_CHAR)
+            {
+              depth -= 1;
+              if (depth == 0)
+                {
+                  ++p;
+                  break;
+                }
+            }
+        }
+    }
+  else
+    {
+      ++p;
+    }
+
+  *type_pos = (int) (p - start);
+}
+
+static int
+find_len_of_complete_type (const DBusString *type_str,
+                           int               type_pos)
+{
+  int end;
+
+  end = type_pos;
+
+  skip_one_complete_type (type_str, &end);
+
+  return end - type_pos;
+}
+
+static void
+base_reader_next (DBusTypeReader *reader,
+                  int             current_type)
+{
+  switch (current_type)
+    {
+    case DBUS_TYPE_DICT_ENTRY:
+    case DBUS_TYPE_STRUCT:
+    case DBUS_TYPE_VARIANT:
+      /* Scan forward over the entire container contents */
+      {
+        DBusTypeReader sub;
+
+        if (reader->klass->types_only && current_type == DBUS_TYPE_VARIANT)
+          ;
+        else
+          {
+            /* Recurse into the struct or variant */
+            _dbus_type_reader_recurse (reader, &sub);
+
+            /* Skip everything in this subreader */
+            while (_dbus_type_reader_next (&sub))
+              {
+                /* nothing */;
+              }
+          }
+        if (!reader->klass->types_only)
+          reader->value_pos = sub.value_pos;
+
+        /* Now we are at the end of this container; for variants, the
+         * subreader's type_pos is totally inapplicable (it's in the
+         * value string) but we know that we increment by one past the
+         * DBUS_TYPE_VARIANT
+         */
+        if (current_type == DBUS_TYPE_VARIANT)
+          reader->type_pos += 1;
+        else
+          reader->type_pos = sub.type_pos;
+      }
+      break;
+
+    case DBUS_TYPE_ARRAY:
+      {
+        if (!reader->klass->types_only)
+          _dbus_marshal_skip_array (reader->value_str,
+                                    _dbus_first_type_in_signature (reader->type_str,
+                                                                   reader->type_pos + 1),
+                                    reader->byte_order,
+                                    &reader->value_pos);
+
+        skip_one_complete_type (reader->type_str, &reader->type_pos);
+      }
+      break;
+
+    default:
+      if (!reader->klass->types_only)
+        _dbus_marshal_skip_basic (reader->value_str,
+                                  current_type, reader->byte_order,
+                                  &reader->value_pos);
+
+      reader->type_pos += 1;
+      break;
+    }
+}
+
+static void
+struct_reader_next (DBusTypeReader *reader,
+                    int             current_type)
+{
+  int t;
+
+  base_reader_next (reader, current_type);
+
+  /* for STRUCT containers we return FALSE at the end of the struct,
+   * for INVALID we return FALSE at the end of the signature.
+   * In both cases we arrange for get_current_type() to return INVALID
+   * which is defined to happen iff we're at the end (no more next())
+   */
+  t = _dbus_string_get_byte (reader->type_str, reader->type_pos);
+  if (t == DBUS_STRUCT_END_CHAR)
+    {
+      reader->type_pos += 1;
+      reader->finished = TRUE;
+    }
+}
+
+static void
+dict_entry_reader_next (DBusTypeReader *reader,
+                        int             current_type)
+{
+  int t;
+
+  base_reader_next (reader, current_type);
+
+  /* for STRUCT containers we return FALSE at the end of the struct,
+   * for INVALID we return FALSE at the end of the signature.
+   * In both cases we arrange for get_current_type() to return INVALID
+   * which is defined to happen iff we're at the end (no more next())
+   */
+  t = _dbus_string_get_byte (reader->type_str, reader->type_pos);
+  if (t == DBUS_DICT_ENTRY_END_CHAR)
+    {
+      reader->type_pos += 1;
+      reader->finished = TRUE;
+    }
+}
+
+static void
+array_types_only_reader_next (DBusTypeReader *reader,
+                              int             current_type)
+{
+  /* We have one "element" to be iterated over
+   * in each array, which is its element type.
+   * So the finished flag indicates whether we've
+   * iterated over it yet or not.
+   */
+  reader->finished = TRUE;
+}
+
+static void
+array_reader_next (DBusTypeReader *reader,
+                   int             current_type)
+{
+  /* Skip one array element */
+  int end_pos;
+
+  end_pos = reader->u.array.start_pos + array_reader_get_array_len (reader);
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+  _dbus_verbose ("  reader %p array next START start_pos = %d end_pos = %d value_pos = %d current_type = %s\n",
+                 reader,
+                 reader->u.array.start_pos,
+                 end_pos, reader->value_pos,
+                 _dbus_type_to_string (current_type));
+#endif
+
+  _dbus_assert (reader->value_pos < end_pos);
+  _dbus_assert (reader->value_pos >= reader->u.array.start_pos);
+
+  switch (_dbus_first_type_in_signature (reader->type_str,
+                                         reader->type_pos))
+    {
+    case DBUS_TYPE_DICT_ENTRY:
+    case DBUS_TYPE_STRUCT:
+    case DBUS_TYPE_VARIANT:
+      {
+        DBusTypeReader sub;
+
+        /* Recurse into the struct or variant */
+        _dbus_type_reader_recurse (reader, &sub);
+
+        /* Skip everything in this element */
+        while (_dbus_type_reader_next (&sub))
+          {
+            /* nothing */;
+          }
+
+        /* Now we are at the end of this element */
+        reader->value_pos = sub.value_pos;
+      }
+      break;
+
+    case DBUS_TYPE_ARRAY:
+      {
+        _dbus_marshal_skip_array (reader->value_str,
+                                  _dbus_first_type_in_signature (reader->type_str,
+                                                           reader->type_pos + 1),
+                                  reader->byte_order,
+                                  &reader->value_pos);
+      }
+      break;
+
+    default:
+      {
+        _dbus_marshal_skip_basic (reader->value_str,
+                                  current_type, reader->byte_order,
+                                  &reader->value_pos);
+      }
+      break;
+    }
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+  _dbus_verbose ("  reader %p array next END start_pos = %d end_pos = %d value_pos = %d current_type = %s\n",
+                 reader,
+                 reader->u.array.start_pos,
+                 end_pos, reader->value_pos,
+                 _dbus_type_to_string (current_type));
+#endif
+
+  _dbus_assert (reader->value_pos <= end_pos);
+
+  if (reader->value_pos == end_pos)
+    {
+      skip_one_complete_type (reader->type_str,
+                              &reader->type_pos);
+    }
+}
+
+static const DBusTypeReaderClass body_reader_class = {
+  "body", 0,
+  FALSE,
+  NULL, /* body is always toplevel, so doesn't get recursed into */
+  NULL,
+  base_reader_next
+};
+
+static const DBusTypeReaderClass body_types_only_reader_class = {
+  "body types", 1,
+  TRUE,
+  NULL, /* body is always toplevel, so doesn't get recursed into */
+  NULL,
+  base_reader_next
+};
+
+static const DBusTypeReaderClass struct_reader_class = {
+  "struct", 2,
+  FALSE,
+  struct_or_dict_entry_reader_recurse,
+  NULL,
+  struct_reader_next
+};
+
+static const DBusTypeReaderClass struct_types_only_reader_class = {
+  "struct types", 3,
+  TRUE,
+  struct_or_dict_entry_types_only_reader_recurse,
+  NULL,
+  struct_reader_next
+};
+
+static const DBusTypeReaderClass dict_entry_reader_class = {
+  "dict_entry", 4,
+  FALSE,
+  struct_or_dict_entry_reader_recurse,
+  NULL,
+  dict_entry_reader_next
+};
+
+static const DBusTypeReaderClass dict_entry_types_only_reader_class = {
+  "dict_entry types", 5,
+  TRUE,
+  struct_or_dict_entry_types_only_reader_recurse,
+  NULL,
+  dict_entry_reader_next
+};
+
+static const DBusTypeReaderClass array_reader_class = {
+  "array", 6,
+  FALSE,
+  array_reader_recurse,
+  array_reader_check_finished,
+  array_reader_next
+};
+
+static const DBusTypeReaderClass array_types_only_reader_class = {
+  "array types", 7,
+  TRUE,
+  array_types_only_reader_recurse,
+  NULL,
+  array_types_only_reader_next
+};
+
+static const DBusTypeReaderClass variant_reader_class = {
+  "variant", 8,
+  FALSE,
+  variant_reader_recurse,
+  NULL,
+  base_reader_next
+};
+
+#ifndef DBUS_DISABLE_ASSERT
+static const DBusTypeReaderClass * const
+all_reader_classes[] = {
+  &body_reader_class,
+  &body_types_only_reader_class,
+  &struct_reader_class,
+  &struct_types_only_reader_class,
+  &dict_entry_reader_class,
+  &dict_entry_types_only_reader_class,
+  &array_reader_class,
+  &array_types_only_reader_class,
+  &variant_reader_class
+};
+#endif
+
+/**
+ * Initializes a type reader.
+ *
+ * @param reader the reader
+ * @param byte_order the byte order of the block to read
+ * @param type_str the signature of the block to read
+ * @param type_pos location of signature
+ * @param value_str the string containing values block
+ * @param value_pos start of values block
+ */
+void
+_dbus_type_reader_init (DBusTypeReader    *reader,
+                        int                byte_order,
+                        const DBusString  *type_str,
+                        int                type_pos,
+                        const DBusString  *value_str,
+                        int                value_pos)
+{
+  reader->klass = &body_reader_class;
+
+  reader_init (reader, byte_order, type_str, type_pos,
+               value_str, value_pos);
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+  _dbus_verbose ("  type reader %p init type_pos = %d value_pos = %d remaining sig '%s'\n",
+                 reader, reader->type_pos, reader->value_pos,
+                 _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0));
+#endif
+}
+
+/**
+ * Like _dbus_type_reader_init() but the iteration is over the
+ * signature, not over values.
+ *
+ * @param reader the reader
+ * @param type_str the signature string
+ * @param type_pos location in the signature string
+ */
+void
+_dbus_type_reader_init_types_only (DBusTypeReader    *reader,
+                                   const DBusString  *type_str,
+                                   int                type_pos)
+{
+  reader->klass = &body_types_only_reader_class;
+
+  reader_init (reader, DBUS_COMPILER_BYTE_ORDER /* irrelevant */,
+               type_str, type_pos, NULL, _DBUS_INT_MAX /* crashes if we screw up */);
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+  _dbus_verbose ("  type reader %p init types only type_pos = %d remaining sig '%s'\n",
+                 reader, reader->type_pos,
+                 _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0));
+#endif
+}
+
+/**
+ * Gets the type of the value the reader is currently pointing to;
+ * or for a types-only reader gets the type it's currently pointing to.
+ * If the reader is at the end of a block or end of a container such
+ * as an array, returns #DBUS_TYPE_INVALID.
+ *
+ * @param reader the reader
+ */
+int
+_dbus_type_reader_get_current_type (const DBusTypeReader *reader)
+{
+  int t;
+
+  if (reader->finished ||
+      (reader->klass->check_finished &&
+       (* reader->klass->check_finished) (reader)))
+    t = DBUS_TYPE_INVALID;
+  else
+    t = _dbus_first_type_in_signature (reader->type_str,
+                                       reader->type_pos);
+
+  _dbus_assert (t != DBUS_STRUCT_END_CHAR);
+  _dbus_assert (t != DBUS_STRUCT_BEGIN_CHAR);
+  _dbus_assert (t != DBUS_DICT_ENTRY_END_CHAR);
+  _dbus_assert (t != DBUS_DICT_ENTRY_BEGIN_CHAR);
+  
+#if 0
+  _dbus_verbose ("  type reader %p current type_pos = %d type = %s\n",
+                 reader, reader->type_pos,
+                 _dbus_type_to_string (t));
+#endif
+
+  return t;
+}
+
+/**
+ * Gets the type of an element of the array the reader is currently
+ * pointing to. It's an error to call this if
+ * _dbus_type_reader_get_current_type() doesn't return #DBUS_TYPE_ARRAY
+ * for this reader.
+ *
+ * @param reader the reader
+ */
+int
+_dbus_type_reader_get_element_type (const DBusTypeReader  *reader)
+{
+  int element_type;
+
+  _dbus_assert (_dbus_type_reader_get_current_type (reader) == DBUS_TYPE_ARRAY);
+
+  element_type = _dbus_first_type_in_signature (reader->type_str,
+                                          reader->type_pos + 1);
+
+  return element_type;
+}
+
+/**
+ * Gets the current position in the value block
+ * @param reader the reader
+ */
+int
+_dbus_type_reader_get_value_pos (const DBusTypeReader  *reader)
+{
+  return reader->value_pos;
+}
+
+/**
+ * Get the address of the marshaled value in the data being read.  The
+ * address may not be aligned; you have to align it to the type of the
+ * value you want to read. Most of the demarshal routines do this for
+ * you.
+ *
+ * @param reader the reader
+ * @param value_location the address of the marshaled value
+ */
+void
+_dbus_type_reader_read_raw (const DBusTypeReader  *reader,
+                            const unsigned char  **value_location)
+{
+  _dbus_assert (!reader->klass->types_only);
+
+  *value_location = _dbus_string_get_const_data_len (reader->value_str,
+                                                     reader->value_pos,
+                                                     0);
+}
+
+/**
+ * Reads a basic-typed value, as with _dbus_marshal_read_basic().
+ *
+ * @param reader the reader
+ * @param value the address of the value
+ */
+void
+_dbus_type_reader_read_basic (const DBusTypeReader    *reader,
+                              void                    *value)
+{
+  int t;
+
+  _dbus_assert (!reader->klass->types_only);
+
+  t = _dbus_type_reader_get_current_type (reader);
+
+  _dbus_marshal_read_basic (reader->value_str,
+                            reader->value_pos,
+                            t, value,
+                            reader->byte_order,
+                            NULL);
+
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+  _dbus_verbose ("  type reader %p read basic type_pos = %d value_pos = %d remaining sig '%s'\n",
+                 reader, reader->type_pos, reader->value_pos,
+                 _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0));
+#endif
+}
+
+/**
+ * Returns the number of bytes in the array.
+ *
+ * @param reader the reader to read from
+ * @returns the number of bytes in the array
+ */
+int
+_dbus_type_reader_get_array_length (const DBusTypeReader  *reader)
+{
+  _dbus_assert (!reader->klass->types_only);
+  _dbus_assert (reader->klass == &array_reader_class);
+
+  return array_reader_get_array_len (reader);
+}
+
+/**
+ * Reads a block of fixed-length basic values, from the current point
+ * in an array to the end of the array.  Does not work for arrays of
+ * string or container types.
+ *
+ * This function returns the array in-place; it does not make a copy,
+ * and it does not swap the bytes.
+ *
+ * If you ask for #DBUS_TYPE_DOUBLE you will get a "const double*" back
+ * and the "value" argument should be a "const double**" and so on.
+ *
+ * @param reader the reader to read from
+ * @param value place to return the array values
+ * @param n_elements place to return number of array elements
+ */
+void
+_dbus_type_reader_read_fixed_multi (const DBusTypeReader  *reader,
+                                    void                  *value,
+                                    int                   *n_elements)
+{
+  int element_type;
+  int end_pos;
+  int remaining_len;
+  int alignment;
+  int total_len;
+
+  _dbus_assert (!reader->klass->types_only);
+  _dbus_assert (reader->klass == &array_reader_class);
+
+  element_type = _dbus_first_type_in_signature (reader->type_str,
+                                                reader->type_pos);
+
+  _dbus_assert (element_type != DBUS_TYPE_INVALID); /* why we don't use get_current_type() */
+  _dbus_assert (dbus_type_is_fixed (element_type));
+
+  alignment = _dbus_type_get_alignment (element_type);
+
+  _dbus_assert (reader->value_pos >= reader->u.array.start_pos);
+
+  total_len = array_reader_get_array_len (reader);
+  end_pos = reader->u.array.start_pos + total_len;
+  remaining_len = end_pos - reader->value_pos;
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+  _dbus_verbose ("end_pos %d total_len %d remaining_len %d value_pos %d\n",
+                 end_pos, total_len, remaining_len, reader->value_pos);
+#endif
+
+  _dbus_assert (remaining_len <= total_len);
+
+  if (remaining_len == 0)
+    *(const DBusBasicValue**) value = NULL;
+  else
+    *(const DBusBasicValue**) value =
+      (void*) _dbus_string_get_const_data_len (reader->value_str,
+                                               reader->value_pos,
+                                               remaining_len);
+
+  *n_elements = remaining_len / alignment;
+  _dbus_assert ((remaining_len % alignment) == 0);
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+  _dbus_verbose ("  type reader %p read fixed array type_pos = %d value_pos = %d remaining sig '%s'\n",
+                 reader, reader->type_pos, reader->value_pos,
+                 _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0));
+#endif
+}
+
+/**
+ * Initialize a new reader pointing to the first type and
+ * corresponding value that's a child of the current container. It's
+ * an error to call this if the current type is a non-container.
+ *
+ * Note that DBusTypeReader traverses values, not types. So if you
+ * have an empty array of array of int, you can't recurse into it. You
+ * can only recurse into each element.
+ *
+ * @param reader the reader
+ * @param sub a reader to init pointing to the first child
+ */
+void
+_dbus_type_reader_recurse (DBusTypeReader *reader,
+                           DBusTypeReader *sub)
+{
+  int t;
+
+  t = _dbus_first_type_in_signature (reader->type_str, reader->type_pos);
+
+  switch (t)
+    {
+    case DBUS_TYPE_STRUCT:
+      if (reader->klass->types_only)
+        sub->klass = &struct_types_only_reader_class;
+      else
+        sub->klass = &struct_reader_class;
+      break;
+    case DBUS_TYPE_DICT_ENTRY:
+      if (reader->klass->types_only)
+        sub->klass = &dict_entry_types_only_reader_class;
+      else
+        sub->klass = &dict_entry_reader_class;
+      break;
+    case DBUS_TYPE_ARRAY:
+      if (reader->klass->types_only)
+        sub->klass = &array_types_only_reader_class;
+      else
+        sub->klass = &array_reader_class;
+      break;
+    case DBUS_TYPE_VARIANT:
+      if (reader->klass->types_only)
+        _dbus_assert_not_reached ("can't recurse into variant typecode");
+      else
+        sub->klass = &variant_reader_class;
+      break;
+    default:
+      _dbus_verbose ("recursing into type %s\n", _dbus_type_to_string (t));
+#ifndef DBUS_DISABLE_CHECKS
+      if (t == DBUS_TYPE_INVALID)
+        _dbus_warn_check_failed ("You can't recurse into an empty array or off the end of a message body\n");
+#endif /* DBUS_DISABLE_CHECKS */
+
+      _dbus_assert_not_reached ("don't yet handle recursing into this type");
+    }
+
+  _dbus_assert (sub->klass == all_reader_classes[sub->klass->id]);
+
+  (* sub->klass->recurse) (sub, reader);
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+  _dbus_verbose ("  type reader %p RECURSED type_pos = %d value_pos = %d remaining sig '%s'\n",
+                 sub, sub->type_pos, sub->value_pos,
+                 _dbus_string_get_const_data_len (sub->type_str, sub->type_pos, 0));
+#endif
+}
+
+/**
+ * Skip to the next value on this "level". e.g. the next field in a
+ * struct, the next value in an array. Returns FALSE at the end of the
+ * current container.
+ *
+ * @param reader the reader
+ * @returns FALSE if nothing more to read at or below this level
+ */
+dbus_bool_t
+_dbus_type_reader_next (DBusTypeReader *reader)
+{
+  int t;
+
+  t = _dbus_type_reader_get_current_type (reader);
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+  _dbus_verbose ("  type reader %p START next() { type_pos = %d value_pos = %d remaining sig '%s' current_type = %s\n",
+                 reader, reader->type_pos, reader->value_pos,
+                 _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0),
+                 _dbus_type_to_string (t));
+#endif
+
+  if (t == DBUS_TYPE_INVALID)
+    return FALSE;
+
+  (* reader->klass->next) (reader, t);
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+  _dbus_verbose ("  type reader %p END next() type_pos = %d value_pos = %d remaining sig '%s' current_type = %s\n",
+                 reader, reader->type_pos, reader->value_pos,
+                 _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0),
+                 _dbus_type_to_string (_dbus_type_reader_get_current_type (reader)));
+#endif
+
+  return _dbus_type_reader_get_current_type (reader) != DBUS_TYPE_INVALID;
+}
+
+/**
+ * Check whether there's another value on this "level". e.g. the next
+ * field in a struct, the next value in an array. Returns FALSE at the
+ * end of the current container.
+ *
+ * You probably don't want to use this; it makes for an awkward for/while
+ * loop. A nicer one is "while ((current_type = get_current_type()) != INVALID)"
+ *
+ * @param reader the reader
+ * @returns FALSE if nothing more to read at or below this level
+ */
+dbus_bool_t
+_dbus_type_reader_has_next (const DBusTypeReader *reader)
+{
+  /* Not efficient but works for now. */
+  DBusTypeReader copy;
+
+  copy = *reader;
+  return _dbus_type_reader_next (&copy);
+}
+
+/**
+ * Gets the string and range of said string containing the signature
+ * of the current value. Essentially a more complete version of
+ * _dbus_type_reader_get_current_type() (returns the full type
+ * rather than only the outside of the onion).
+ *
+ * Note though that the first byte in a struct signature is
+ * #DBUS_STRUCT_BEGIN_CHAR while the current type will be
+ * #DBUS_TYPE_STRUCT so it isn't true that the first byte of the
+ * signature is always the same as the current type. Another
+ * difference is that this function will still return a signature when
+ * inside an empty array; say you recurse into empty array of int32,
+ * the signature is "i" but the current type will always be
+ * #DBUS_TYPE_INVALID since there are no elements to be currently
+ * pointing to.
+ *
+ * @param reader the reader
+ * @param str_p place to return the string with the type in it
+ * @param start_p place to return start of the type
+ * @param len_p place to return the length of the type
+ */
+void
+_dbus_type_reader_get_signature (const DBusTypeReader  *reader,
+                                 const DBusString     **str_p,
+                                 int                   *start_p,
+                                 int                   *len_p)
+{
+  *str_p = reader->type_str;
+  *start_p = reader->type_pos;
+  *len_p = find_len_of_complete_type (reader->type_str, reader->type_pos);
+}
+
+typedef struct
+{
+  DBusString replacement; /**< Marshaled value including alignment padding */
+  int padding;            /**< How much of the replacement block is padding */
+} ReplacementBlock;
+
+static dbus_bool_t
+replacement_block_init (ReplacementBlock *block,
+                        DBusTypeReader   *reader)
+{
+  if (!_dbus_string_init (&block->replacement))
+    return FALSE;
+
+  /* % 8 is the padding to have the same align properties in
+   * our replacement string as we do at the position being replaced
+   */
+  block->padding = reader->value_pos % 8;
+
+  if (!_dbus_string_lengthen (&block->replacement, block->padding))
+    goto oom;
+
+  return TRUE;
+
+ oom:
+  _dbus_string_free (&block->replacement);
+  return FALSE;
+}
+
+static dbus_bool_t
+replacement_block_replace (ReplacementBlock     *block,
+                           DBusTypeReader       *reader,
+                           const DBusTypeReader *realign_root)
+{
+  DBusTypeWriter writer;
+  DBusTypeReader realign_reader;
+  DBusList *fixups;
+  int orig_len;
+
+  _dbus_assert (realign_root != NULL);
+
+  orig_len = _dbus_string_get_length (&block->replacement);
+
+  realign_reader = *realign_root;
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+  _dbus_verbose ("INITIALIZING replacement block writer %p at value_pos %d\n",
+                 &writer, _dbus_string_get_length (&block->replacement));
+#endif
+  _dbus_type_writer_init_values_only (&writer,
+                                      realign_reader.byte_order,
+                                      realign_reader.type_str,
+                                      realign_reader.type_pos,
+                                      &block->replacement,
+                                      _dbus_string_get_length (&block->replacement));
+
+  _dbus_assert (realign_reader.value_pos <= reader->value_pos);
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+  _dbus_verbose ("COPYING from reader at value_pos %d to writer %p starting after value_pos %d\n",
+                 realign_reader.value_pos, &writer, reader->value_pos);
+#endif
+  fixups = NULL;
+  if (!_dbus_type_writer_write_reader_partial (&writer,
+                                               &realign_reader,
+                                               reader,
+                                               block->padding,
+                                               _dbus_string_get_length (&block->replacement) - block->padding,
+                                               &fixups))
+    goto oom;
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+  _dbus_verbose ("REPLACEMENT at padding %d len %d\n", block->padding,
+                 _dbus_string_get_length (&block->replacement) - block->padding);
+  _dbus_verbose_bytes_of_string (&block->replacement, block->padding,
+                                 _dbus_string_get_length (&block->replacement) - block->padding);
+  _dbus_verbose ("TO BE REPLACED at value_pos = %d (align pad %d) len %d realign_reader.value_pos %d\n",
+                 reader->value_pos, reader->value_pos % 8,
+                 realign_reader.value_pos - reader->value_pos,
+                 realign_reader.value_pos);
+  _dbus_verbose_bytes_of_string (reader->value_str,
+                                 reader->value_pos,
+                                 realign_reader.value_pos - reader->value_pos);
+#endif
+
+  /* Move the replacement into position
+   * (realign_reader should now be at the end of the block to be replaced)
+   */
+  if (!_dbus_string_replace_len (&block->replacement, block->padding,
+                                 _dbus_string_get_length (&block->replacement) - block->padding,
+                                 (DBusString*) reader->value_str,
+                                 reader->value_pos,
+                                 realign_reader.value_pos - reader->value_pos))
+    goto oom;
+
+  /* Process our fixups now that we can't have an OOM error */
+  apply_and_free_fixups (&fixups, reader);
+
+  return TRUE;
+
+ oom:
+  _dbus_string_set_length (&block->replacement, orig_len);
+  free_fixups (&fixups);
+  return FALSE;
+}
+
+static void
+replacement_block_free (ReplacementBlock *block)
+{
+  _dbus_string_free (&block->replacement);
+}
+
+/* In the variable-length case, we have to fix alignment after we insert.
+ * The strategy is as follows:
+ *
+ *  - pad a new string to have the same alignment as the
+ *    start of the current basic value
+ *  - write the new basic value
+ *  - copy from the original reader to the new string,
+ *    which will fix the alignment of types following
+ *    the new value
+ *    - this copy has to start at realign_root,
+ *      but not really write anything until it
+ *      passes the value being set
+ *    - as an optimization, we can stop copying
+ *      when the source and dest values are both
+ *      on an 8-boundary, since we know all following
+ *      padding and alignment will be identical
+ *  - copy the new string back to the original
+ *    string, replacing the relevant part of the
+ *    original string
+ *  - now any arrays in the original string that
+ *    contained the replaced string may have the
+ *    wrong length; so we have to fix that
+ */
+static dbus_bool_t
+reader_set_basic_variable_length (DBusTypeReader       *reader,
+                                  int                   current_type,
+                                  const void           *value,
+                                  const DBusTypeReader *realign_root)
+{
+  dbus_bool_t retval;
+  ReplacementBlock block;
+  DBusTypeWriter writer;
+
+  _dbus_assert (realign_root != NULL);
+
+  retval = FALSE;
+
+  if (!replacement_block_init (&block, reader))
+    return FALSE;
+
+  /* Write the new basic value */
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+  _dbus_verbose ("INITIALIZING writer %p to write basic value at value_pos %d of replacement string\n",
+                 &writer, _dbus_string_get_length (&block.replacement));
+#endif
+  _dbus_type_writer_init_values_only (&writer,
+                                      reader->byte_order,
+                                      reader->type_str,
+                                      reader->type_pos,
+                                      &block.replacement,
+                                      _dbus_string_get_length (&block.replacement));
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+  _dbus_verbose ("WRITING basic value to writer %p (replacement string)\n", &writer);
+#endif
+  if (!_dbus_type_writer_write_basic (&writer, current_type, value))
+    goto out;
+
+  if (!replacement_block_replace (&block,
+                                  reader,
+                                  realign_root))
+    goto out;
+
+  retval = TRUE;
+
+ out:
+  replacement_block_free (&block);
+  return retval;
+}
+
+static void
+reader_set_basic_fixed_length (DBusTypeReader *reader,
+                               int             current_type,
+                               const void     *value)
+{
+  _dbus_marshal_set_basic ((DBusString*) reader->value_str,
+                           reader->value_pos,
+                           current_type,
+                           value,
+                           reader->byte_order,
+                           NULL, NULL);
+}
+
+/**
+ * Sets a new value for the basic type value pointed to by the reader,
+ * leaving the reader valid to continue reading. Any other readers
+ * will be invalidated if you set a variable-length type such as a
+ * string.
+ *
+ * The provided realign_root is the reader to start from when
+ * realigning the data that follows the newly-set value. The reader
+ * parameter must point to a value below the realign_root parameter.
+ * If the type being set is fixed-length, then realign_root may be
+ * #NULL. Only values reachable from realign_root will be realigned,
+ * so if your string contains other values you will need to deal with
+ * those somehow yourself. It is OK if realign_root is the same
+ * reader as the reader parameter, though if you aren't setting the
+ * root it may not be such a good idea.
+ *
+ * @todo DBusTypeReader currently takes "const" versions of the type
+ * and value strings, and this function modifies those strings by
+ * casting away the const, which is of course bad if we want to get
+ * picky. (To be truly clean you'd have an object which contained the
+ * type and value strings and set_basic would be a method on that
+ * object... this would also make DBusTypeReader the same thing as
+ * DBusTypeMark. But since DBusMessage is effectively that object for
+ * D-Bus it doesn't seem worth creating some random object.)
+ *
+ * @todo optimize this by only rewriting until the old and new values
+ * are at the same alignment. Frequently this should result in only
+ * replacing the value that's immediately at hand.
+ *
+ * @param reader reader indicating where to set a new value
+ * @param value address of the value to set
+ * @param realign_root realign from here
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_type_reader_set_basic (DBusTypeReader       *reader,
+                             const void           *value,
+                             const DBusTypeReader *realign_root)
+{
+  int current_type;
+
+  _dbus_assert (!reader->klass->types_only);
+  _dbus_assert (reader->value_str == realign_root->value_str);
+  _dbus_assert (reader->value_pos >= realign_root->value_pos);
+
+  current_type = _dbus_type_reader_get_current_type (reader);
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+  _dbus_verbose ("  SET BASIC type reader %p type_pos = %d value_pos = %d remaining sig '%s' realign_root = %p with value_pos %d current_type = %s\n",
+                 reader, reader->type_pos, reader->value_pos,
+                 _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0),
+                 realign_root,
+                 realign_root ? realign_root->value_pos : -1,
+                 _dbus_type_to_string (current_type));
+  _dbus_verbose_bytes_of_string (realign_root->value_str, realign_root->value_pos,
+                                 _dbus_string_get_length (realign_root->value_str) -
+                                 realign_root->value_pos);
+#endif
+
+  _dbus_assert (dbus_type_is_basic (current_type));
+
+  if (dbus_type_is_fixed (current_type))
+    {
+      reader_set_basic_fixed_length (reader, current_type, value);
+      return TRUE;
+    }
+  else
+    {
+      _dbus_assert (realign_root != NULL);
+      return reader_set_basic_variable_length (reader, current_type,
+                                               value, realign_root);
+    }
+}
+
+/**
+ * Recursively deletes any value pointed to by the reader, leaving the
+ * reader valid to continue reading. Any other readers will be
+ * invalidated.
+ *
+ * The provided realign_root is the reader to start from when
+ * realigning the data that follows the newly-set value.
+ * See _dbus_type_reader_set_basic() for more details on the
+ * realign_root paramter.
+ *
+ * @todo for now this does not delete the typecodes associated with
+ * the value, so this function should only be used for array elements.
+ *
+ * @param reader reader indicating where to delete a value
+ * @param realign_root realign from here
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_type_reader_delete (DBusTypeReader        *reader,
+                          const DBusTypeReader  *realign_root)
+{
+  dbus_bool_t retval;
+  ReplacementBlock block;
+
+  _dbus_assert (realign_root != NULL);
+  _dbus_assert (reader->klass == &array_reader_class);
+
+  retval = FALSE;
+
+  if (!replacement_block_init (&block, reader))
+    return FALSE;
+
+  if (!replacement_block_replace (&block,
+                                  reader,
+                                  realign_root))
+    goto out;
+
+  retval = TRUE;
+
+ out:
+  replacement_block_free (&block);
+  return retval;
+}
+
+/**
+ * Compares two readers, which must be iterating over the same value data.
+ * Returns #TRUE if the first parameter is further along than the second parameter.
+ *
+ * @param lhs left-hand-side (first) parameter
+ * @param rhs left-hand-side (first) parameter
+ * @returns whether lhs is greater than rhs
+ */
+dbus_bool_t
+_dbus_type_reader_greater_than (const DBusTypeReader  *lhs,
+                                const DBusTypeReader  *rhs)
+{
+  _dbus_assert (lhs->value_str == rhs->value_str);
+
+  return lhs->value_pos > rhs->value_pos;
+}
+
+/*
+ *
+ *
+ *         DBusTypeWriter
+ *
+ *
+ *
+ */
+
+/**
+ * Initialize a write iterator, which is used to write out values in
+ * serialized D-Bus format.
+ *
+ * The type_pos passed in is expected to be inside an already-valid,
+ * though potentially empty, type signature. This means that the byte
+ * after type_pos must be either #DBUS_TYPE_INVALID (aka nul) or some
+ * other valid type. #DBusTypeWriter won't enforce that the signature
+ * is already valid (you can append the nul byte at the end if you
+ * like), but just be aware that you need the nul byte eventually and
+ * #DBusTypeWriter isn't going to write it for you.
+ *
+ * @param writer the writer to init
+ * @param byte_order the byte order to marshal into
+ * @param type_str the string to write typecodes into
+ * @param type_pos where to insert typecodes
+ * @param value_str the string to write values into
+ * @param value_pos where to insert values
+ *
+ */
+void
+_dbus_type_writer_init (DBusTypeWriter *writer,
+                        int             byte_order,
+                        DBusString     *type_str,
+                        int             type_pos,
+                        DBusString     *value_str,
+                        int             value_pos)
+{
+  writer->byte_order = byte_order;
+  writer->type_str = type_str;
+  writer->type_pos = type_pos;
+  writer->value_str = value_str;
+  writer->value_pos = value_pos;
+  writer->container_type = DBUS_TYPE_INVALID;
+  writer->type_pos_is_expectation = FALSE;
+  writer->enabled = TRUE;
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+  _dbus_verbose ("writer %p init remaining sig '%s'\n", writer,
+                 writer->type_str ?
+                 _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0) :
+                 "unknown");
+#endif
+}
+
+/**
+ * Initialize a write iterator, with the signature to be provided
+ * later.
+ *
+ * @param writer the writer to init
+ * @param byte_order the byte order to marshal into
+ * @param value_str the string to write values into
+ * @param value_pos where to insert values
+ *
+ */
+void
+_dbus_type_writer_init_types_delayed (DBusTypeWriter *writer,
+                                      int             byte_order,
+                                      DBusString     *value_str,
+                                      int             value_pos)
+{
+  _dbus_type_writer_init (writer, byte_order,
+                          NULL, 0, value_str, value_pos);
+}
+
+/**
+ * Adds type string to the writer, if it had none.
+ *
+ * @param writer the writer to init
+ * @param type_str type string to add
+ * @param type_pos type position
+ *
+ */
+void
+_dbus_type_writer_add_types (DBusTypeWriter *writer,
+                             DBusString     *type_str,
+                             int             type_pos)
+{
+  if (writer->type_str == NULL) /* keeps us from using this as setter */
+    {
+      writer->type_str = type_str;
+      writer->type_pos = type_pos;
+    }
+}
+
+/**
+ * Removes type string from the writer.
+ *
+ * @param writer the writer to remove from
+ */
+void
+_dbus_type_writer_remove_types (DBusTypeWriter *writer)
+{
+  writer->type_str = NULL;
+  writer->type_pos = -1;
+}
+
+/**
+ * Like _dbus_type_writer_init(), except the type string
+ * passed in should correspond to an existing signature that
+ * matches what you're going to write out. The writer will
+ * check what you write vs. this existing signature.
+ *
+ * @param writer the writer to init
+ * @param byte_order the byte order to marshal into
+ * @param type_str the string with signature
+ * @param type_pos start of signature
+ * @param value_str the string to write values into
+ * @param value_pos where to insert values
+ *
+ */
+void
+_dbus_type_writer_init_values_only (DBusTypeWriter   *writer,
+                                    int               byte_order,
+                                    const DBusString *type_str,
+                                    int               type_pos,
+                                    DBusString       *value_str,
+                                    int               value_pos)
+{
+  _dbus_type_writer_init (writer, byte_order,
+                          (DBusString*)type_str, type_pos,
+                          value_str, value_pos);
+
+  writer->type_pos_is_expectation = TRUE;
+}
+
+static dbus_bool_t
+_dbus_type_writer_write_basic_no_typecode (DBusTypeWriter *writer,
+                                           int             type,
+                                           const void     *value)
+{
+  if (writer->enabled)
+    return _dbus_marshal_write_basic (writer->value_str,
+                                      writer->value_pos,
+                                      type,
+                                      value,
+                                      writer->byte_order,
+                                      &writer->value_pos);
+  else
+    return TRUE;
+}
+
+/* If our parent is an array, things are a little bit complicated.
+ *
+ * The parent must have a complete element type, such as
+ * "i" or "aai" or "(ii)" or "a(ii)". There can't be
+ * unclosed parens, or an "a" with no following type.
+ *
+ * To recurse, the only allowed operation is to recurse into the
+ * first type in the element type. So for "i" you can't recurse, for
+ * "ai" you can recurse into the array, for "(ii)" you can recurse
+ * into the struct.
+ *
+ * If you recurse into the array for "ai", then you must specify
+ * "i" for the element type of the array you recurse into.
+ *
+ * While inside an array at any level, we need to avoid writing to
+ * type_str, since the type only appears once for the whole array,
+ * it does not appear for each array element.
+ *
+ * While inside an array type_pos points to the expected next
+ * typecode, rather than the next place we could write a typecode.
+ */
+static void
+writer_recurse_init_and_check (DBusTypeWriter *writer,
+                               int             container_type,
+                               DBusTypeWriter *sub)
+{
+  _dbus_type_writer_init (sub,
+                          writer->byte_order,
+                          writer->type_str,
+                          writer->type_pos,
+                          writer->value_str,
+                          writer->value_pos);
+
+  sub->container_type = container_type;
+
+  if (writer->type_pos_is_expectation ||
+      (sub->container_type == DBUS_TYPE_ARRAY || sub->container_type == DBUS_TYPE_VARIANT))
+    sub->type_pos_is_expectation = TRUE;
+  else
+    sub->type_pos_is_expectation = FALSE;
+
+  sub->enabled = writer->enabled;
+
+#ifndef DBUS_DISABLE_CHECKS
+  if (writer->type_pos_is_expectation && writer->type_str)
+    {
+      int expected;
+
+      expected = _dbus_first_type_in_signature (writer->type_str, writer->type_pos);
+
+      if (expected != sub->container_type)
+        {
+          if (expected != DBUS_TYPE_INVALID)
+            _dbus_warn_check_failed ("Writing an element of type %s, but the expected type here is %s\n"
+                                     "The overall signature expected here was '%s' and we are on byte %d of that signature.\n",
+                                     _dbus_type_to_string (sub->container_type),
+                                     _dbus_type_to_string (expected),
+                                     _dbus_string_get_const_data (writer->type_str), writer->type_pos);
+          else
+            _dbus_warn_check_failed ("Writing an element of type %s, but no value is expected here\n"
+                                     "The overall signature expected here was '%s' and we are on byte %d of that signature.\n",
+                                     _dbus_type_to_string (sub->container_type),
+                                     _dbus_string_get_const_data (writer->type_str), writer->type_pos);
+          
+          _dbus_assert_not_reached ("bad array element or variant content written");
+        }
+    }
+#endif /* DBUS_DISABLE_CHECKS */
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+  _dbus_verbose ("  type writer %p recurse parent %s type_pos = %d value_pos = %d is_expectation = %d remaining sig '%s' enabled = %d\n",
+                 writer,
+                 _dbus_type_to_string (writer->container_type),
+                 writer->type_pos, writer->value_pos, writer->type_pos_is_expectation,
+                 writer->type_str ?
+                 _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0) :
+                 "unknown",
+                 writer->enabled);
+  _dbus_verbose ("  type writer %p recurse sub %s   type_pos = %d value_pos = %d is_expectation = %d enabled = %d\n",
+                 sub,
+                 _dbus_type_to_string (sub->container_type),
+                 sub->type_pos, sub->value_pos,
+                 sub->type_pos_is_expectation,
+                 sub->enabled);
+#endif
+}
+
+static dbus_bool_t
+write_or_verify_typecode (DBusTypeWriter *writer,
+                          int             typecode)
+{
+  /* A subwriter inside an array or variant will have type_pos
+   * pointing to the expected typecode; a writer not inside an array
+   * or variant has type_pos pointing to the next place to insert a
+   * typecode.
+   */
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+  _dbus_verbose ("  type writer %p write_or_verify start type_pos = %d remaining sig '%s' enabled = %d\n",
+                 writer, writer->type_pos,
+                 writer->type_str ?
+                 _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0) :
+                 "unknown",
+                 writer->enabled);
+#endif
+
+  if (writer->type_str == NULL)
+    return TRUE;
+
+  if (writer->type_pos_is_expectation)
+    {
+#ifndef DBUS_DISABLE_CHECKS
+      {
+        int expected;
+
+        expected = _dbus_string_get_byte (writer->type_str, writer->type_pos);
+
+        if (expected != typecode)
+          {
+            if (expected != DBUS_TYPE_INVALID)
+              _dbus_warn_check_failed ("Array or variant type requires that type %s be written, but %s was written.\n"
+                                       "The overall signature expected here was '%s' and we are on byte %d of that signature.\n",
+                                       _dbus_type_to_string (expected), _dbus_type_to_string (typecode),
+                                       _dbus_string_get_const_data (writer->type_str), writer->type_pos);
+            else
+              _dbus_warn_check_failed ("Array or variant type wasn't expecting any more values to be written into it, but a value %s was written.\n"
+                                       "The overall signature expected here was '%s' and we are on byte %d of that signature.\n",
+                                       _dbus_type_to_string (typecode),
+                                       _dbus_string_get_const_data (writer->type_str), writer->type_pos);
+            _dbus_assert_not_reached ("bad type inserted somewhere inside an array or variant");
+          }
+      }
+#endif /* DBUS_DISABLE_CHECKS */
+
+      /* if immediately inside an array we'd always be appending an element,
+       * so the expected type doesn't change; if inside a struct or something
+       * below an array, we need to move through said struct or something.
+       */
+      if (writer->container_type != DBUS_TYPE_ARRAY)
+        writer->type_pos += 1;
+    }
+  else
+    {
+      if (!_dbus_string_insert_byte (writer->type_str,
+                                     writer->type_pos,
+                                     typecode))
+        return FALSE;
+
+      writer->type_pos += 1;
+    }
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+  _dbus_verbose ("  type writer %p write_or_verify end type_pos = %d remaining sig '%s'\n",
+                 writer, writer->type_pos,
+                 _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0));
+#endif
+
+  return TRUE;
+}
+
+static dbus_bool_t
+writer_recurse_struct_or_dict_entry (DBusTypeWriter   *writer,
+                                     int               begin_char,
+                                     const DBusString *contained_type,
+                                     int               contained_type_start,
+                                     int               contained_type_len,
+                                     DBusTypeWriter   *sub)
+{
+  /* FIXME right now contained_type is ignored; we could probably
+   * almost trivially fix the code so if it's present we
+   * write it out and then set type_pos_is_expectation
+   */
+
+  /* Ensure that we'll be able to add alignment padding and the typecode */
+  if (writer->enabled)
+    {
+      if (!_dbus_string_alloc_space (sub->value_str, 8))
+        return FALSE;
+    }
+
+  if (!write_or_verify_typecode (sub, begin_char))
+    _dbus_assert_not_reached ("failed to insert struct typecode after prealloc");
+
+  if (writer->enabled)
+    {
+      if (!_dbus_string_insert_bytes (sub->value_str,
+                                      sub->value_pos,
+                                      _DBUS_ALIGN_VALUE (sub->value_pos, 8) - sub->value_pos,
+                                      '\0'))
+        _dbus_assert_not_reached ("should not have failed to insert alignment padding for struct");
+      sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 8);
+    }
+
+  return TRUE;
+}
+
+
+static dbus_bool_t
+writer_recurse_array (DBusTypeWriter   *writer,
+                      const DBusString *contained_type,
+                      int               contained_type_start,
+                      int               contained_type_len,
+                      DBusTypeWriter   *sub,
+                      dbus_bool_t       is_array_append)
+{
+  dbus_uint32_t value = 0;
+  int alignment;
+  int aligned;
+
+#ifndef DBUS_DISABLE_CHECKS
+  if (writer->container_type == DBUS_TYPE_ARRAY &&
+      writer->type_str)
+    {
+      if (!_dbus_string_equal_substring (contained_type,
+                                         contained_type_start,
+                                         contained_type_len,
+                                         writer->type_str,
+                                         writer->u.array.element_type_pos + 1))
+        {
+          _dbus_warn_check_failed ("Writing an array of '%s' but this is incompatible with the expected type of elements in the parent array\n",
+                                   _dbus_string_get_const_data_len (contained_type,
+                                                                    contained_type_start,
+                                                                    contained_type_len));
+          _dbus_assert_not_reached ("incompatible type for child array");
+        }
+    }
+#endif /* DBUS_DISABLE_CHECKS */
+
+  if (writer->enabled && !is_array_append)
+    {
+      /* 3 pad + 4 bytes for the array length, and 4 bytes possible padding
+       * before array values
+       */
+      if (!_dbus_string_alloc_space (sub->value_str, 3 + 4 + 4))
+        return FALSE;
+    }
+
+  if (writer->type_str != NULL)
+    {
+      sub->type_pos += 1; /* move to point to the element type, since type_pos
+                           * should be the expected type for further writes
+                           */
+      sub->u.array.element_type_pos = sub->type_pos;
+    }
+
+  if (!writer->type_pos_is_expectation)
+    {
+      /* sub is a toplevel/outermost array so we need to write the type data */
+
+      /* alloc space for array typecode, element signature */
+      if (!_dbus_string_alloc_space (writer->type_str, 1 + contained_type_len))
+        return FALSE;
+
+      if (!_dbus_string_insert_byte (writer->type_str,
+                                     writer->type_pos,
+                                     DBUS_TYPE_ARRAY))
+        _dbus_assert_not_reached ("failed to insert array typecode after prealloc");
+
+      if (!_dbus_string_copy_len (contained_type,
+                                  contained_type_start, contained_type_len,
+                                  sub->type_str,
+                                  sub->u.array.element_type_pos))
+        _dbus_assert_not_reached ("should not have failed to insert array element typecodes");
+    }
+
+  if (writer->type_str != NULL)
+    {
+      /* If the parent is an array, we hold type_pos pointing at the array element type;
+       * otherwise advance it to reflect the array value we just recursed into
+       */
+      if (writer->container_type != DBUS_TYPE_ARRAY)
+        writer->type_pos += 1 + contained_type_len;
+      else
+        _dbus_assert (writer->type_pos_is_expectation); /* because it's an array */
+    }
+
+  if (writer->enabled)
+    {
+      /* Write (or jump over, if is_array_append) the length */
+      sub->u.array.len_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 4);
+
+      if (is_array_append)
+        {
+          sub->value_pos += 4;
+        }
+      else
+        {
+          if (!_dbus_type_writer_write_basic_no_typecode (sub, DBUS_TYPE_UINT32,
+                                                          &value))
+            _dbus_assert_not_reached ("should not have failed to insert array len");
+        }
+
+      _dbus_assert (sub->u.array.len_pos == sub->value_pos - 4);
+
+      /* Write alignment padding for array elements
+       * Note that we write the padding *even for empty arrays*
+       * to avoid wonky special cases
+       */
+      alignment = element_type_get_alignment (contained_type, contained_type_start);
+
+      aligned = _DBUS_ALIGN_VALUE (sub->value_pos, alignment);
+      if (aligned != sub->value_pos)
+        {
+          if (!is_array_append)
+            {
+              if (!_dbus_string_insert_bytes (sub->value_str,
+                                              sub->value_pos,
+                                              aligned - sub->value_pos,
+                                              '\0'))
+                _dbus_assert_not_reached ("should not have failed to insert alignment padding");
+            }
+
+          sub->value_pos = aligned;
+        }
+
+      sub->u.array.start_pos = sub->value_pos;
+
+      if (is_array_append)
+        {
+          dbus_uint32_t len;
+
+          _dbus_assert (_DBUS_ALIGN_VALUE (sub->u.array.len_pos, 4) ==
+                        (unsigned) sub->u.array.len_pos);
+          len = _dbus_unpack_uint32 (sub->byte_order,
+                                     _dbus_string_get_const_data_len (sub->value_str,
+                                                                      sub->u.array.len_pos,
+                                                                      4));
+
+          sub->value_pos += len;
+        }
+    }
+  else
+    {
+      /* not enabled, so we won't write the len_pos; set it to -1 to so indicate */
+      sub->u.array.len_pos = -1;
+      sub->u.array.start_pos = sub->value_pos;
+    }
+
+  _dbus_assert (sub->u.array.len_pos < sub->u.array.start_pos);
+  _dbus_assert (is_array_append || sub->u.array.start_pos == sub->value_pos);
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+      _dbus_verbose ("  type writer %p recurse array done remaining sig '%s' array start_pos = %d len_pos = %d value_pos = %d\n", sub,
+                     sub->type_str ?
+                     _dbus_string_get_const_data_len (sub->type_str, sub->type_pos, 0) :
+                     "unknown",
+                     sub->u.array.start_pos, sub->u.array.len_pos, sub->value_pos);
+#endif
+
+  return TRUE;
+}
+
+/* Variant value will normally have:
+ *   1 byte signature length not including nul
+ *   signature typecodes (nul terminated)
+ *   padding to alignment of contained type
+ *   body according to signature
+ *
+ * The signature string can only have a single type
+ * in it but that type may be complex/recursive.
+ *
+ * So a typical variant type with the integer 3 will have these
+ * octets:
+ *   0x1 'i' '\0' [1 byte padding to alignment boundary] 0x0 0x0 0x0 0x3
+ *
+ * The main world of hurt for writing out a variant is that the type
+ * string is the same string as the value string. Which means
+ * inserting to the type string will move the value_pos; and it means
+ * that inserting to the type string could break type alignment.
+ */
+static dbus_bool_t
+writer_recurse_variant (DBusTypeWriter   *writer,
+                        const DBusString *contained_type,
+                        int               contained_type_start,
+                        int               contained_type_len,
+                        DBusTypeWriter   *sub)
+{
+  int contained_alignment;
+  
+  if (writer->enabled)
+    {
+      /* Allocate space for the worst case, which is 1 byte sig
+       * length, nul byte at end of sig, and 7 bytes padding to
+       * 8-boundary.
+       */
+      if (!_dbus_string_alloc_space (sub->value_str, contained_type_len + 9))
+        return FALSE;
+    }
+
+  /* write VARIANT typecode to the parent's type string */
+  if (!write_or_verify_typecode (writer, DBUS_TYPE_VARIANT))
+    return FALSE;
+
+  /* If not enabled, mark that we have no type_str anymore ... */
+
+  if (!writer->enabled)
+    {
+      sub->type_str = NULL;
+      sub->type_pos = -1;
+
+      return TRUE;
+    }
+
+  /* If we're enabled then continue ... */
+
+  if (!_dbus_string_insert_byte (sub->value_str,
+                                 sub->value_pos,
+                                 contained_type_len))
+    _dbus_assert_not_reached ("should not have failed to insert variant type sig len");
+
+  sub->value_pos += 1;
+
+  /* Here we switch over to the expected type sig we're about to write */
+  sub->type_str = sub->value_str;
+  sub->type_pos = sub->value_pos;
+
+  if (!_dbus_string_copy_len (contained_type, contained_type_start, contained_type_len,
+                              sub->value_str, sub->value_pos))
+    _dbus_assert_not_reached ("should not have failed to insert variant type sig");
+
+  sub->value_pos += contained_type_len;
+
+  if (!_dbus_string_insert_byte (sub->value_str,
+                                 sub->value_pos,
+                                 DBUS_TYPE_INVALID))
+    _dbus_assert_not_reached ("should not have failed to insert variant type nul termination");
+
+  sub->value_pos += 1;
+
+  contained_alignment = _dbus_type_get_alignment (_dbus_first_type_in_signature (contained_type, contained_type_start));
+  
+  if (!_dbus_string_insert_bytes (sub->value_str,
+                                  sub->value_pos,
+                                  _DBUS_ALIGN_VALUE (sub->value_pos, contained_alignment) - sub->value_pos,
+                                  '\0'))
+    _dbus_assert_not_reached ("should not have failed to insert alignment padding for variant body");
+  sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, contained_alignment);
+
+  return TRUE;
+}
+
+static dbus_bool_t
+_dbus_type_writer_recurse_contained_len (DBusTypeWriter   *writer,
+                                         int               container_type,
+                                         const DBusString *contained_type,
+                                         int               contained_type_start,
+                                         int               contained_type_len,
+                                         DBusTypeWriter   *sub,
+                                         dbus_bool_t       is_array_append)
+{
+  writer_recurse_init_and_check (writer, container_type, sub);
+
+  switch (container_type)
+    {
+    case DBUS_TYPE_STRUCT:
+      return writer_recurse_struct_or_dict_entry (writer,
+                                                  DBUS_STRUCT_BEGIN_CHAR,
+                                                  contained_type,
+                                                  contained_type_start, contained_type_len,
+                                                  sub);
+      break;
+    case DBUS_TYPE_DICT_ENTRY:
+      return writer_recurse_struct_or_dict_entry (writer,
+                                                  DBUS_DICT_ENTRY_BEGIN_CHAR,
+                                                  contained_type,
+                                                  contained_type_start, contained_type_len,
+                                                  sub);
+      break;
+    case DBUS_TYPE_ARRAY:
+      return writer_recurse_array (writer,
+                                   contained_type, contained_type_start, contained_type_len,
+                                   sub, is_array_append);
+      break;
+    case DBUS_TYPE_VARIANT:
+      return writer_recurse_variant (writer,
+                                     contained_type, contained_type_start, contained_type_len,
+                                     sub);
+      break;
+    default:
+      _dbus_assert_not_reached ("tried to recurse into type that doesn't support that");
+      return FALSE;
+      break;
+    }
+}
+
+/**
+ * Opens a new container and writes out the initial information for that container.
+ *
+ * @param writer the writer
+ * @param container_type the type of the container to open
+ * @param contained_type the array element type or variant content type
+ * @param contained_type_start position to look for the type
+ * @param sub the new sub-writer to write container contents
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_type_writer_recurse (DBusTypeWriter   *writer,
+                           int               container_type,
+                           const DBusString *contained_type,
+                           int               contained_type_start,
+                           DBusTypeWriter   *sub)
+{
+  int contained_type_len;
+
+  if (contained_type)
+    contained_type_len = find_len_of_complete_type (contained_type, contained_type_start);
+  else
+    contained_type_len = 0;
+
+  return _dbus_type_writer_recurse_contained_len (writer, container_type,
+                                                  contained_type,
+                                                  contained_type_start,
+                                                  contained_type_len,
+                                                  sub,
+                                                  FALSE);
+}
+
+/**
+ * Append to an existing array. Essentially, the writer will read an
+ * existing length at the write location; jump over that length; and
+ * write new fields. On unrecurse(), the existing length will be
+ * updated.
+ *
+ * @param writer the writer
+ * @param contained_type element type
+ * @param contained_type_start position of element type
+ * @param sub the subwriter to init
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_type_writer_append_array (DBusTypeWriter   *writer,
+                                const DBusString *contained_type,
+                                int               contained_type_start,
+                                DBusTypeWriter   *sub)
+{
+  int contained_type_len;
+
+  if (contained_type)
+    contained_type_len = find_len_of_complete_type (contained_type, contained_type_start);
+  else
+    contained_type_len = 0;
+
+  return _dbus_type_writer_recurse_contained_len (writer, DBUS_TYPE_ARRAY,
+                                                  contained_type,
+                                                  contained_type_start,
+                                                  contained_type_len,
+                                                  sub,
+                                                  TRUE);
+}
+
+static int
+writer_get_array_len (DBusTypeWriter *writer)
+{
+  _dbus_assert (writer->container_type == DBUS_TYPE_ARRAY);
+  return writer->value_pos - writer->u.array.start_pos;
+}
+
+/**
+ * Closes a container created by _dbus_type_writer_recurse()
+ * and writes any additional information to the values block.
+ *
+ * @param writer the writer
+ * @param sub the sub-writer created by _dbus_type_writer_recurse()
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_type_writer_unrecurse (DBusTypeWriter *writer,
+                             DBusTypeWriter *sub)
+{
+  /* type_pos_is_expectation never gets unset once set, or we'd get all hosed */
+  _dbus_assert (!writer->type_pos_is_expectation ||
+                (writer->type_pos_is_expectation && sub->type_pos_is_expectation));
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+  _dbus_verbose ("  type writer %p unrecurse type_pos = %d value_pos = %d is_expectation = %d container_type = %s\n",
+                 writer, writer->type_pos, writer->value_pos, writer->type_pos_is_expectation,
+                 _dbus_type_to_string (writer->container_type));
+  _dbus_verbose ("  type writer %p unrecurse sub type_pos = %d value_pos = %d is_expectation = %d container_type = %s\n",
+                 sub, sub->type_pos, sub->value_pos,
+                 sub->type_pos_is_expectation,
+                 _dbus_type_to_string (sub->container_type));
+#endif
+
+  if (sub->container_type == DBUS_TYPE_STRUCT)
+    {
+      if (!write_or_verify_typecode (sub, DBUS_STRUCT_END_CHAR))
+        return FALSE;
+    }
+  else if (sub->container_type == DBUS_TYPE_DICT_ENTRY)
+    {
+      if (!write_or_verify_typecode (sub, DBUS_DICT_ENTRY_END_CHAR))
+        return FALSE;
+    }
+  else if (sub->container_type == DBUS_TYPE_ARRAY)
+    {
+      if (sub->u.array.len_pos >= 0) /* len_pos == -1 if we weren't enabled when we passed it */
+        {
+          dbus_uint32_t len;
+
+          /* Set the array length */
+          len = writer_get_array_len (sub);
+          _dbus_marshal_set_uint32 (sub->value_str,
+                                    sub->u.array.len_pos,
+                                    len,
+                                    sub->byte_order);
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+          _dbus_verbose ("    filled in sub array len to %u at len_pos %d\n",
+                         len, sub->u.array.len_pos);
+#endif
+        }
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+      else
+        {
+          _dbus_verbose ("    not filling in sub array len because we were disabled when we passed the len\n");
+        }
+#endif
+    }
+
+  /* Now get type_pos right for the parent writer. Here are the cases:
+   *
+   * Cases !writer->type_pos_is_expectation:
+   *   (in these cases we want to update to the new insertion point)
+   *
+   * - if we recursed into a STRUCT then we didn't know in advance
+   *   what the types in the struct would be; so we have to fill in
+   *   that information now.
+   *       writer->type_pos = sub->type_pos
+   *
+   * - if we recursed into anything else, we knew the full array
+   *   type, or knew the single typecode marking VARIANT, so
+   *   writer->type_pos is already correct.
+   *       writer->type_pos should remain as-is
+   *
+   * - note that the parent is never an ARRAY or VARIANT, if it were
+   *   then type_pos_is_expectation would be TRUE. The parent
+   *   is thus known to be a toplevel or STRUCT.
+   *
+   * Cases where writer->type_pos_is_expectation:
+   *   (in these cases we want to update to next expected type to write)
+   *
+   * - we recursed from STRUCT into STRUCT and we didn't increment
+   *   type_pos in the parent just to stay consistent with the
+   *   !writer->type_pos_is_expectation case (though we could
+   *   special-case this in recurse_struct instead if we wanted)
+   *       writer->type_pos = sub->type_pos
+   *
+   * - we recursed from STRUCT into ARRAY or VARIANT and type_pos
+   *   for parent should have been incremented already
+   *       writer->type_pos should remain as-is
+   *
+   * - we recursed from ARRAY into a sub-element, so type_pos in the
+   *   parent is the element type and should remain the element type
+   *   for the benefit of the next child element
+   *       writer->type_pos should remain as-is
+   *
+   * - we recursed from VARIANT into its value, so type_pos in the
+   *   parent makes no difference since there's only one value
+   *   and we just finished writing it and won't use type_pos again
+   *       writer->type_pos should remain as-is
+   *
+   *
+   * For all these, DICT_ENTRY is the same as STRUCT
+   */
+  if (writer->type_str != NULL)
+    {
+      if ((sub->container_type == DBUS_TYPE_STRUCT ||
+           sub->container_type == DBUS_TYPE_DICT_ENTRY) &&
+          (writer->container_type == DBUS_TYPE_STRUCT ||
+           writer->container_type == DBUS_TYPE_DICT_ENTRY ||
+           writer->container_type == DBUS_TYPE_INVALID))
+        {
+          /* Advance the parent to the next struct field */
+          writer->type_pos = sub->type_pos;
+        }
+    }
+
+  writer->value_pos = sub->value_pos;
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+  _dbus_verbose ("  type writer %p unrecursed type_pos = %d value_pos = %d remaining sig '%s'\n",
+                 writer, writer->type_pos, writer->value_pos,
+                 writer->type_str ?
+                 _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0) :
+                 "unknown");
+#endif
+
+  return TRUE;
+}
+
+/**
+ * Writes out a basic type.
+ *
+ * @param writer the writer
+ * @param type the type to write
+ * @param value the address of the value to write
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_type_writer_write_basic (DBusTypeWriter *writer,
+                               int             type,
+                               const void     *value)
+{
+  dbus_bool_t retval;
+
+  /* First ensure that our type realloc will succeed */
+  if (!writer->type_pos_is_expectation && writer->type_str != NULL)
+    {
+      if (!_dbus_string_alloc_space (writer->type_str, 1))
+        return FALSE;
+    }
+
+  retval = FALSE;
+
+  if (!_dbus_type_writer_write_basic_no_typecode (writer, type, value))
+    goto out;
+
+  if (!write_or_verify_typecode (writer, type))
+    _dbus_assert_not_reached ("failed to write typecode after prealloc");
+
+  retval = TRUE;
+
+ out:
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+  _dbus_verbose ("  type writer %p basic type_pos = %d value_pos = %d is_expectation = %d enabled = %d\n",
+                 writer, writer->type_pos, writer->value_pos, writer->type_pos_is_expectation,
+                 writer->enabled);
+#endif
+
+  return retval;
+}
+
+/**
+ * Writes a block of fixed-length basic values, i.e. those that are
+ * both dbus_type_is_fixed() and _dbus_type_is_basic(). The block
+ * must be written inside an array.
+ *
+ * The value parameter should be the address of said array of values,
+ * so e.g. if it's an array of double, pass in "const double**"
+ *
+ * @param writer the writer
+ * @param element_type type of stuff in the array
+ * @param value address of the array
+ * @param n_elements number of elements in the array
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_type_writer_write_fixed_multi (DBusTypeWriter        *writer,
+                                     int                    element_type,
+                                     const void            *value,
+                                     int                    n_elements)
+{
+  _dbus_assert (writer->container_type == DBUS_TYPE_ARRAY);
+  _dbus_assert (dbus_type_is_fixed (element_type));
+  _dbus_assert (writer->type_pos_is_expectation);
+  _dbus_assert (n_elements >= 0);
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+  _dbus_verbose ("  type writer %p entering fixed multi type_pos = %d value_pos = %d n_elements %d\n",
+                 writer, writer->type_pos, writer->value_pos, n_elements);
+#endif
+
+  if (!write_or_verify_typecode (writer, element_type))
+    _dbus_assert_not_reached ("OOM should not happen if only verifying typecode");
+
+  if (writer->enabled)
+    {
+      if (!_dbus_marshal_write_fixed_multi (writer->value_str,
+                                            writer->value_pos,
+                                            element_type,
+                                            value,
+                                            n_elements,
+                                            writer->byte_order,
+                                            &writer->value_pos))
+        return FALSE;
+    }
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+  _dbus_verbose ("  type writer %p fixed multi written new type_pos = %d new value_pos = %d n_elements %d\n",
+                 writer, writer->type_pos, writer->value_pos, n_elements);
+#endif
+
+  return TRUE;
+}
+
+static void
+enable_if_after (DBusTypeWriter       *writer,
+                 DBusTypeReader       *reader,
+                 const DBusTypeReader *start_after)
+{
+  if (start_after)
+    {
+      if (!writer->enabled && _dbus_type_reader_greater_than (reader, start_after))
+        {
+          _dbus_type_writer_set_enabled (writer, TRUE);
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+          _dbus_verbose ("ENABLING writer %p at %d because reader at value_pos %d is after reader at value_pos %d\n",
+                         writer, writer->value_pos, reader->value_pos, start_after->value_pos);
+#endif
+        }
+
+      _dbus_assert ((!writer->enabled && !_dbus_type_reader_greater_than (reader, start_after)) ||
+                    (writer->enabled && _dbus_type_reader_greater_than (reader, start_after)));
+    }
+}
+
+static dbus_bool_t
+append_fixup (DBusList               **fixups,
+              const DBusArrayLenFixup *fixup)
+{
+  DBusArrayLenFixup *f;
+
+  f = dbus_new (DBusArrayLenFixup, 1);
+  if (f == NULL)
+    return FALSE;
+
+  *f = *fixup;
+
+  if (!_dbus_list_append (fixups, f))
+    {
+      dbus_free (f);
+      return FALSE;
+    }
+
+  _dbus_assert (f->len_pos_in_reader == fixup->len_pos_in_reader);
+  _dbus_assert (f->new_len == fixup->new_len);
+
+  return TRUE;
+}
+
+/* This loop is trivial if you ignore all the start_after nonsense,
+ * so if you're trying to figure it out, start by ignoring that
+ */
+static dbus_bool_t
+writer_write_reader_helper (DBusTypeWriter       *writer,
+                            DBusTypeReader       *reader,
+                            const DBusTypeReader *start_after,
+                            int                   start_after_new_pos,
+                            int                   start_after_new_len,
+                            DBusList            **fixups,
+                            dbus_bool_t           inside_start_after)
+{
+  int current_type;
+
+  while ((current_type = _dbus_type_reader_get_current_type (reader)) != DBUS_TYPE_INVALID)
+    {
+      if (dbus_type_is_container (current_type))
+        {
+          DBusTypeReader subreader;
+          DBusTypeWriter subwriter;
+          const DBusString *sig_str;
+          int sig_start;
+          int sig_len;
+          dbus_bool_t enabled_at_recurse;
+          dbus_bool_t past_start_after;
+          int reader_array_len_pos;
+          int reader_array_start_pos;
+          dbus_bool_t this_is_start_after;
+
+          /* type_pos is checked since e.g. in a struct the struct
+           * and its first field have the same value_pos.
+           * type_str will differ in reader/start_after for variants
+           * where type_str is inside the value_str
+           */
+          if (!inside_start_after && start_after &&
+              reader->value_pos == start_after->value_pos &&
+              reader->type_str == start_after->type_str &&
+              reader->type_pos == start_after->type_pos)
+            this_is_start_after = TRUE;
+          else
+            this_is_start_after = FALSE;
+
+          _dbus_type_reader_recurse (reader, &subreader);
+
+          if (current_type == DBUS_TYPE_ARRAY)
+            {
+              reader_array_len_pos = ARRAY_READER_LEN_POS (&subreader);
+              reader_array_start_pos = subreader.u.array.start_pos;
+            }
+          else
+            {
+              /* quiet gcc */
+              reader_array_len_pos = -1;
+              reader_array_start_pos = -1;
+            }
+
+          _dbus_type_reader_get_signature (&subreader, &sig_str,
+                                           &sig_start, &sig_len);
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+          _dbus_verbose ("about to recurse into %s reader at %d subreader at %d writer at %d start_after reader at %d write target len %d inside_start_after = %d this_is_start_after = %d\n",
+                         _dbus_type_to_string (current_type),
+                         reader->value_pos,
+                         subreader.value_pos,
+                         writer->value_pos,
+                         start_after ? start_after->value_pos : -1,
+                         _dbus_string_get_length (writer->value_str),
+                         inside_start_after, this_is_start_after);
+#endif
+
+          if (!inside_start_after && !this_is_start_after)
+            enable_if_after (writer, &subreader, start_after);
+          enabled_at_recurse = writer->enabled;
+          if (!_dbus_type_writer_recurse_contained_len (writer, current_type,
+                                                        sig_str, sig_start, sig_len,
+                                                        &subwriter, FALSE))
+            goto oom;
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+          _dbus_verbose ("recursed into subwriter at %d write target len %d\n",
+                         subwriter.value_pos,
+                         _dbus_string_get_length (subwriter.value_str));
+#endif
+
+          if (!writer_write_reader_helper (&subwriter, &subreader, start_after,
+                                           start_after_new_pos, start_after_new_len,
+                                           fixups,
+                                           inside_start_after ||
+                                           this_is_start_after))
+            goto oom;
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+          _dbus_verbose ("about to unrecurse from %s subreader at %d writer at %d subwriter at %d  write target len %d\n",
+                         _dbus_type_to_string (current_type),
+                         subreader.value_pos,
+                         writer->value_pos,
+                         subwriter.value_pos,
+                         _dbus_string_get_length (writer->value_str));
+#endif
+
+          if (!inside_start_after && !this_is_start_after)
+            enable_if_after (writer, &subreader, start_after);
+          past_start_after = writer->enabled;
+          if (!_dbus_type_writer_unrecurse (writer, &subwriter))
+            goto oom;
+
+          /* If we weren't enabled when we recursed, we didn't
+           * write an array len; if we passed start_after
+           * somewhere inside the array, then we need to generate
+           * a fixup.
+           */
+          if (start_after != NULL &&
+              !enabled_at_recurse && past_start_after &&
+              current_type == DBUS_TYPE_ARRAY &&
+              fixups != NULL)
+            {
+              DBusArrayLenFixup fixup;
+              int bytes_written_after_start_after;
+              int bytes_before_start_after;
+              int old_len;
+
+              /* this subwriter access is moderately unkosher since we
+               * already unrecursed, but it works as long as unrecurse
+               * doesn't break us on purpose
+               */
+              bytes_written_after_start_after = writer_get_array_len (&subwriter);
+
+              bytes_before_start_after =
+                start_after->value_pos - reader_array_start_pos;
+
+              fixup.len_pos_in_reader = reader_array_len_pos;
+              fixup.new_len =
+                bytes_before_start_after +
+                start_after_new_len +
+                bytes_written_after_start_after;
+
+              _dbus_assert (_DBUS_ALIGN_VALUE (fixup.len_pos_in_reader, 4) ==
+                            (unsigned) fixup.len_pos_in_reader);
+
+              old_len = _dbus_unpack_uint32 (reader->byte_order,
+                                             _dbus_string_get_const_data_len (reader->value_str,
+                                                                              fixup.len_pos_in_reader, 4));
+
+              if (old_len != fixup.new_len && !append_fixup (fixups, &fixup))
+                goto oom;
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+              _dbus_verbose ("Generated fixup len_pos_in_reader = %d new_len = %d reader_array_start_pos = %d start_after->value_pos = %d bytes_before_start_after = %d start_after_new_len = %d bytes_written_after_start_after = %d\n",
+                             fixup.len_pos_in_reader,
+                             fixup.new_len,
+                             reader_array_start_pos,
+                             start_after->value_pos,
+                             bytes_before_start_after,
+                             start_after_new_len,
+                             bytes_written_after_start_after);
+#endif
+            }
+        }
+      else
+        {
+          DBusBasicValue val;
+
+          _dbus_assert (dbus_type_is_basic (current_type));
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+          _dbus_verbose ("Reading basic value %s at %d\n",
+                         _dbus_type_to_string (current_type),
+                         reader->value_pos);
+#endif
+
+          _dbus_type_reader_read_basic (reader, &val);
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+          _dbus_verbose ("Writing basic value %s at %d write target len %d inside_start_after = %d\n",
+                         _dbus_type_to_string (current_type),
+                         writer->value_pos,
+                         _dbus_string_get_length (writer->value_str),
+                         inside_start_after);
+#endif
+          if (!inside_start_after)
+            enable_if_after (writer, reader, start_after);
+          if (!_dbus_type_writer_write_basic (writer, current_type, &val))
+            goto oom;
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+          _dbus_verbose ("Wrote basic value %s, new value_pos %d write target len %d\n",
+                         _dbus_type_to_string (current_type),
+                         writer->value_pos,
+                         _dbus_string_get_length (writer->value_str));
+#endif
+        }
+
+      _dbus_type_reader_next (reader);
+    }
+
+  return TRUE;
+
+ oom:
+  if (fixups)
+    apply_and_free_fixups (fixups, NULL); /* NULL for reader to apply to */
+
+  return FALSE;
+}
+
+/**
+ * Iterate through all values in the given reader, writing a copy of
+ * each value to the writer.  The reader will be moved forward to its
+ * end position.
+ *
+ * If a reader start_after is provided, it should be a reader for the
+ * same data as the reader to be written. Only values occurring after
+ * the value pointed to by start_after will be written to the writer.
+ *
+ * If start_after is provided, then the copy of the reader will be
+ * partial. This means that array lengths will not have been copied.
+ * The assumption is that you wrote a new version of the value at
+ * start_after to the writer. You have to pass in the start position
+ * and length of the new value. (If you are deleting the value
+ * at start_after, pass in 0 for the length.)
+ *
+ * If the fixups parameter is non-#NULL, then any array length that
+ * was read but not written due to start_after will be provided
+ * as a #DBusArrayLenFixup. The fixup contains the position of the
+ * array length in the source data, and the correct array length
+ * assuming you combine the source data before start_after with
+ * the written data at start_after and beyond.
+ *
+ * @param writer the writer to copy to
+ * @param reader the reader to copy from
+ * @param start_after #NULL or a reader showing where to start
+ * @param start_after_new_pos the position of start_after equivalent in the target data
+ * @param start_after_new_len the length of start_after equivalent in the target data
+ * @param fixups list to append #DBusArrayLenFixup if the write was partial
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_type_writer_write_reader_partial (DBusTypeWriter       *writer,
+                                        DBusTypeReader       *reader,
+                                        const DBusTypeReader *start_after,
+                                        int                   start_after_new_pos,
+                                        int                   start_after_new_len,
+                                        DBusList            **fixups)
+{
+  DBusTypeWriter orig;
+  int orig_type_len;
+  int orig_value_len;
+  int new_bytes;
+  int orig_enabled;
+
+  orig = *writer;
+  orig_type_len = _dbus_string_get_length (writer->type_str);
+  orig_value_len = _dbus_string_get_length (writer->value_str);
+  orig_enabled = writer->enabled;
+
+  if (start_after)
+    _dbus_type_writer_set_enabled (writer, FALSE);
+
+  if (!writer_write_reader_helper (writer, reader, start_after,
+                                   start_after_new_pos,
+                                   start_after_new_len,
+                                   fixups, FALSE))
+    goto oom;
+
+  _dbus_type_writer_set_enabled (writer, orig_enabled);
+  return TRUE;
+
+ oom:
+  if (!writer->type_pos_is_expectation)
+    {
+      new_bytes = _dbus_string_get_length (writer->type_str) - orig_type_len;
+      _dbus_string_delete (writer->type_str, orig.type_pos, new_bytes);
+    }
+  new_bytes = _dbus_string_get_length (writer->value_str) - orig_value_len;
+  _dbus_string_delete (writer->value_str, orig.value_pos, new_bytes);
+
+  *writer = orig;
+
+  return FALSE;
+}
+
+/**
+ * Iterate through all values in the given reader, writing a copy of
+ * each value to the writer.  The reader will be moved forward to its
+ * end position.
+ *
+ * @param writer the writer to copy to
+ * @param reader the reader to copy from
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_type_writer_write_reader (DBusTypeWriter       *writer,
+                                DBusTypeReader       *reader)
+{
+  return _dbus_type_writer_write_reader_partial (writer, reader, NULL, 0, 0, NULL);
+}
+
+/**
+ * If disabled, a writer can still be iterated forward and recursed/unrecursed
+ * but won't write any values. Types will still be written unless the
+ * writer is a "values only" writer, because the writer needs access to
+ * a valid signature to be able to iterate.
+ *
+ * @param writer the type writer
+ * @param enabled #TRUE if values should be written
+ */
+void
+_dbus_type_writer_set_enabled (DBusTypeWriter   *writer,
+                               dbus_bool_t       enabled)
+{
+  writer->enabled = enabled != FALSE;
+}
+
+/** @} */ /* end of DBusMarshal group */
+
+/* tests in dbus-marshal-recursive-util.c */
diff --git a/src/dbus/dbus-marshal-recursive.h b/src/dbus/dbus-marshal-recursive.h
new file mode 100644 (file)
index 0000000..14f38b2
--- /dev/null
@@ -0,0 +1,196 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-recursive.h  Marshalling routines for recursive types
+ *
+ * Copyright (C) 2004, 2005 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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_MARSHAL_RECURSIVE_H
+#define DBUS_MARSHAL_RECURSIVE_H
+
+#include <config.h>
+#include <dbus/dbus-protocol.h>
+#include <dbus/dbus-list.h>
+
+#ifndef PACKAGE
+#error "config.h not included here"
+#endif
+
+typedef struct DBusTypeReader      DBusTypeReader;
+typedef struct DBusTypeWriter      DBusTypeWriter;
+typedef struct DBusTypeReaderClass DBusTypeReaderClass;
+typedef struct DBusArrayLenFixup   DBusArrayLenFixup;
+
+/**
+ * The type reader is an iterator for reading values from a block of
+ * values.
+ */
+struct DBusTypeReader
+{
+  dbus_uint32_t byte_order : 8; /**< byte order of the block */
+
+  dbus_uint32_t finished : 1;   /**< marks we're at end iterator for cases
+                                 * where we don't have another way to tell
+                                 */
+  dbus_uint32_t array_len_offset : 3; /**< bytes back from start_pos that len ends */
+  const DBusString *type_str;   /**< string containing signature of block */
+  int type_pos;                 /**< current position in signature */
+  const DBusString *value_str;  /**< string containing values of block */
+  int value_pos;                /**< current position in values */
+
+  const DBusTypeReaderClass *klass; /**< the vtable for the reader */
+  union
+  {
+    struct {
+      int start_pos;                /**< for array readers, the start of the array values */
+    } array;
+  } u; /**< class-specific data */
+};
+
+/**
+ * The type writer is an iterator for writing to a block of values.
+ */
+struct DBusTypeWriter
+{
+  dbus_uint32_t byte_order : 8;            /**< byte order to write values with */
+
+  dbus_uint32_t container_type : 8;        /**< what are we inside? (e.g. struct, variant, array) */
+
+  dbus_uint32_t type_pos_is_expectation : 1; /**< type_pos can be either an insertion point for or an expected next type */
+
+  dbus_uint32_t enabled : 1; /**< whether to write values */
+
+  DBusString *type_str; /**< where to write typecodes (or read type expectations) */
+  int type_pos;         /**< current pos in type_str */
+  DBusString *value_str; /**< where to write values */
+  int value_pos;         /**< next position to write */
+
+  union
+  {
+    struct {
+      int start_pos; /**< position of first element in the array */
+      int len_pos;   /**< position of length of the array */
+      int element_type_pos; /**< position of array element type in type_str */
+    } array;
+  } u; /**< class-specific data */
+};
+
+/**
+ * When modifying an existing block of values, array lengths may need
+ * to be adjusted; those adjustments are described by this struct.
+ */
+struct DBusArrayLenFixup
+{
+  int len_pos_in_reader; /**< where the length was in the original block */
+  int new_len;           /**< the new value of the length in the written-out block */
+};
+
+void        _dbus_type_reader_init                      (DBusTypeReader        *reader,
+                                                         int                    byte_order,
+                                                         const DBusString      *type_str,
+                                                         int                    type_pos,
+                                                         const DBusString      *value_str,
+                                                         int                    value_pos);
+void        _dbus_type_reader_init_types_only           (DBusTypeReader        *reader,
+                                                         const DBusString      *type_str,
+                                                         int                    type_pos);
+int         _dbus_type_reader_get_current_type          (const DBusTypeReader  *reader);
+int         _dbus_type_reader_get_element_type          (const DBusTypeReader  *reader);
+int         _dbus_type_reader_get_value_pos             (const DBusTypeReader  *reader);
+void        _dbus_type_reader_read_basic                (const DBusTypeReader  *reader,
+                                                         void                  *value);
+int         _dbus_type_reader_get_array_length          (const DBusTypeReader  *reader);
+void        _dbus_type_reader_read_fixed_multi          (const DBusTypeReader  *reader,
+                                                         void                  *value,
+                                                         int                   *n_elements);
+void        _dbus_type_reader_read_raw                  (const DBusTypeReader  *reader,
+                                                         const unsigned char  **value_location);
+void        _dbus_type_reader_recurse                   (DBusTypeReader        *reader,
+                                                         DBusTypeReader        *subreader);
+dbus_bool_t _dbus_type_reader_next                      (DBusTypeReader        *reader);
+dbus_bool_t _dbus_type_reader_has_next                  (const DBusTypeReader  *reader);
+void        _dbus_type_reader_get_signature             (const DBusTypeReader  *reader,
+                                                         const DBusString     **str_p,
+                                                         int                   *start_p,
+                                                         int                   *len_p);
+dbus_bool_t _dbus_type_reader_set_basic                 (DBusTypeReader        *reader,
+                                                         const void            *value,
+                                                         const DBusTypeReader  *realign_root);
+dbus_bool_t _dbus_type_reader_delete                    (DBusTypeReader        *reader,
+                                                         const DBusTypeReader  *realign_root);
+dbus_bool_t _dbus_type_reader_greater_than              (const DBusTypeReader  *lhs,
+                                                         const DBusTypeReader  *rhs);
+
+dbus_bool_t _dbus_type_reader_equal_values              (const DBusTypeReader *lhs,
+                                                         const DBusTypeReader *rhs);
+
+void        _dbus_type_signature_next                   (const char            *signature,
+                                                        int                   *type_pos);
+
+void        _dbus_type_writer_init                 (DBusTypeWriter        *writer,
+                                                    int                    byte_order,
+                                                    DBusString            *type_str,
+                                                    int                    type_pos,
+                                                    DBusString            *value_str,
+                                                    int                    value_pos);
+void        _dbus_type_writer_init_types_delayed   (DBusTypeWriter        *writer,
+                                                    int                    byte_order,
+                                                    DBusString            *value_str,
+                                                    int                    value_pos);
+void        _dbus_type_writer_add_types            (DBusTypeWriter        *writer,
+                                                    DBusString            *type_str,
+                                                    int                    type_pos);
+void        _dbus_type_writer_remove_types         (DBusTypeWriter        *writer);
+void        _dbus_type_writer_init_values_only     (DBusTypeWriter        *writer,
+                                                    int                    byte_order,
+                                                    const DBusString      *type_str,
+                                                    int                    type_pos,
+                                                    DBusString            *value_str,
+                                                    int                    value_pos);
+dbus_bool_t _dbus_type_writer_write_basic          (DBusTypeWriter        *writer,
+                                                    int                    type,
+                                                    const void            *value);
+dbus_bool_t _dbus_type_writer_write_fixed_multi    (DBusTypeWriter        *writer,
+                                                    int                    element_type,
+                                                    const void            *value,
+                                                    int                    n_elements);
+dbus_bool_t _dbus_type_writer_recurse              (DBusTypeWriter        *writer,
+                                                    int                    container_type,
+                                                    const DBusString      *contained_type,
+                                                    int                    contained_type_start,
+                                                    DBusTypeWriter        *sub);
+dbus_bool_t _dbus_type_writer_unrecurse            (DBusTypeWriter        *writer,
+                                                    DBusTypeWriter        *sub);
+dbus_bool_t _dbus_type_writer_append_array         (DBusTypeWriter        *writer,
+                                                    const DBusString      *contained_type,
+                                                    int                    contained_type_start,
+                                                    DBusTypeWriter        *sub);
+dbus_bool_t _dbus_type_writer_write_reader         (DBusTypeWriter        *writer,
+                                                    DBusTypeReader        *reader);
+dbus_bool_t _dbus_type_writer_write_reader_partial (DBusTypeWriter        *writer,
+                                                    DBusTypeReader        *reader,
+                                                    const DBusTypeReader  *start_after,
+                                                    int                    start_after_new_pos,
+                                                    int                    start_after_new_len,
+                                                    DBusList             **fixups);
+void        _dbus_type_writer_set_enabled          (DBusTypeWriter        *writer,
+                                                    dbus_bool_t            enabled);
+
+
+#endif /* DBUS_MARSHAL_RECURSIVE_H */
diff --git a/src/dbus/dbus-marshal-validate-util.c b/src/dbus/dbus-marshal-validate-util.c
new file mode 100644 (file)
index 0000000..ac901c3
--- /dev/null
@@ -0,0 +1,588 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-validate-util.c Would be in dbus-marshal-validate.c, but only used by tests/bus
+ *
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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 <config.h>
+#ifdef DBUS_BUILD_TESTS
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+#include "dbus-internals.h"
+#include "dbus-marshal-validate.h"
+#include "dbus-marshal-recursive.h"
+
+#include "dbus-test.h"
+#include <stdio.h>
+
+typedef struct
+{
+  const char *data;
+  DBusValidity expected;
+} ValidityTest;
+
+static void
+run_validity_tests (const ValidityTest *tests,
+                    int                 n_tests,
+                    DBusValidity (* func) (const DBusString*,int,int))
+{
+  int i;
+
+  for (i = 0; i < n_tests; i++)
+    {
+      DBusString str;
+      DBusValidity v;
+
+      _dbus_string_init_const (&str, tests[i].data);
+
+      v = (*func) (&str, 0, _dbus_string_get_length (&str));
+
+      if (v != tests[i].expected)
+        {
+          _dbus_warn ("Improper validation result %d for '%s'\n",
+                      v, tests[i].data);
+          _dbus_assert_not_reached ("test failed");
+        }
+
+      ++i;
+    }
+}
+
+static const ValidityTest signature_tests[] = {
+  { "", DBUS_VALID },
+  { "i", DBUS_VALID },
+  { "ai", DBUS_VALID },
+  { "(i)", DBUS_VALID },
+  { "w", DBUS_INVALID_UNKNOWN_TYPECODE },
+  { "a", DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE },
+  { "aaaaaa", DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE },
+  { "ii(ii)a", DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE },
+  { "ia", DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE },
+  /* DBUS_INVALID_SIGNATURE_TOO_LONG, */ /* too hard to test this way */
+  { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+    DBUS_INVALID_EXCEEDED_MAXIMUM_ARRAY_RECURSION },
+  { "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((ii))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))",
+    DBUS_INVALID_EXCEEDED_MAXIMUM_STRUCT_RECURSION },
+  { ")", DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED },
+  { "i)", DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED },
+  { "a)", DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED },
+  { "(", DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED },
+  { "(i", DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED },
+  { "(iiiii", DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED },
+  { "(ai", DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED },
+  { "()", DBUS_INVALID_STRUCT_HAS_NO_FIELDS },
+  { "(())", DBUS_INVALID_STRUCT_HAS_NO_FIELDS },
+  { "a()", DBUS_INVALID_STRUCT_HAS_NO_FIELDS },
+  { "i()", DBUS_INVALID_STRUCT_HAS_NO_FIELDS },
+  { "()i", DBUS_INVALID_STRUCT_HAS_NO_FIELDS },
+  { "(a)", DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE },
+  { "a{ia}", DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE },
+  { "a{}", DBUS_INVALID_DICT_ENTRY_HAS_NO_FIELDS },
+  { "a{aii}", DBUS_INVALID_DICT_KEY_MUST_BE_BASIC_TYPE },
+  /* { "a{i}", DBUS_INVALID_DICT_ENTRY_HAS_ONLY_ONE_FIELD }, */
+  /* { "{is}", DBUS_INVALID_DICT_ENTRY_NOT_INSIDE_ARRAY }, */
+  /* { "a{isi}", DBUS_INVALID_DICT_ENTRY_HAS_TOO_MANY_FIELDS }, */
+};
+
+dbus_bool_t
+_dbus_marshal_validate_test (void)
+{
+  DBusString str;
+  int i;
+
+  const char *valid_paths[] = {
+    "/",
+    "/foo/bar",
+    "/foo",
+    "/foo/bar/baz"
+  };
+  const char *invalid_paths[] = {
+    "bar",
+    "bar/baz",
+    "/foo/bar/",
+    "/foo/"
+    "foo/",
+    "boo//blah",
+    "//",
+    "///",
+    "foo///blah/",
+    "Hello World",
+    "",
+    "   ",
+    "foo bar"
+  };
+
+  const char *valid_interfaces[] = {
+    "org.freedesktop.Foo",
+    "Bar.Baz",
+    "Blah.Blah.Blah.Blah.Blah",
+    "a.b",
+    "a.b.c.d.e.f.g",
+    "a0.b1.c2.d3.e4.f5.g6",
+    "abc123.foo27"
+  };
+  const char *invalid_interfaces[] = {
+    ".",
+    "",
+    "..",
+    ".Foo.Bar",
+    "..Foo.Bar",
+    "Foo.Bar.",
+    "Foo.Bar..",
+    "Foo",
+    "9foo.bar.baz",
+    "foo.bar..baz",
+    "foo.bar...baz",
+    "foo.bar.b..blah",
+    ":",
+    ":0-1",
+    "10",
+    ":11.34324",
+    "0.0.0",
+    "0..0",
+    "foo.Bar.%",
+    "foo.Bar!!",
+    "!Foo.bar.bz",
+    "foo.$.blah",
+    "",
+    "   ",
+    "foo bar"
+  };
+
+  const char *valid_unique_names[] = {
+    ":0",
+    ":a",
+    ":",
+    ":.a",
+    ":.1",
+    ":0.1",
+    ":000.2222",
+    ":.blah",
+    ":abce.freedesktop.blah"
+  };
+  const char *invalid_unique_names[] = {
+    //":-",
+    ":!",
+    //":0-10",
+    ":blah.",
+    ":blah.",
+    ":blah..org",
+    ":blah.org..",
+    ":..blah.org",
+    "",
+    "   ",
+    "foo bar"
+  };
+
+  const char *valid_members[] = {
+    "Hello",
+    "Bar",
+    "foobar",
+    "_foobar",
+    "foo89"
+  };
+
+  const char *invalid_members[] = {
+    "9Hello",
+    "10",
+    "1",
+    "foo-bar",
+    "blah.org",
+    ".blah",
+    "blah.",
+    "Hello.",
+    "!foo",
+    "",
+    "   ",
+    "foo bar"
+  };
+
+  const char *valid_signatures[] = {
+    "",
+    "sss",
+    "i",
+    "b"
+  };
+
+  const char *invalid_signatures[] = {
+    " ",
+    "not a valid signature",
+    "123",
+    ".",
+    "(",
+    "a{(ii)i}" /* https://bugs.freedesktop.org/show_bug.cgi?id=17803 */
+  };
+
+  /* Signature with reason */
+
+  run_validity_tests (signature_tests, _DBUS_N_ELEMENTS (signature_tests),
+                      _dbus_validate_signature_with_reason);
+
+  /* Path validation */
+  i = 0;
+  while (i < (int) _DBUS_N_ELEMENTS (valid_paths))
+    {
+      _dbus_string_init_const (&str, valid_paths[i]);
+
+      if (!_dbus_validate_path (&str, 0,
+                                _dbus_string_get_length (&str)))
+        {
+          _dbus_warn ("Path \"%s\" should have been valid\n", valid_paths[i]);
+          _dbus_assert_not_reached ("invalid path");
+        }
+
+      ++i;
+    }
+
+  i = 0;
+  while (i < (int) _DBUS_N_ELEMENTS (invalid_paths))
+    {
+      _dbus_string_init_const (&str, invalid_paths[i]);
+
+      if (_dbus_validate_path (&str, 0,
+                               _dbus_string_get_length (&str)))
+        {
+          _dbus_warn ("Path \"%s\" should have been invalid\n", invalid_paths[i]);
+          _dbus_assert_not_reached ("valid path");
+        }
+
+      ++i;
+    }
+
+  /* Interface validation */
+  i = 0;
+  while (i < (int) _DBUS_N_ELEMENTS (valid_interfaces))
+    {
+      _dbus_string_init_const (&str, valid_interfaces[i]);
+
+      if (!_dbus_validate_interface (&str, 0,
+                                     _dbus_string_get_length (&str)))
+        {
+          _dbus_warn ("Interface \"%s\" should have been valid\n", valid_interfaces[i]);
+          _dbus_assert_not_reached ("invalid interface");
+        }
+
+      ++i;
+    }
+
+  i = 0;
+  while (i < (int) _DBUS_N_ELEMENTS (invalid_interfaces))
+    {
+      _dbus_string_init_const (&str, invalid_interfaces[i]);
+
+      if (_dbus_validate_interface (&str, 0,
+                                    _dbus_string_get_length (&str)))
+        {
+          _dbus_warn ("Interface \"%s\" should have been invalid\n", invalid_interfaces[i]);
+          _dbus_assert_not_reached ("valid interface");
+        }
+
+      ++i;
+    }
+
+  /* Bus name validation (check that valid interfaces are valid bus names,
+   * and invalid interfaces are invalid services except if they start with ':')
+   */
+  i = 0;
+  while (i < (int) _DBUS_N_ELEMENTS (valid_interfaces))
+    {
+      _dbus_string_init_const (&str, valid_interfaces[i]);
+
+      if (!_dbus_validate_bus_name (&str, 0,
+                                   _dbus_string_get_length (&str)))
+        {
+          _dbus_warn ("Bus name \"%s\" should have been valid\n", valid_interfaces[i]);
+          _dbus_assert_not_reached ("invalid bus name");
+        }
+
+      ++i;
+    }
+
+  i = 0;
+  while (i < (int) _DBUS_N_ELEMENTS (invalid_interfaces))
+    {
+      if (invalid_interfaces[i][0] != ':')
+        {
+          _dbus_string_init_const (&str, invalid_interfaces[i]);
+
+          if (_dbus_validate_bus_name (&str, 0,
+                                       _dbus_string_get_length (&str)))
+            {
+              _dbus_warn ("Bus name \"%s\" should have been invalid\n", invalid_interfaces[i]);
+              _dbus_assert_not_reached ("valid bus name");
+            }
+        }
+
+      ++i;
+    }
+
+  /* unique name validation */
+  i = 0;
+  while (i < (int) _DBUS_N_ELEMENTS (valid_unique_names))
+    {
+      _dbus_string_init_const (&str, valid_unique_names[i]);
+
+      if (!_dbus_validate_bus_name (&str, 0,
+                                    _dbus_string_get_length (&str)))
+        {
+          _dbus_warn ("Bus name \"%s\" should have been valid\n", valid_unique_names[i]);
+          _dbus_assert_not_reached ("invalid unique name");
+        }
+
+      ++i;
+    }
+
+  i = 0;
+  while (i < (int) _DBUS_N_ELEMENTS (invalid_unique_names))
+    {
+      _dbus_string_init_const (&str, invalid_unique_names[i]);
+
+      if (_dbus_validate_bus_name (&str, 0,
+                                   _dbus_string_get_length (&str)))
+        {
+          _dbus_warn ("Bus name \"%s\" should have been invalid\n", invalid_unique_names[i]);
+          _dbus_assert_not_reached ("valid unique name");
+        }
+
+      ++i;
+    }
+
+
+  /* Error name validation (currently identical to interfaces)
+   */
+  i = 0;
+  while (i < (int) _DBUS_N_ELEMENTS (valid_interfaces))
+    {
+      _dbus_string_init_const (&str, valid_interfaces[i]);
+
+      if (!_dbus_validate_error_name (&str, 0,
+                                      _dbus_string_get_length (&str)))
+        {
+          _dbus_warn ("Error name \"%s\" should have been valid\n", valid_interfaces[i]);
+          _dbus_assert_not_reached ("invalid error name");
+        }
+
+      ++i;
+    }
+
+  i = 0;
+  while (i < (int) _DBUS_N_ELEMENTS (invalid_interfaces))
+    {
+      if (invalid_interfaces[i][0] != ':')
+        {
+          _dbus_string_init_const (&str, invalid_interfaces[i]);
+
+          if (_dbus_validate_error_name (&str, 0,
+                                         _dbus_string_get_length (&str)))
+            {
+              _dbus_warn ("Error name \"%s\" should have been invalid\n", invalid_interfaces[i]);
+              _dbus_assert_not_reached ("valid error name");
+            }
+        }
+
+      ++i;
+    }
+
+  /* Member validation */
+  i = 0;
+  while (i < (int) _DBUS_N_ELEMENTS (valid_members))
+    {
+      _dbus_string_init_const (&str, valid_members[i]);
+
+      if (!_dbus_validate_member (&str, 0,
+                                  _dbus_string_get_length (&str)))
+        {
+          _dbus_warn ("Member \"%s\" should have been valid\n", valid_members[i]);
+          _dbus_assert_not_reached ("invalid member");
+        }
+
+      ++i;
+    }
+
+  i = 0;
+  while (i < (int) _DBUS_N_ELEMENTS (invalid_members))
+    {
+      _dbus_string_init_const (&str, invalid_members[i]);
+
+      if (_dbus_validate_member (&str, 0,
+                                 _dbus_string_get_length (&str)))
+        {
+          _dbus_warn ("Member \"%s\" should have been invalid\n", invalid_members[i]);
+          _dbus_assert_not_reached ("valid member");
+        }
+
+      ++i;
+    }
+
+  /* Signature validation */
+  i = 0;
+  while (i < (int) _DBUS_N_ELEMENTS (valid_signatures))
+    {
+      _dbus_string_init_const (&str, valid_signatures[i]);
+
+      if (!_dbus_validate_signature (&str, 0,
+                                     _dbus_string_get_length (&str)))
+        {
+          _dbus_warn ("Signature \"%s\" should have been valid\n", valid_signatures[i]);
+          _dbus_assert_not_reached ("invalid signature");
+        }
+
+      ++i;
+    }
+
+  i = 0;
+  while (i < (int) _DBUS_N_ELEMENTS (invalid_signatures))
+    {
+      _dbus_string_init_const (&str, invalid_signatures[i]);
+
+      if (_dbus_validate_signature (&str, 0,
+                                    _dbus_string_get_length (&str)))
+        {
+          _dbus_warn ("Signature \"%s\" should have been invalid\n", invalid_signatures[i]);
+          _dbus_assert_not_reached ("valid signature");
+        }
+
+      ++i;
+    }
+
+  /* Validate claimed length longer than real length */
+  _dbus_string_init_const (&str, "abc.efg");
+  if (_dbus_validate_bus_name (&str, 0, 8))
+    _dbus_assert_not_reached ("validated too-long string");
+  if (_dbus_validate_interface (&str, 0, 8))
+    _dbus_assert_not_reached ("validated too-long string");
+  if (_dbus_validate_error_name (&str, 0, 8))
+    _dbus_assert_not_reached ("validated too-long string");
+
+  _dbus_string_init_const (&str, "abc");
+  if (_dbus_validate_member (&str, 0, 4))
+    _dbus_assert_not_reached ("validated too-long string");
+
+  _dbus_string_init_const (&str, "sss");
+  if (_dbus_validate_signature (&str, 0, 4))
+    _dbus_assert_not_reached ("validated too-long signature");
+
+  /* Validate string exceeding max name length */
+  if (!_dbus_string_init (&str))
+    _dbus_assert_not_reached ("no memory");
+
+  while (_dbus_string_get_length (&str) <= DBUS_MAXIMUM_NAME_LENGTH)
+    if (!_dbus_string_append (&str, "abc.def"))
+      _dbus_assert_not_reached ("no memory");
+
+  if (_dbus_validate_bus_name (&str, 0, _dbus_string_get_length (&str)))
+    _dbus_assert_not_reached ("validated overmax string");
+  if (_dbus_validate_interface (&str, 0, _dbus_string_get_length (&str)))
+    _dbus_assert_not_reached ("validated overmax string");
+  if (_dbus_validate_error_name (&str, 0, _dbus_string_get_length (&str)))
+    _dbus_assert_not_reached ("validated overmax string");
+
+  /* overlong member */
+  _dbus_string_set_length (&str, 0);
+  while (_dbus_string_get_length (&str) <= DBUS_MAXIMUM_NAME_LENGTH)
+    if (!_dbus_string_append (&str, "abc"))
+      _dbus_assert_not_reached ("no memory");
+
+  if (_dbus_validate_member (&str, 0, _dbus_string_get_length (&str)))
+    _dbus_assert_not_reached ("validated overmax string");
+
+  /* overlong unique name */
+  _dbus_string_set_length (&str, 0);
+  _dbus_string_append (&str, ":");
+  while (_dbus_string_get_length (&str) <= DBUS_MAXIMUM_NAME_LENGTH)
+    if (!_dbus_string_append (&str, "abc"))
+      _dbus_assert_not_reached ("no memory");
+
+  if (_dbus_validate_bus_name (&str, 0, _dbus_string_get_length (&str)))
+    _dbus_assert_not_reached ("validated overmax string");
+
+  _dbus_string_free (&str);
+
+  /* Body validation; test basic validation of valid bodies for both endian */
+  
+  {
+    int sequence;
+    DBusString signature;
+    DBusString body;
+
+    if (!_dbus_string_init (&signature) || !_dbus_string_init (&body))
+      _dbus_assert_not_reached ("oom");
+
+    sequence = 0;
+    while (dbus_internal_do_not_use_generate_bodies (sequence,
+                                                     DBUS_LITTLE_ENDIAN,
+                                                     &signature, &body))
+      {
+        DBusValidity validity;
+
+        validity = _dbus_validate_body_with_reason (&signature, 0,
+                                                    DBUS_LITTLE_ENDIAN,
+                                                    NULL, &body, 0,
+                                                    _dbus_string_get_length (&body));
+        if (validity != DBUS_VALID)
+          {
+            _dbus_warn ("invalid code %d expected valid on sequence %d little endian\n",
+                        validity, sequence);
+            _dbus_verbose_bytes_of_string (&signature, 0, _dbus_string_get_length (&signature));
+            _dbus_verbose_bytes_of_string (&body, 0, _dbus_string_get_length (&body));
+            _dbus_assert_not_reached ("test failed");
+          }
+
+        _dbus_string_set_length (&signature, 0);
+        _dbus_string_set_length (&body, 0);
+        ++sequence;
+      }
+                                                     
+    sequence = 0;
+    while (dbus_internal_do_not_use_generate_bodies (sequence,
+                                                     DBUS_BIG_ENDIAN,
+                                                     &signature, &body))
+      {
+        DBusValidity validity;
+
+        validity = _dbus_validate_body_with_reason (&signature, 0,
+                                                    DBUS_BIG_ENDIAN,
+                                                    NULL, &body, 0,
+                                                    _dbus_string_get_length (&body));
+        if (validity != DBUS_VALID)
+          {
+            _dbus_warn ("invalid code %d expected valid on sequence %d big endian\n",
+                        validity, sequence);
+            _dbus_verbose_bytes_of_string (&signature, 0, _dbus_string_get_length (&signature));
+            _dbus_verbose_bytes_of_string (&body, 0, _dbus_string_get_length (&body));
+            _dbus_assert_not_reached ("test failed");
+          }
+
+        _dbus_string_set_length (&signature, 0);
+        _dbus_string_set_length (&body, 0);
+        ++sequence;
+      }
+
+    _dbus_string_free (&signature);
+    _dbus_string_free (&body);
+  }
+  
+  return TRUE;
+}
+
+#endif /* !DOXYGEN_SHOULD_SKIP_THIS */
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-marshal-validate.c b/src/dbus/dbus-marshal-validate.c
new file mode 100644 (file)
index 0000000..78d5594
--- /dev/null
@@ -0,0 +1,1204 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-validate.c Validation routines for marshaled data
+ *
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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-internals.h"
+#include "dbus-marshal-validate.h"
+#include "dbus-marshal-recursive.h"
+#include "dbus-marshal-basic.h"
+#include "dbus-signature.h"
+#include "dbus-string.h"
+
+/**
+ * @addtogroup DBusMarshal
+ *
+ * @{
+ */
+
+/**
+ * Verifies that the range of type_str from type_pos to type_end is a
+ * valid signature.  If this function returns #TRUE, it will be safe
+ * to iterate over the signature with a types-only #DBusTypeReader.
+ * The range passed in should NOT include the terminating
+ * nul/DBUS_TYPE_INVALID.
+ *
+ * @param type_str the string
+ * @param type_pos where the typecodes start
+ * @param len length of typecodes
+ * @returns #DBUS_VALID if valid, reason why invalid otherwise
+ */
+DBusValidity
+_dbus_validate_signature_with_reason (const DBusString *type_str,
+                                      int               type_pos,
+                                      int               len)
+{
+  const unsigned char *p;
+  const unsigned char *end;
+  int last;
+  int struct_depth;
+  int array_depth;
+  int dict_entry_depth;
+  DBusValidity result;
+
+  int element_count;
+  DBusList *element_count_stack;
+
+  result = DBUS_VALID;
+  element_count_stack = NULL;
+
+  if (!_dbus_list_append (&element_count_stack, _DBUS_INT_TO_POINTER (0)))
+    {
+      result = DBUS_VALIDITY_UNKNOWN_OOM_ERROR;
+      goto out;
+    }
+
+  _dbus_assert (type_str != NULL);
+  _dbus_assert (type_pos < _DBUS_INT32_MAX - len);
+  _dbus_assert (len >= 0);
+  _dbus_assert (type_pos >= 0);
+
+  if (len > DBUS_MAXIMUM_SIGNATURE_LENGTH)
+    {
+      result = DBUS_INVALID_SIGNATURE_TOO_LONG;
+      goto out;
+    }
+
+  p = _dbus_string_get_const_data_len (type_str, type_pos, 0);
+
+  end = _dbus_string_get_const_data_len (type_str, type_pos + len, 0);
+  struct_depth = 0;
+  array_depth = 0;
+  dict_entry_depth = 0;
+  last = DBUS_TYPE_INVALID;
+
+  while (p != end)
+    {
+      switch (*p)
+        {
+        case DBUS_TYPE_BYTE:
+        case DBUS_TYPE_BOOLEAN:
+        case DBUS_TYPE_INT16:
+        case DBUS_TYPE_UINT16:
+        case DBUS_TYPE_INT32:
+        case DBUS_TYPE_UINT32:
+        case DBUS_TYPE_INT64:
+        case DBUS_TYPE_UINT64:
+        case DBUS_TYPE_DOUBLE:
+        case DBUS_TYPE_STRING:
+        case DBUS_TYPE_OBJECT_PATH:
+        case DBUS_TYPE_SIGNATURE:
+        case DBUS_TYPE_VARIANT:
+          break;
+
+        case DBUS_TYPE_ARRAY:
+          array_depth += 1;
+          if (array_depth > DBUS_MAXIMUM_TYPE_RECURSION_DEPTH)
+            {
+              result = DBUS_INVALID_EXCEEDED_MAXIMUM_ARRAY_RECURSION;
+              goto out;
+            }
+          break;
+
+        case DBUS_STRUCT_BEGIN_CHAR:
+          struct_depth += 1;
+
+          if (struct_depth > DBUS_MAXIMUM_TYPE_RECURSION_DEPTH)
+            {
+              result = DBUS_INVALID_EXCEEDED_MAXIMUM_STRUCT_RECURSION;
+              goto out;
+            }
+          
+          if (!_dbus_list_append (&element_count_stack, 
+                             _DBUS_INT_TO_POINTER (0)))
+            {
+              result = DBUS_VALIDITY_UNKNOWN_OOM_ERROR;
+              goto out;
+            }
+
+          break;
+
+        case DBUS_STRUCT_END_CHAR:
+          if (struct_depth == 0)
+            {
+              result = DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED;
+              goto out;
+            }
+
+          if (last == DBUS_STRUCT_BEGIN_CHAR)
+            {
+              result = DBUS_INVALID_STRUCT_HAS_NO_FIELDS;
+              goto out;
+            }
+
+          _dbus_list_pop_last (&element_count_stack);
+
+          struct_depth -= 1;
+          break;
+
+        case DBUS_DICT_ENTRY_BEGIN_CHAR:
+          if (last != DBUS_TYPE_ARRAY)
+            {
+              result = DBUS_INVALID_DICT_ENTRY_NOT_INSIDE_ARRAY;
+              goto out;
+            }
+            
+          dict_entry_depth += 1;
+
+          if (dict_entry_depth > DBUS_MAXIMUM_TYPE_RECURSION_DEPTH)
+            {
+              result = DBUS_INVALID_EXCEEDED_MAXIMUM_DICT_ENTRY_RECURSION;
+              goto out;
+            }
+
+          if (!_dbus_list_append (&element_count_stack, 
+                             _DBUS_INT_TO_POINTER (0)))
+            {
+              result = DBUS_VALIDITY_UNKNOWN_OOM_ERROR;
+              goto out;
+            }
+
+          break;
+
+        case DBUS_DICT_ENTRY_END_CHAR:
+          if (dict_entry_depth == 0)
+            {
+              result = DBUS_INVALID_DICT_ENTRY_ENDED_BUT_NOT_STARTED;
+              goto out;
+            }
+            
+          dict_entry_depth -= 1;
+
+          element_count = 
+            _DBUS_POINTER_TO_INT (_dbus_list_pop_last (&element_count_stack));
+
+          if (element_count != 2)
+            {
+              if (element_count == 0)
+                result = DBUS_INVALID_DICT_ENTRY_HAS_NO_FIELDS;
+              else if (element_count == 1)
+                result = DBUS_INVALID_DICT_ENTRY_HAS_ONLY_ONE_FIELD;
+              else
+                result = DBUS_INVALID_DICT_ENTRY_HAS_TOO_MANY_FIELDS;
+              
+              goto out;
+            }
+          break;
+          
+        case DBUS_TYPE_STRUCT:     /* doesn't appear in signatures */
+        case DBUS_TYPE_DICT_ENTRY: /* ditto */
+        default:
+          result = DBUS_INVALID_UNKNOWN_TYPECODE;
+         goto out;
+        }
+
+      if (*p != DBUS_TYPE_ARRAY && 
+          *p != DBUS_DICT_ENTRY_BEGIN_CHAR && 
+         *p != DBUS_STRUCT_BEGIN_CHAR) 
+        {
+          element_count = 
+            _DBUS_POINTER_TO_INT (_dbus_list_pop_last (&element_count_stack));
+
+          ++element_count;
+
+          if (!_dbus_list_append (&element_count_stack, 
+                             _DBUS_INT_TO_POINTER (element_count)))
+            {
+              result = DBUS_VALIDITY_UNKNOWN_OOM_ERROR;
+              goto out;
+            }
+        }
+      
+      if (array_depth > 0)
+        {
+          if (*p == DBUS_TYPE_ARRAY && p != end)
+            {
+              const char *p1;
+              p1 = p + 1;
+               if (*p1 == DBUS_STRUCT_END_CHAR ||
+                   *p1 == DBUS_DICT_ENTRY_END_CHAR)
+                 {
+                   result = DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE;
+                   goto out;
+                 }
+            }
+          else
+           {
+              array_depth = 0;
+           }
+        }
+
+      if (last == DBUS_DICT_ENTRY_BEGIN_CHAR)
+        {
+          if (!(_dbus_type_is_valid (*p) && dbus_type_is_basic (*p)))
+            {
+              result = DBUS_INVALID_DICT_KEY_MUST_BE_BASIC_TYPE;
+              goto out;
+            }
+        }
+
+      last = *p;
+      ++p;
+    }
+
+
+  if (array_depth > 0)
+    {
+      result = DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE;
+      goto out;
+    }
+    
+  if (struct_depth > 0)
+    {
+       result = DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED;
+       goto out;
+    }
+    
+  if (dict_entry_depth > 0)
+    {
+      result =  DBUS_INVALID_DICT_ENTRY_STARTED_BUT_NOT_ENDED;
+      goto out;
+    }
+    
+  _dbus_assert (last != DBUS_TYPE_ARRAY);
+  _dbus_assert (last != DBUS_STRUCT_BEGIN_CHAR);
+  _dbus_assert (last != DBUS_DICT_ENTRY_BEGIN_CHAR);
+
+  result = DBUS_VALID;
+
+out:
+  _dbus_list_clear (&element_count_stack);
+  return result;
+}
+
+static DBusValidity
+validate_body_helper (DBusTypeReader       *reader,
+                      int                   byte_order,
+                      dbus_bool_t           walk_reader_to_end,
+                      const unsigned char  *p,
+                      const unsigned char  *end,
+                      const unsigned char **new_p)
+{
+  int current_type;
+
+  while ((current_type = _dbus_type_reader_get_current_type (reader)) != DBUS_TYPE_INVALID)
+    {
+      const unsigned char *a;
+      int alignment;
+
+#if 0
+      _dbus_verbose ("   validating value of type %s type reader %p type_pos %d p %p end %p %d remain\n",
+                     _dbus_type_to_string (current_type), reader, reader->type_pos, p, end,
+                     (int) (end - p));
+#endif
+
+      /* Guarantee that p has one byte to look at */
+      if (p == end)
+        return DBUS_INVALID_NOT_ENOUGH_DATA;
+
+      switch (current_type)
+        {
+        case DBUS_TYPE_BYTE:
+          ++p;
+          break;
+          
+        case DBUS_TYPE_BOOLEAN:
+        case DBUS_TYPE_INT16:
+        case DBUS_TYPE_UINT16:
+        case DBUS_TYPE_INT32:
+        case DBUS_TYPE_UINT32:
+        case DBUS_TYPE_INT64:
+        case DBUS_TYPE_UINT64:
+        case DBUS_TYPE_DOUBLE:
+          alignment = _dbus_type_get_alignment (current_type);
+          a = _DBUS_ALIGN_ADDRESS (p, alignment);
+          if (a >= end)
+            return DBUS_INVALID_NOT_ENOUGH_DATA;
+          while (p != a)
+            {
+              if (*p != '\0')
+                return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL;
+              ++p;
+            }
+          
+          if (current_type == DBUS_TYPE_BOOLEAN)
+            {
+              dbus_uint32_t v = _dbus_unpack_uint32 (byte_order,
+                                                     p);
+              if (!(v == 0 || v == 1))
+                return DBUS_INVALID_BOOLEAN_NOT_ZERO_OR_ONE;
+            }
+          
+          p += alignment;
+          break;
+
+        case DBUS_TYPE_ARRAY:
+        case DBUS_TYPE_STRING:
+        case DBUS_TYPE_OBJECT_PATH:
+          {
+            dbus_uint32_t claimed_len;
+
+            a = _DBUS_ALIGN_ADDRESS (p, 4);
+            if (a + 4 > end)
+              return DBUS_INVALID_NOT_ENOUGH_DATA;
+            while (p != a)
+              {
+                if (*p != '\0')
+                  return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL;
+                ++p;
+              }
+
+            claimed_len = _dbus_unpack_uint32 (byte_order, p);
+            p += 4;
+
+            /* p may now be == end */
+            _dbus_assert (p <= end);
+
+            if (current_type == DBUS_TYPE_ARRAY)
+              {
+                int array_elem_type = _dbus_type_reader_get_element_type (reader);
+
+                if (!_dbus_type_is_valid (array_elem_type))
+                  {
+                    return DBUS_INVALID_UNKNOWN_TYPECODE;
+                  }
+
+                alignment = _dbus_type_get_alignment (array_elem_type);
+
+                a = _DBUS_ALIGN_ADDRESS (p, alignment);
+
+                /* a may now be == end */
+                if (a > end)
+                  return DBUS_INVALID_NOT_ENOUGH_DATA;
+
+                while (p != a)
+                  {
+                    if (*p != '\0')
+                      return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL;
+                    ++p;
+                  }
+              }
+
+            if (claimed_len > (unsigned long) (end - p))
+              return DBUS_INVALID_LENGTH_OUT_OF_BOUNDS;
+
+            if (current_type == DBUS_TYPE_OBJECT_PATH)
+              {
+                DBusString str;
+                _dbus_string_init_const_len (&str, p, claimed_len);
+                if (!_dbus_validate_path (&str, 0,
+                                          _dbus_string_get_length (&str)))
+                  return DBUS_INVALID_BAD_PATH;
+
+                p += claimed_len;
+              }
+            else if (current_type == DBUS_TYPE_STRING)
+              {
+                DBusString str;
+                _dbus_string_init_const_len (&str, p, claimed_len);
+                if (!_dbus_string_validate_utf8 (&str, 0,
+                                                 _dbus_string_get_length (&str)))
+                  return DBUS_INVALID_BAD_UTF8_IN_STRING;
+
+                p += claimed_len;
+              }
+            else if (current_type == DBUS_TYPE_ARRAY && claimed_len > 0)
+              {
+                DBusTypeReader sub;
+                DBusValidity validity;
+                const unsigned char *array_end;
+                int array_elem_type;
+
+                if (claimed_len > DBUS_MAXIMUM_ARRAY_LENGTH)
+                  return DBUS_INVALID_ARRAY_LENGTH_EXCEEDS_MAXIMUM;
+                
+                /* Remember that the reader is types only, so we can't
+                 * use it to iterate over elements. It stays the same
+                 * for all elements.
+                 */
+                _dbus_type_reader_recurse (reader, &sub);
+
+                array_end = p + claimed_len;
+
+                array_elem_type = _dbus_type_reader_get_element_type (reader);
+
+                /* avoid recursive call to validate_body_helper if this is an array
+                 * of fixed-size elements
+                 */ 
+                if (dbus_type_is_fixed (array_elem_type))
+                  {
+                    /* bools need to be handled differently, because they can
+                     * have an invalid value
+                     */
+                    if (array_elem_type == DBUS_TYPE_BOOLEAN)
+                      {
+                        dbus_uint32_t v;
+                        alignment = _dbus_type_get_alignment (array_elem_type);
+
+                        while (p < array_end)
+                          {
+                            v = _dbus_unpack_uint32 (byte_order, p);
+
+                            if (!(v == 0 || v == 1))
+                              return DBUS_INVALID_BOOLEAN_NOT_ZERO_OR_ONE;
+
+                            p += alignment;
+                          }
+                      }
+
+                    else
+                      {
+                        p = array_end;
+                      }
+                  }
+
+                else
+                  {
+                    while (p < array_end)
+                      {
+                        validity = validate_body_helper (&sub, byte_order, FALSE, p, end, &p);
+                        if (validity != DBUS_VALID)
+                          return validity;
+                      }
+                  }
+
+                if (p != array_end)
+                  return DBUS_INVALID_ARRAY_LENGTH_INCORRECT;
+              }
+
+            /* check nul termination */
+            if (current_type != DBUS_TYPE_ARRAY)
+              {
+                if (p == end)
+                  return DBUS_INVALID_NOT_ENOUGH_DATA;
+
+                if (*p != '\0')
+                  return DBUS_INVALID_STRING_MISSING_NUL;
+                ++p;
+              }
+          }
+          break;
+
+        case DBUS_TYPE_SIGNATURE:
+          {
+            dbus_uint32_t claimed_len;
+            DBusString str;
+            DBusValidity validity;
+
+            claimed_len = *p;
+            ++p;
+
+            /* 1 is for nul termination */
+            if (claimed_len + 1 > (unsigned long) (end - p))
+              return DBUS_INVALID_SIGNATURE_LENGTH_OUT_OF_BOUNDS;
+
+            _dbus_string_init_const_len (&str, p, claimed_len);
+            validity =
+              _dbus_validate_signature_with_reason (&str, 0,
+                                                    _dbus_string_get_length (&str));
+
+            if (validity != DBUS_VALID)
+              return validity;
+
+            p += claimed_len;
+
+            _dbus_assert (p < end);
+            if (*p != DBUS_TYPE_INVALID)
+              return DBUS_INVALID_SIGNATURE_MISSING_NUL;
+
+            ++p;
+
+            _dbus_verbose ("p = %p end = %p claimed_len %u\n", p, end, claimed_len);
+          }
+          break;
+
+        case DBUS_TYPE_VARIANT:
+          {
+            /* 1 byte sig len, sig typecodes, align to
+             * contained-type-boundary, values.
+             */
+
+            /* In addition to normal signature validation, we need to be sure
+             * the signature contains only a single (possibly container) type.
+             */
+            dbus_uint32_t claimed_len;
+            DBusString sig;
+            DBusTypeReader sub;
+            DBusValidity validity;
+            int contained_alignment;
+            int contained_type;
+            DBusValidity reason;
+
+            claimed_len = *p;
+            ++p;
+
+            /* + 1 for nul */
+            if (claimed_len + 1 > (unsigned long) (end - p))
+              return DBUS_INVALID_VARIANT_SIGNATURE_LENGTH_OUT_OF_BOUNDS;
+
+            _dbus_string_init_const_len (&sig, p, claimed_len);
+            reason = _dbus_validate_signature_with_reason (&sig, 0,
+                                           _dbus_string_get_length (&sig));
+            if (!(reason == DBUS_VALID))
+              {
+                if (reason == DBUS_VALIDITY_UNKNOWN_OOM_ERROR)
+                  return reason;
+                else 
+                  return DBUS_INVALID_VARIANT_SIGNATURE_BAD;
+              }
+
+            p += claimed_len;
+            
+            if (*p != DBUS_TYPE_INVALID)
+              return DBUS_INVALID_VARIANT_SIGNATURE_MISSING_NUL;
+            ++p;
+
+            contained_type = _dbus_first_type_in_signature (&sig, 0);
+            if (contained_type == DBUS_TYPE_INVALID)
+              return DBUS_INVALID_VARIANT_SIGNATURE_EMPTY;
+            
+            contained_alignment = _dbus_type_get_alignment (contained_type);
+            
+            a = _DBUS_ALIGN_ADDRESS (p, contained_alignment);
+            if (a > end)
+              return DBUS_INVALID_NOT_ENOUGH_DATA;
+            while (p != a)
+              {
+                if (*p != '\0')
+                  return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL;
+                ++p;
+              }
+
+            _dbus_type_reader_init_types_only (&sub, &sig, 0);
+
+            _dbus_assert (_dbus_type_reader_get_current_type (&sub) != DBUS_TYPE_INVALID);
+
+            validity = validate_body_helper (&sub, byte_order, FALSE, p, end, &p);
+            if (validity != DBUS_VALID)
+              return validity;
+
+            if (_dbus_type_reader_next (&sub))
+              return DBUS_INVALID_VARIANT_SIGNATURE_SPECIFIES_MULTIPLE_VALUES;
+
+            _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_INVALID);
+          }
+          break;
+
+        case DBUS_TYPE_DICT_ENTRY:
+        case DBUS_TYPE_STRUCT:
+          {
+            DBusTypeReader sub;
+            DBusValidity validity;
+
+            a = _DBUS_ALIGN_ADDRESS (p, 8);
+            if (a > end)
+              return DBUS_INVALID_NOT_ENOUGH_DATA;
+            while (p != a)
+              {
+                if (*p != '\0')
+                  return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL;
+                ++p;
+              }
+
+            _dbus_type_reader_recurse (reader, &sub);
+
+            validity = validate_body_helper (&sub, byte_order, TRUE, p, end, &p);
+            if (validity != DBUS_VALID)
+              return validity;
+          }
+          break;
+
+        default:
+          _dbus_assert_not_reached ("invalid typecode in supposedly-validated signature");
+          break;
+        }
+
+#if 0
+      _dbus_verbose ("   validated value of type %s type reader %p type_pos %d p %p end %p %d remain\n",
+                     _dbus_type_to_string (current_type), reader, reader->type_pos, p, end,
+                     (int) (end - p));
+#endif
+
+      if (p > end)
+        {
+          _dbus_verbose ("not enough data!!! p = %p end = %p end-p = %d\n",
+                         p, end, (int) (end - p));
+          return DBUS_INVALID_NOT_ENOUGH_DATA;
+        }
+
+      if (walk_reader_to_end)
+        _dbus_type_reader_next (reader);
+      else
+        break;
+    }
+
+  if (new_p)
+    *new_p = p;
+
+  return DBUS_VALID;
+}
+
+/**
+ * Verifies that the range of value_str from value_pos to value_end is
+ * a legitimate value of type expected_signature.  If this function
+ * returns #TRUE, it will be safe to iterate over the values with
+ * #DBusTypeReader. The signature is assumed to be already valid.
+ *
+ * If bytes_remaining is not #NULL, then leftover bytes will be stored
+ * there and #DBUS_VALID returned. If it is #NULL, then
+ * #DBUS_INVALID_TOO_MUCH_DATA will be returned if bytes are left
+ * over.
+ *
+ * @param expected_signature the expected types in the value_str
+ * @param expected_signature_start where in expected_signature is the signature
+ * @param byte_order the byte order
+ * @param bytes_remaining place to store leftover bytes
+ * @param value_str the string containing the body
+ * @param value_pos where the values start
+ * @param len length of values after value_pos
+ * @returns #DBUS_VALID if valid, reason why invalid otherwise
+ */
+DBusValidity
+_dbus_validate_body_with_reason (const DBusString *expected_signature,
+                                 int               expected_signature_start,
+                                 int               byte_order,
+                                 int              *bytes_remaining,
+                                 const DBusString *value_str,
+                                 int               value_pos,
+                                 int               len)
+{
+  DBusTypeReader reader;
+  const unsigned char *p;
+  const unsigned char *end;
+  DBusValidity validity;
+
+  _dbus_assert (len >= 0);
+  _dbus_assert (value_pos >= 0);
+  _dbus_assert (value_pos <= _dbus_string_get_length (value_str) - len);
+
+  _dbus_verbose ("validating body from pos %d len %d sig '%s'\n",
+                 value_pos, len, _dbus_string_get_const_data_len (expected_signature,
+                                                                  expected_signature_start,
+                                                                  0));
+
+  _dbus_type_reader_init_types_only (&reader,
+                                     expected_signature, expected_signature_start);
+
+  p = _dbus_string_get_const_data_len (value_str, value_pos, len);
+  end = p + len;
+
+  validity = validate_body_helper (&reader, byte_order, TRUE, p, end, &p);
+  if (validity != DBUS_VALID)
+    return validity;
+  
+  if (bytes_remaining)
+    {
+      *bytes_remaining = end - p;
+      return DBUS_VALID;
+    }
+  else if (p < end)
+    return DBUS_INVALID_TOO_MUCH_DATA;
+  else
+    {
+      _dbus_assert (p == end);
+      return DBUS_VALID;
+    }
+}
+
+/**
+ * Determine wether the given character is valid as the first character
+ * in a name.
+ */
+#define VALID_INITIAL_NAME_CHARACTER(c)         \
+  ( ((c) >= 'A' && (c) <= 'Z') ||               \
+    ((c) >= 'a' && (c) <= 'z') ||               \
+    ((c) == '_') )
+
+/**
+ * Determine wether the given character is valid as a second or later
+ * character in a name
+ */
+#define VALID_NAME_CHARACTER(c)                 \
+  ( ((c) >= '0' && (c) <= '9') ||               \
+    ((c) >= 'A' && (c) <= 'Z') ||               \
+    ((c) >= 'a' && (c) <= 'z') ||               \
+    ((c) == '_') )
+
+/**
+ * Checks that the given range of the string is a valid object path
+ * name in the D-Bus protocol. Part of the validation ensures that
+ * the object path contains only ASCII.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ *
+ * @todo change spec to disallow more things, such as spaces in the
+ * path name
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is a valid name
+ */
+dbus_bool_t
+_dbus_validate_path (const DBusString  *str,
+                     int                start,
+                     int                len)
+{
+  const unsigned char *s;
+  const unsigned char *end;
+  const unsigned char *last_slash;
+
+  _dbus_assert (start >= 0);
+  _dbus_assert (len >= 0);
+  _dbus_assert (start <= _dbus_string_get_length (str));
+  
+  if (len > _dbus_string_get_length (str) - start)
+    return FALSE;
+
+  if (len == 0)
+    return FALSE;
+
+  s = _dbus_string_get_const_data (str) + start;
+  end = s + len;
+
+  if (*s != '/')
+    return FALSE;
+  last_slash = s;
+  ++s;
+
+  while (s != end)
+    {
+      if (*s == '/')
+        {
+          if ((s - last_slash) < 2)
+            return FALSE; /* no empty path components allowed */
+
+          last_slash = s;
+        }
+      else
+        {
+          if (_DBUS_UNLIKELY (!VALID_NAME_CHARACTER (*s)))
+            return FALSE;
+        }
+
+      ++s;
+    }
+
+  if ((end - last_slash) < 2 &&
+      len > 1)
+    return FALSE; /* trailing slash not allowed unless the string is "/" */
+
+  return TRUE;
+}
+
+const char *
+_dbus_validity_to_error_message (DBusValidity validity)
+{
+  switch (validity)
+    {
+    case DBUS_VALIDITY_UNKNOWN_OOM_ERROR:                          return "Out of memory";
+    case DBUS_INVALID_FOR_UNKNOWN_REASON:                          return "Unknown reason";
+    case DBUS_VALID_BUT_INCOMPLETE:                                return "Valid but incomplete";
+    case DBUS_VALIDITY_UNKNOWN:                                    return "Validity unknown";
+    case DBUS_VALID:                                               return "Valid";
+    case DBUS_INVALID_UNKNOWN_TYPECODE:                            return "Unknown typecode";
+    case DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE:                  return "Missing array element type";
+    case DBUS_INVALID_SIGNATURE_TOO_LONG:                          return "Signature is too long";
+    case DBUS_INVALID_EXCEEDED_MAXIMUM_ARRAY_RECURSION:            return "Exceeded maximum array recursion";
+    case DBUS_INVALID_EXCEEDED_MAXIMUM_STRUCT_RECURSION:           return "Exceeded maximum struct recursion";
+    case DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED:                return "Struct ended but not started";
+    case DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED:                return "Struct started but not ended";
+    case DBUS_INVALID_STRUCT_HAS_NO_FIELDS:                        return "Struct has no fields";
+    case DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL:                   return "Alignment padding not null";
+    case DBUS_INVALID_BOOLEAN_NOT_ZERO_OR_ONE:                     return "Boolean is not zero or one";
+    case DBUS_INVALID_NOT_ENOUGH_DATA:                             return "Not enough data";
+    case DBUS_INVALID_TOO_MUCH_DATA:                               return "Too much data";
+    case DBUS_INVALID_BAD_BYTE_ORDER:                              return "Bad byte order";
+    case DBUS_INVALID_BAD_PROTOCOL_VERSION:                        return "Bad protocol version";
+    case DBUS_INVALID_BAD_MESSAGE_TYPE:                            return "Bad message type";
+    case DBUS_INVALID_BAD_SERIAL:                                  return "Bad serial";
+    case DBUS_INVALID_INSANE_FIELDS_ARRAY_LENGTH:                  return "Insane fields array length";
+    case DBUS_INVALID_INSANE_BODY_LENGTH:                          return "Insane body length";
+    case DBUS_INVALID_MESSAGE_TOO_LONG:                            return "Message too long";
+    case DBUS_INVALID_HEADER_FIELD_CODE:                           return "Header field code";
+    case DBUS_INVALID_HEADER_FIELD_HAS_WRONG_TYPE:                 return "Header field has wrong type";
+    case DBUS_INVALID_USES_LOCAL_INTERFACE:                        return "Uses local interface";
+    case DBUS_INVALID_USES_LOCAL_PATH:                             return "Uses local path";
+    case DBUS_INVALID_HEADER_FIELD_APPEARS_TWICE:                  return "Header field appears twice";
+    case DBUS_INVALID_BAD_DESTINATION:                             return "Bad destination";
+    case DBUS_INVALID_BAD_INTERFACE:                               return "Bad interface";
+    case DBUS_INVALID_BAD_MEMBER:                                  return "Bad member";
+    case DBUS_INVALID_BAD_ERROR_NAME:                              return "Bad error name";
+    case DBUS_INVALID_BAD_SENDER:                                  return "Bad sender";
+    case DBUS_INVALID_MISSING_PATH:                                return "Missing path";
+    case DBUS_INVALID_MISSING_INTERFACE:                           return "Missing interface";
+    case DBUS_INVALID_MISSING_MEMBER:                              return "Missing member";
+    case DBUS_INVALID_MISSING_ERROR_NAME:                          return "Missing error name";
+    case DBUS_INVALID_MISSING_REPLY_SERIAL:                        return "Missing reply serial";
+    case DBUS_INVALID_LENGTH_OUT_OF_BOUNDS:                        return "Length out of bounds";
+    case DBUS_INVALID_ARRAY_LENGTH_EXCEEDS_MAXIMUM:                return "Array length exceeds maximum";
+    case DBUS_INVALID_BAD_PATH:                                    return "Bad path";
+    case DBUS_INVALID_SIGNATURE_LENGTH_OUT_OF_BOUNDS:              return "Signature length out of bounds";
+    case DBUS_INVALID_BAD_UTF8_IN_STRING:                          return "Bad utf8 in string";
+    case DBUS_INVALID_ARRAY_LENGTH_INCORRECT:                      return "Array length incorrect";
+    case DBUS_INVALID_VARIANT_SIGNATURE_LENGTH_OUT_OF_BOUNDS:      return "Variant signature length out of bounds";
+    case DBUS_INVALID_VARIANT_SIGNATURE_BAD:                       return "Variant signature bad";
+    case DBUS_INVALID_VARIANT_SIGNATURE_EMPTY:                     return "Variant signature empty";
+    case DBUS_INVALID_VARIANT_SIGNATURE_SPECIFIES_MULTIPLE_VALUES: return "Variant signature specifies multiple values";
+    case DBUS_INVALID_VARIANT_SIGNATURE_MISSING_NUL:               return "Variant signature missing nul";
+    case DBUS_INVALID_STRING_MISSING_NUL:                          return "String missing nul";
+    case DBUS_INVALID_SIGNATURE_MISSING_NUL:                       return "Signature missing nul";
+    case DBUS_INVALID_EXCEEDED_MAXIMUM_DICT_ENTRY_RECURSION:       return "Exceeded maximum dict entry recursion";
+    case DBUS_INVALID_DICT_ENTRY_ENDED_BUT_NOT_STARTED:            return "Dict entry ended but not started";
+    case DBUS_INVALID_DICT_ENTRY_STARTED_BUT_NOT_ENDED:            return "Dict entry started but not ended";
+    case DBUS_INVALID_DICT_ENTRY_HAS_NO_FIELDS:                    return "Dict entry has no fields";
+    case DBUS_INVALID_DICT_ENTRY_HAS_ONLY_ONE_FIELD:               return "Dict entry has only one field";
+    case DBUS_INVALID_DICT_ENTRY_HAS_TOO_MANY_FIELDS:              return "Dict entry has too many fields";
+    case DBUS_INVALID_DICT_ENTRY_NOT_INSIDE_ARRAY:                 return "Dict entry not inside array";
+    case DBUS_INVALID_DICT_KEY_MUST_BE_BASIC_TYPE:                 return "Dict key must be basic type";
+
+    default:
+      return "Invalid";
+    }
+}
+
+/**
+ * Checks that the given range of the string is a valid interface name
+ * in the D-Bus protocol. This includes a length restriction and an
+ * ASCII subset, see the specification.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is a valid name
+ */
+dbus_bool_t
+_dbus_validate_interface (const DBusString  *str,
+                          int                start,
+                          int                len)
+{
+  const unsigned char *s;
+  const unsigned char *end;
+  const unsigned char *iface;
+  const unsigned char *last_dot;
+
+  _dbus_assert (start >= 0);
+  _dbus_assert (len >= 0);
+  _dbus_assert (start <= _dbus_string_get_length (str));
+
+  if (len > _dbus_string_get_length (str) - start)
+    return FALSE;
+
+  if (len > DBUS_MAXIMUM_NAME_LENGTH)
+    return FALSE;
+
+  if (len == 0)
+    return FALSE;
+
+  last_dot = NULL;
+  iface = _dbus_string_get_const_data (str) + start;
+  end = iface + len;
+  s = iface;
+
+  /* check special cases of first char so it doesn't have to be done
+   * in the loop. Note we know len > 0
+   */
+  if (_DBUS_UNLIKELY (*s == '.')) /* disallow starting with a . */
+    return FALSE;
+  else if (_DBUS_UNLIKELY (!VALID_INITIAL_NAME_CHARACTER (*s)))
+    return FALSE;
+  else
+    ++s;
+
+  while (s != end)
+    {
+      if (*s == '.')
+        {
+          if (_DBUS_UNLIKELY ((s + 1) == end))
+            return FALSE;
+          else if (_DBUS_UNLIKELY (!VALID_INITIAL_NAME_CHARACTER (*(s + 1))))
+            return FALSE;
+          last_dot = s;
+          ++s; /* we just validated the next char, so skip two */
+        }
+      else if (_DBUS_UNLIKELY (!VALID_NAME_CHARACTER (*s)))
+        {
+          return FALSE;
+        }
+
+      ++s;
+    }
+
+  if (_DBUS_UNLIKELY (last_dot == NULL))
+    return FALSE;
+
+  return TRUE;
+}
+
+/**
+ * Checks that the given range of the string is a valid member name
+ * in the D-Bus protocol. This includes a length restriction, etc.,
+ * see the specification.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is a valid name
+ */
+dbus_bool_t
+_dbus_validate_member (const DBusString  *str,
+                       int                start,
+                       int                len)
+{
+  const unsigned char *s;
+  const unsigned char *end;
+  const unsigned char *member;
+
+  _dbus_assert (start >= 0);
+  _dbus_assert (len >= 0);
+  _dbus_assert (start <= _dbus_string_get_length (str));
+
+  if (len > _dbus_string_get_length (str) - start)
+    return FALSE;
+
+  if (len > DBUS_MAXIMUM_NAME_LENGTH)
+    return FALSE;
+
+  if (len == 0)
+    return FALSE;
+
+  member = _dbus_string_get_const_data (str) + start;
+  end = member + len;
+  s = member;
+
+  /* check special cases of first char so it doesn't have to be done
+   * in the loop. Note we know len > 0
+   */
+
+  if (_DBUS_UNLIKELY (!VALID_INITIAL_NAME_CHARACTER (*s)))
+    return FALSE;
+  else
+    ++s;
+
+  while (s != end)
+    {
+      if (_DBUS_UNLIKELY (!VALID_NAME_CHARACTER (*s)))
+        {
+          return FALSE;
+        }
+
+      ++s;
+    }
+
+  return TRUE;
+}
+
+/**
+ * Checks that the given range of the string is a valid error name
+ * in the D-Bus protocol. This includes a length restriction, etc.,
+ * see the specification.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is a valid name
+ */
+dbus_bool_t
+_dbus_validate_error_name (const DBusString  *str,
+                           int                start,
+                           int                len)
+{
+  /* Same restrictions as interface name at the moment */
+  return _dbus_validate_interface (str, start, len);
+}
+
+/**
+ * Determine wether the given character is valid as the first character
+ * in a bus name.
+ */
+#define VALID_INITIAL_BUS_NAME_CHARACTER(c)         \
+  ( ((c) >= 'A' && (c) <= 'Z') ||               \
+    ((c) >= 'a' && (c) <= 'z') ||               \
+    ((c) == '_') || ((c) == '-'))
+
+/**
+ * Determine wether the given character is valid as a second or later
+ * character in a bus name
+ */
+#define VALID_BUS_NAME_CHARACTER(c)                 \
+  ( ((c) >= '0' && (c) <= '9') ||               \
+    ((c) >= 'A' && (c) <= 'Z') ||               \
+    ((c) >= 'a' && (c) <= 'z') ||               \
+    ((c) == '_') || ((c) == '-'))
+
+/**
+ * Checks that the given range of the string is a valid bus name in
+ * the D-Bus protocol. This includes a length restriction, etc., see
+ * the specification.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is a valid name
+ */
+dbus_bool_t
+_dbus_validate_bus_name (const DBusString  *str,
+                         int                start,
+                         int                len)
+{
+  const unsigned char *s;
+  const unsigned char *end;
+  const unsigned char *iface;
+  const unsigned char *last_dot;
+
+  _dbus_assert (start >= 0);
+  _dbus_assert (len >= 0);
+  _dbus_assert (start <= _dbus_string_get_length (str));
+
+  if (len > _dbus_string_get_length (str) - start)
+    return FALSE;
+
+  if (len > DBUS_MAXIMUM_NAME_LENGTH)
+    return FALSE;
+
+  if (len == 0)
+    return FALSE;
+
+  last_dot = NULL;
+  iface = _dbus_string_get_const_data (str) + start;
+  end = iface + len;
+  s = iface;
+
+  /* check special cases of first char so it doesn't have to be done
+   * in the loop. Note we know len > 0
+   */
+  if (*s == ':')
+  {
+    /* unique name */
+    ++s;
+    while (s != end)
+      {
+        if (*s == '.')
+          {
+            if (_DBUS_UNLIKELY ((s + 1) == end))
+              return FALSE;
+            if (_DBUS_UNLIKELY (!VALID_BUS_NAME_CHARACTER (*(s + 1))))
+              return FALSE;
+            ++s; /* we just validated the next char, so skip two */
+          }
+        else if (_DBUS_UNLIKELY (!VALID_BUS_NAME_CHARACTER (*s)))
+          {
+            return FALSE;
+          }
+
+        ++s;
+      }
+
+    return TRUE;
+  }
+  else if (_DBUS_UNLIKELY (*s == '.')) /* disallow starting with a . */
+    return FALSE;
+  else if (_DBUS_UNLIKELY (!VALID_INITIAL_BUS_NAME_CHARACTER (*s)))
+    return FALSE;
+  else
+    ++s;
+
+  while (s != end)
+    {
+      if (*s == '.')
+        {
+          if (_DBUS_UNLIKELY ((s + 1) == end))
+            return FALSE;
+          else if (_DBUS_UNLIKELY (!VALID_INITIAL_BUS_NAME_CHARACTER (*(s + 1))))
+            return FALSE;
+          last_dot = s;
+          ++s; /* we just validated the next char, so skip two */
+        }
+      else if (_DBUS_UNLIKELY (!VALID_BUS_NAME_CHARACTER (*s)))
+        {
+          return FALSE;
+        }
+
+      ++s;
+    }
+
+  if (_DBUS_UNLIKELY (last_dot == NULL))
+    return FALSE;
+
+  return TRUE;
+}
+
+/**
+ * Checks that the given range of the string is a valid message type
+ * signature in the D-Bus protocol.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is a valid signature
+ */
+dbus_bool_t
+_dbus_validate_signature (const DBusString  *str,
+                          int                start,
+                          int                len)
+{
+  _dbus_assert (start >= 0);
+  _dbus_assert (start <= _dbus_string_get_length (str));
+  _dbus_assert (len >= 0);
+
+  if (len > _dbus_string_get_length (str) - start)
+    return FALSE;
+
+  return _dbus_validate_signature_with_reason (str, start, len) == DBUS_VALID;
+}
+
+/** define _dbus_check_is_valid_path() */
+DEFINE_DBUS_NAME_CHECK(path)
+/** define _dbus_check_is_valid_interface() */
+DEFINE_DBUS_NAME_CHECK(interface)
+/** define _dbus_check_is_valid_member() */
+DEFINE_DBUS_NAME_CHECK(member)
+/** define _dbus_check_is_valid_error_name() */
+DEFINE_DBUS_NAME_CHECK(error_name)
+/** define _dbus_check_is_valid_bus_name() */
+DEFINE_DBUS_NAME_CHECK(bus_name)
+/** define _dbus_check_is_valid_signature() */
+DEFINE_DBUS_NAME_CHECK(signature)
+
+/** @} */
+
+/* tests in dbus-marshal-validate-util.c */
diff --git a/src/dbus/dbus-marshal-validate.h b/src/dbus/dbus-marshal-validate.h
new file mode 100644 (file)
index 0000000..d09acc6
--- /dev/null
@@ -0,0 +1,203 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-validate.h  Validation routines for marshaled data
+ *
+ * Copyright (C) 2005  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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_MARSHAL_VALIDATE_H
+#define DBUS_MARSHAL_VALIDATE_H
+
+#include <config.h>
+
+#ifndef PACKAGE
+#error "config.h not included here"
+#endif
+
+/**
+ * @addtogroup DBusMarshal
+ *
+ * @{
+ */
+
+/**
+ * This is used rather than a bool for high visibility
+ */
+typedef enum
+{
+  DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY,
+  DBUS_VALIDATION_MODE_DATA_IS_UNTRUSTED
+} DBusValidationMode;
+
+/**
+ * This is primarily used in unit testing, so we can verify that each
+ * invalid message is invalid for the expected reasons. Thus we really
+ * want a distinct enum value for every codepath leaving the validator
+ * functions. Enum values are specified manually for ease of debugging
+ * (so you can see the enum value given a printf)
+ */
+typedef enum
+{
+#define _DBUS_NEGATIVE_VALIDITY_COUNT 4
+  DBUS_VALIDITY_UNKNOWN_OOM_ERROR = -4, /**< can't determine validity due to OOM */
+  DBUS_INVALID_FOR_UNKNOWN_REASON = -3,
+  DBUS_VALID_BUT_INCOMPLETE = -2,
+  DBUS_VALIDITY_UNKNOWN = -1,
+  DBUS_VALID = 0, /**< the data is valid */
+  DBUS_INVALID_UNKNOWN_TYPECODE = 1,
+  DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE = 2,
+  DBUS_INVALID_SIGNATURE_TOO_LONG = 3, /* this one is impossible right now since
+                                        * you can't put a too-long value in a byte
+                                        */
+  DBUS_INVALID_EXCEEDED_MAXIMUM_ARRAY_RECURSION = 4,
+  DBUS_INVALID_EXCEEDED_MAXIMUM_STRUCT_RECURSION = 5,
+  DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED = 6,
+  DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED = 7,
+  DBUS_INVALID_STRUCT_HAS_NO_FIELDS = 8,
+  DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL = 9,
+  DBUS_INVALID_BOOLEAN_NOT_ZERO_OR_ONE = 10,
+  DBUS_INVALID_NOT_ENOUGH_DATA = 11,
+  DBUS_INVALID_TOO_MUCH_DATA = 12, /**< trailing junk makes it invalid */
+  DBUS_INVALID_BAD_BYTE_ORDER = 13,
+  DBUS_INVALID_BAD_PROTOCOL_VERSION = 14,
+  DBUS_INVALID_BAD_MESSAGE_TYPE = 15,
+  DBUS_INVALID_BAD_SERIAL = 16,
+  DBUS_INVALID_INSANE_FIELDS_ARRAY_LENGTH = 17,
+  DBUS_INVALID_INSANE_BODY_LENGTH = 18,
+  DBUS_INVALID_MESSAGE_TOO_LONG = 19,
+  DBUS_INVALID_HEADER_FIELD_CODE = 20,
+  DBUS_INVALID_HEADER_FIELD_HAS_WRONG_TYPE = 21,
+  DBUS_INVALID_USES_LOCAL_INTERFACE = 22,
+  DBUS_INVALID_USES_LOCAL_PATH = 23,
+  DBUS_INVALID_HEADER_FIELD_APPEARS_TWICE = 24,
+  DBUS_INVALID_BAD_DESTINATION = 25,
+  DBUS_INVALID_BAD_INTERFACE = 26,
+  DBUS_INVALID_BAD_MEMBER = 27,
+  DBUS_INVALID_BAD_ERROR_NAME = 28,
+  DBUS_INVALID_BAD_SENDER = 29,
+  DBUS_INVALID_MISSING_PATH = 30,
+  DBUS_INVALID_MISSING_INTERFACE = 31,
+  DBUS_INVALID_MISSING_MEMBER = 32,
+  DBUS_INVALID_MISSING_ERROR_NAME = 33,
+  DBUS_INVALID_MISSING_REPLY_SERIAL = 34,
+  DBUS_INVALID_LENGTH_OUT_OF_BOUNDS = 35,
+  DBUS_INVALID_ARRAY_LENGTH_EXCEEDS_MAXIMUM = 36,
+  DBUS_INVALID_BAD_PATH = 37,
+  DBUS_INVALID_SIGNATURE_LENGTH_OUT_OF_BOUNDS = 38,
+  DBUS_INVALID_BAD_UTF8_IN_STRING = 39,
+  DBUS_INVALID_ARRAY_LENGTH_INCORRECT = 40,
+  DBUS_INVALID_VARIANT_SIGNATURE_LENGTH_OUT_OF_BOUNDS = 41,
+  DBUS_INVALID_VARIANT_SIGNATURE_BAD = 42,
+  DBUS_INVALID_VARIANT_SIGNATURE_EMPTY = 43,
+  DBUS_INVALID_VARIANT_SIGNATURE_SPECIFIES_MULTIPLE_VALUES = 44,
+  DBUS_INVALID_VARIANT_SIGNATURE_MISSING_NUL = 45,
+  DBUS_INVALID_STRING_MISSING_NUL = 46,
+  DBUS_INVALID_SIGNATURE_MISSING_NUL = 47,
+  DBUS_INVALID_EXCEEDED_MAXIMUM_DICT_ENTRY_RECURSION = 48,
+  DBUS_INVALID_DICT_ENTRY_ENDED_BUT_NOT_STARTED = 49,
+  DBUS_INVALID_DICT_ENTRY_STARTED_BUT_NOT_ENDED = 50,
+  DBUS_INVALID_DICT_ENTRY_HAS_NO_FIELDS = 51,
+  DBUS_INVALID_DICT_ENTRY_HAS_ONLY_ONE_FIELD = 52,
+  DBUS_INVALID_DICT_ENTRY_HAS_TOO_MANY_FIELDS = 53,
+  DBUS_INVALID_DICT_ENTRY_NOT_INSIDE_ARRAY = 54,
+  DBUS_INVALID_DICT_KEY_MUST_BE_BASIC_TYPE = 55,
+  DBUS_VALIDITY_LAST
+} DBusValidity;
+
+DBusValidity _dbus_validate_signature_with_reason (const DBusString *type_str,
+                                                   int               type_pos,
+                                                   int               len);
+DBusValidity _dbus_validate_body_with_reason      (const DBusString *expected_signature,
+                                                   int               expected_signature_start,
+                                                   int               byte_order,
+                                                   int              *bytes_remaining,
+                                                   const DBusString *value_str,
+                                                   int               value_pos,
+                                                   int               len);
+
+const char *_dbus_validity_to_error_message (DBusValidity validity);
+
+dbus_bool_t _dbus_validate_path       (const DBusString *str,
+                                       int               start,
+                                       int               len);
+dbus_bool_t _dbus_validate_interface  (const DBusString *str,
+                                       int               start,
+                                       int               len);
+dbus_bool_t _dbus_validate_member     (const DBusString *str,
+                                       int               start,
+                                       int               len);
+dbus_bool_t _dbus_validate_error_name (const DBusString *str,
+                                       int               start,
+                                       int               len);
+dbus_bool_t _dbus_validate_bus_name   (const DBusString *str,
+                                       int               start,
+                                       int               len);
+dbus_bool_t _dbus_validate_signature  (const DBusString *str,
+                                       int               start,
+                                       int               len);
+
+#ifdef DBUS_DISABLE_CHECKS
+
+/* Be sure they don't exist, since we don't want to use them outside of checks
+ * and so we want the compile failure.
+ */
+#define DECLARE_DBUS_NAME_CHECK(what)
+#define DEFINE_DBUS_NAME_CHECK(what)
+
+#else /* !DBUS_DISABLE_CHECKS */
+
+/** A name check is used in _dbus_return_if_fail(), it's not suitable
+ * for validating untrusted data. use _dbus_validate_whatever for that.
+ */
+#define DECLARE_DBUS_NAME_CHECK(what) \
+dbus_bool_t _dbus_check_is_valid_##what (const char *name)
+
+/** Define a name check to be used in _dbus_return_if_fail() statements.
+ */
+#define DEFINE_DBUS_NAME_CHECK(what)                                    \
+dbus_bool_t                                                             \
+_dbus_check_is_valid_##what (const char *name)                          \
+{                                                                       \
+  DBusString str;                                                       \
+                                                                        \
+  if (name == NULL)                                                     \
+    return FALSE;                                                       \
+                                                                        \
+  _dbus_string_init_const (&str, name);                                 \
+  return _dbus_validate_##what (&str, 0,                                \
+                                _dbus_string_get_length (&str));        \
+}
+#endif /* !DBUS_DISABLE_CHECKS */
+
+/** defines _dbus_check_is_valid_path() */
+DECLARE_DBUS_NAME_CHECK(path);
+/** defines _dbus_check_is_valid_interface() */
+DECLARE_DBUS_NAME_CHECK(interface);
+/** defines _dbus_check_is_valid_member() */
+DECLARE_DBUS_NAME_CHECK(member);
+/** defines _dbus_check_is_valid_error_name() */
+DECLARE_DBUS_NAME_CHECK(error_name);
+/** defines _dbus_check_is_valid_bus_name() */
+DECLARE_DBUS_NAME_CHECK(bus_name);
+/** defines _dbus_check_is_valid_signature() */
+DECLARE_DBUS_NAME_CHECK(signature);
+
+/** @} */
+
+#endif /* DBUS_MARSHAL_VALIDATE_H */
diff --git a/src/dbus/dbus-memory.c b/src/dbus/dbus-memory.c
new file mode 100644 (file)
index 0000000..8dc9147
--- /dev/null
@@ -0,0 +1,842 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-memory.c  D-Bus memory handling
+ *
+ * Copyright (C) 2002, 2003  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-memory.h"
+#include "dbus-internals.h"
+#include "dbus-sysdeps.h"
+#include "dbus-list.h"
+#include <stdlib.h>
+
+/**
+ * @defgroup DBusMemory Memory Allocation
+ * @ingroup  DBus
+ * @brief dbus_malloc(), dbus_free(), etc.
+ *
+ * Functions and macros related to allocating and releasing
+ * blocks of memory.
+ *
+ */
+
+/**
+ * @defgroup DBusMemoryInternals Memory allocation implementation details
+ * @ingroup  DBusInternals
+ * @brief internals of dbus_malloc() etc.
+ *
+ * Implementation details related to allocating and releasing blocks
+ * of memory.
+ */
+
+/**
+ * @addtogroup DBusMemory
+ *
+ * @{
+ */
+
+/**
+ * @def dbus_new
+ *
+ * Safe macro for using dbus_malloc(). Accepts the type
+ * to allocate and the number of type instances to
+ * allocate as arguments, and returns a memory block
+ * cast to the desired type, instead of as a void*.
+ *
+ * @param type type name to allocate
+ * @param count number of instances in the allocated array
+ * @returns the new memory block or #NULL on failure
+ */
+
+/**
+ * @def dbus_new0
+ *
+ * Safe macro for using dbus_malloc0(). Accepts the type
+ * to allocate and the number of type instances to
+ * allocate as arguments, and returns a memory block
+ * cast to the desired type, instead of as a void*.
+ * The allocated array is initialized to all-bits-zero.
+ *
+ * @param type type name to allocate
+ * @param count number of instances in the allocated array
+ * @returns the new memory block or #NULL on failure
+ */
+
+/**
+ * @typedef DBusFreeFunction
+ *
+ * The type of a function which frees a block of memory.
+ *
+ * @param memory the memory to free
+ */
+
+/** @} */ /* end of public API docs */
+
+/**
+ * @addtogroup DBusMemoryInternals
+ *
+ * @{
+ */
+
+#ifdef DBUS_BUILD_TESTS
+static dbus_bool_t debug_initialized = FALSE;
+static int fail_nth = -1;
+static size_t fail_size = 0;
+static int fail_alloc_counter = _DBUS_INT_MAX;
+static int n_failures_per_failure = 1;
+static int n_failures_this_failure = 0;
+static dbus_bool_t guards = FALSE;
+static dbus_bool_t disable_mem_pools = FALSE;
+static dbus_bool_t backtrace_on_fail_alloc = FALSE;
+static DBusAtomic n_blocks_outstanding = {0};
+
+/** value stored in guard padding for debugging buffer overrun */
+#define GUARD_VALUE 0xdeadbeef
+/** size of the information about the block stored in guard mode */
+#define GUARD_INFO_SIZE 8
+/** size of the GUARD_VALUE-filled padding after the header info  */
+#define GUARD_START_PAD 16
+/** size of the GUARD_VALUE-filled padding at the end of the block */
+#define GUARD_END_PAD 16
+/** size of stuff at start of block */
+#define GUARD_START_OFFSET (GUARD_START_PAD + GUARD_INFO_SIZE)
+/** total extra size over the requested allocation for guard stuff */
+#define GUARD_EXTRA_SIZE (GUARD_START_OFFSET + GUARD_END_PAD)
+
+static void
+_dbus_initialize_malloc_debug (void)
+{
+  if (!debug_initialized)
+    {
+      debug_initialized = TRUE;
+      
+      if (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH") != NULL)
+       {
+         fail_nth = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH"));
+          fail_alloc_counter = fail_nth;
+          _dbus_verbose ("Will fail malloc every %d times\n", fail_nth);
+       }
+      
+      if (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN") != NULL)
+        {
+          fail_size = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN"));
+          _dbus_verbose ("Will fail mallocs over %ld bytes\n",
+                         (long) fail_size);
+        }
+
+      if (_dbus_getenv ("DBUS_MALLOC_GUARDS") != NULL)
+        {
+          guards = TRUE;
+          _dbus_verbose ("Will use malloc guards\n");
+        }
+
+      if (_dbus_getenv ("DBUS_DISABLE_MEM_POOLS") != NULL)
+        {
+          disable_mem_pools = TRUE;
+          _dbus_verbose ("Will disable memory pools\n");
+        }
+
+      if (_dbus_getenv ("DBUS_MALLOC_BACKTRACES") != NULL)
+        {
+          backtrace_on_fail_alloc = TRUE;
+          _dbus_verbose ("Will backtrace on failing a malloc\n");
+        }
+    }
+}
+
+/**
+ * Whether to turn off mem pools, useful for leak checking.
+ *
+ * @returns #TRUE if mempools should not be used.
+ */
+dbus_bool_t
+_dbus_disable_mem_pools (void)
+{
+  _dbus_initialize_malloc_debug ();
+  return disable_mem_pools;
+}
+
+/**
+ * Sets the number of allocations until we simulate a failed
+ * allocation. If set to 0, the next allocation to run
+ * fails; if set to 1, one succeeds then the next fails; etc.
+ * Set to _DBUS_INT_MAX to not fail anything. 
+ *
+ * @param until_next_fail number of successful allocs before one fails
+ */
+void
+_dbus_set_fail_alloc_counter (int until_next_fail)
+{
+  _dbus_initialize_malloc_debug ();
+
+  fail_alloc_counter = until_next_fail;
+
+#if 0
+  _dbus_verbose ("Set fail alloc counter = %d\n", fail_alloc_counter);
+#endif
+}
+
+/**
+ * Gets the number of successful allocs until we'll simulate
+ * a failed alloc.
+ *
+ * @returns current counter value
+ */
+int
+_dbus_get_fail_alloc_counter (void)
+{
+  _dbus_initialize_malloc_debug ();
+
+  return fail_alloc_counter;
+}
+
+/**
+ * Sets how many mallocs to fail when the fail alloc counter reaches
+ * 0.
+ *
+ * @param failures_per_failure number to fail
+ */
+void
+_dbus_set_fail_alloc_failures (int failures_per_failure)
+{
+  n_failures_per_failure = failures_per_failure;
+}
+
+/**
+ * Gets the number of failures we'll have when the fail malloc
+ * counter reaches 0.
+ *
+ * @returns number of failures planned
+ */
+int
+_dbus_get_fail_alloc_failures (void)
+{
+  return n_failures_per_failure;
+}
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Called when about to alloc some memory; if
+ * it returns #TRUE, then the allocation should
+ * fail. If it returns #FALSE, then the allocation
+ * should not fail.
+ *
+ * @returns #TRUE if this alloc should fail
+ */
+dbus_bool_t
+_dbus_decrement_fail_alloc_counter (void)
+{
+  _dbus_initialize_malloc_debug ();
+  
+  if (fail_alloc_counter <= 0)
+    {
+      if (backtrace_on_fail_alloc)
+        _dbus_print_backtrace ();
+
+      _dbus_verbose ("failure %d\n", n_failures_this_failure);
+      
+      n_failures_this_failure += 1;
+      if (n_failures_this_failure >= n_failures_per_failure)
+        {
+          if (fail_nth >= 0)
+            fail_alloc_counter = fail_nth;
+          else
+            fail_alloc_counter = _DBUS_INT_MAX;
+
+          n_failures_this_failure = 0;
+
+          _dbus_verbose ("reset fail alloc counter to %d\n", fail_alloc_counter);
+        }
+      
+      return TRUE;
+    }
+  else
+    {
+      fail_alloc_counter -= 1;
+      return FALSE;
+    }
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * Get the number of outstanding malloc()'d blocks.
+ *
+ * @returns number of blocks
+ */
+int
+_dbus_get_malloc_blocks_outstanding (void)
+{
+  return n_blocks_outstanding.value;
+}
+
+/**
+ * Where the block came from.
+ */
+typedef enum
+{
+  SOURCE_UNKNOWN,
+  SOURCE_MALLOC,
+  SOURCE_REALLOC,
+  SOURCE_MALLOC_ZERO,
+  SOURCE_REALLOC_NULL
+} BlockSource;
+
+static const char*
+source_string (BlockSource source)
+{
+  switch (source)
+    {
+    case SOURCE_UNKNOWN:
+      return "unknown";
+    case SOURCE_MALLOC:
+      return "malloc";
+    case SOURCE_REALLOC:
+      return "realloc";
+    case SOURCE_MALLOC_ZERO:
+      return "malloc0";
+    case SOURCE_REALLOC_NULL:
+      return "realloc(NULL)";
+    }
+  _dbus_assert_not_reached ("Invalid malloc block source ID");
+  return "invalid!";
+}
+
+static void
+check_guards (void       *free_block,
+              dbus_bool_t overwrite)
+{
+  if (free_block != NULL)
+    {
+      unsigned char *block = ((unsigned char*)free_block) - GUARD_START_OFFSET;
+      size_t requested_bytes = *(dbus_uint32_t*)block;
+      BlockSource source = *(dbus_uint32_t*)(block + 4);
+      unsigned int i;
+      dbus_bool_t failed;
+
+      failed = FALSE;
+
+#if 0
+      _dbus_verbose ("Checking %d bytes request from source %s\n",
+                     requested_bytes, source_string (source));
+#endif
+      
+      i = GUARD_INFO_SIZE;
+      while (i < GUARD_START_OFFSET)
+        {
+          dbus_uint32_t value = *(dbus_uint32_t*) &block[i];
+          if (value != GUARD_VALUE)
+            {
+              _dbus_warn ("Block of %lu bytes from %s had start guard value 0x%ux at %d expected 0x%x\n",
+                          (long) requested_bytes, source_string (source),
+                          value, i, GUARD_VALUE);
+              failed = TRUE;
+            }
+          
+          i += 4;
+        }
+
+      i = GUARD_START_OFFSET + requested_bytes;
+      while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD))
+        {
+          dbus_uint32_t value = *(dbus_uint32_t*) &block[i];
+          if (value != GUARD_VALUE)
+            {
+              _dbus_warn ("Block of %lu bytes from %s had end guard value 0x%ux at %d expected 0x%x\n",
+                          (long) requested_bytes, source_string (source),
+                          value, i, GUARD_VALUE);
+              failed = TRUE;
+            }
+          
+          i += 4;
+        }
+
+      /* set memory to anything but nul bytes */
+      if (overwrite)
+        memset (free_block, 'g', requested_bytes);
+      
+      if (failed)
+        _dbus_assert_not_reached ("guard value corruption");
+    }
+}
+
+static void*
+set_guards (void       *real_block,
+            size_t      requested_bytes,
+            BlockSource source)
+{
+  unsigned char *block = real_block;
+  unsigned int i;
+  
+  if (block == NULL)
+    return NULL;
+
+  _dbus_assert (GUARD_START_OFFSET + GUARD_END_PAD == GUARD_EXTRA_SIZE);
+  
+  *((dbus_uint32_t*)block) = requested_bytes;
+  *((dbus_uint32_t*)(block + 4)) = source;
+
+  i = GUARD_INFO_SIZE;
+  while (i < GUARD_START_OFFSET)
+    {
+      (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE;
+      
+      i += 4;
+    }
+
+  i = GUARD_START_OFFSET + requested_bytes;
+  while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD))
+    {
+      (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE;
+      
+      i += 4;
+    }
+  
+  check_guards (block + GUARD_START_OFFSET, FALSE);
+  
+  return block + GUARD_START_OFFSET;
+}
+
+#endif
+
+/** @} */ /* End of internals docs */
+
+
+/**
+ * @addtogroup DBusMemory
+ *
+ * @{
+ */
+
+/**
+ * Allocates the given number of bytes, as with standard
+ * malloc(). Guaranteed to return #NULL if bytes is zero
+ * on all platforms. Returns #NULL if the allocation fails.
+ * The memory must be released with dbus_free().
+ *
+ * dbus_malloc() memory is NOT safe to free with regular free() from
+ * the C library. Free it with dbus_free() only.
+ *
+ * @param bytes number of bytes to allocate
+ * @return allocated memory, or #NULL if the allocation fails.
+ */
+void*
+dbus_malloc (size_t bytes)
+{
+#ifdef DBUS_BUILD_TESTS
+  _dbus_initialize_malloc_debug ();
+  
+  if (_dbus_decrement_fail_alloc_counter ())
+    {
+      _dbus_verbose (" FAILING malloc of %ld bytes\n", (long) bytes);
+      return NULL;
+    }
+#endif
+
+  if (bytes == 0) /* some system mallocs handle this, some don't */
+    return NULL;
+#ifdef DBUS_BUILD_TESTS
+  else if (fail_size != 0 && bytes > fail_size)
+    return NULL;
+  else if (guards)
+    {
+      void *block;
+
+      block = malloc (bytes + GUARD_EXTRA_SIZE);
+      if (block)
+       _dbus_atomic_inc (&n_blocks_outstanding);
+      
+      return set_guards (block, bytes, SOURCE_MALLOC);
+    }
+#endif
+  else
+    {
+      void *mem;
+      mem = malloc (bytes);
+#ifdef DBUS_BUILD_TESTS
+      if (mem)
+       _dbus_atomic_inc (&n_blocks_outstanding);
+#endif
+      return mem;
+    }
+}
+
+/**
+ * Allocates the given number of bytes, as with standard malloc(), but
+ * all bytes are initialized to zero as with calloc(). Guaranteed to
+ * return #NULL if bytes is zero on all platforms. Returns #NULL if the
+ * allocation fails.  The memory must be released with dbus_free().
+ *
+ * dbus_malloc0() memory is NOT safe to free with regular free() from
+ * the C library. Free it with dbus_free() only.
+ *
+ * @param bytes number of bytes to allocate
+ * @return allocated memory, or #NULL if the allocation fails.
+ */
+void*
+dbus_malloc0 (size_t bytes)
+{
+#ifdef DBUS_BUILD_TESTS
+  _dbus_initialize_malloc_debug ();
+  
+  if (_dbus_decrement_fail_alloc_counter ())
+    {
+      _dbus_verbose (" FAILING malloc0 of %ld bytes\n", (long) bytes);
+      
+      return NULL;
+    }
+#endif
+  
+  if (bytes == 0)
+    return NULL;
+#ifdef DBUS_BUILD_TESTS
+  else if (fail_size != 0 && bytes > fail_size)
+    return NULL;
+  else if (guards)
+    {
+      void *block;
+
+      block = calloc (bytes + GUARD_EXTRA_SIZE, 1);
+      if (block)
+       _dbus_atomic_inc (&n_blocks_outstanding);
+      return set_guards (block, bytes, SOURCE_MALLOC_ZERO);
+    }
+#endif
+  else
+    {
+      void *mem;
+      mem = calloc (bytes, 1);
+#ifdef DBUS_BUILD_TESTS
+      if (mem)
+       _dbus_atomic_inc (&n_blocks_outstanding);
+#endif
+      return mem;
+    }
+}
+
+/**
+ * Resizes a block of memory previously allocated by dbus_malloc() or
+ * dbus_malloc0(). Guaranteed to free the memory and return #NULL if bytes
+ * is zero on all platforms. Returns #NULL if the resize fails.
+ * If the resize fails, the memory is not freed.
+ *
+ * @param memory block to be resized
+ * @param bytes new size of the memory block
+ * @return allocated memory, or #NULL if the resize fails.
+ */
+void*
+dbus_realloc (void  *memory,
+              size_t bytes)
+{
+#ifdef DBUS_BUILD_TESTS
+  _dbus_initialize_malloc_debug ();
+  
+  if (_dbus_decrement_fail_alloc_counter ())
+    {
+      _dbus_verbose (" FAILING realloc of %ld bytes\n", (long) bytes);
+      
+      return NULL;
+    }
+#endif
+  
+  if (bytes == 0) /* guarantee this is safe */
+    {
+      dbus_free (memory);
+      return NULL;
+    }
+#ifdef DBUS_BUILD_TESTS
+  else if (fail_size != 0 && bytes > fail_size)
+    return NULL;
+  else if (guards)
+    {
+      if (memory)
+        {
+          size_t old_bytes;
+          void *block;
+          
+          check_guards (memory, FALSE);
+          
+          block = realloc (((unsigned char*)memory) - GUARD_START_OFFSET,
+                           bytes + GUARD_EXTRA_SIZE);
+
+         old_bytes = *(dbus_uint32_t*)block;
+          if (block && bytes >= old_bytes)
+            /* old guards shouldn't have moved */
+            check_guards (((unsigned char*)block) + GUARD_START_OFFSET, FALSE);
+          
+          return set_guards (block, bytes, SOURCE_REALLOC);
+        }
+      else
+        {
+          void *block;
+          
+          block = malloc (bytes + GUARD_EXTRA_SIZE);
+
+          if (block)
+           _dbus_atomic_inc (&n_blocks_outstanding);
+          
+          return set_guards (block, bytes, SOURCE_REALLOC_NULL);   
+        }
+    }
+#endif
+  else
+    {
+      void *mem;
+      mem = realloc (memory, bytes);
+#ifdef DBUS_BUILD_TESTS
+      if (memory == NULL && mem != NULL)
+           _dbus_atomic_inc (&n_blocks_outstanding);
+#endif
+      return mem;
+    }
+}
+
+/**
+ * Frees a block of memory previously allocated by dbus_malloc() or
+ * dbus_malloc0(). If passed #NULL, does nothing.
+ * 
+ * @param memory block to be freed
+ */
+void
+dbus_free (void  *memory)
+{
+#ifdef DBUS_BUILD_TESTS
+  if (guards)
+    {
+      check_guards (memory, TRUE);
+      if (memory)
+        {
+         _dbus_atomic_dec (&n_blocks_outstanding);
+          
+         _dbus_assert (n_blocks_outstanding.value >= 0);
+          
+          free (((unsigned char*)memory) - GUARD_START_OFFSET);
+        }
+      
+      return;
+    }
+#endif
+    
+  if (memory) /* we guarantee it's safe to free (NULL) */
+    {
+#ifdef DBUS_BUILD_TESTS
+      _dbus_atomic_dec (&n_blocks_outstanding);
+      
+      _dbus_assert (n_blocks_outstanding.value >= 0);
+#endif
+
+      free (memory);
+    }
+}
+
+/**
+ * Frees a #NULL-terminated array of strings.
+ * If passed #NULL, does nothing.
+ *
+ * @param str_array the array to be freed
+ */
+void
+dbus_free_string_array (char **str_array)
+{
+  if (str_array)
+    {
+      int i;
+
+      i = 0;
+      while (str_array[i])
+       {
+         dbus_free (str_array[i]);
+         i++;
+       }
+
+      dbus_free (str_array);
+    }
+}
+
+/** @} */ /* End of public API docs block */
+
+
+/**
+ * @addtogroup DBusMemoryInternals
+ *
+ * @{
+ */
+
+/**
+ * _dbus_current_generation is used to track each
+ * time that dbus_shutdown() is called, so we can
+ * reinit things after it's been called. It is simply
+ * incremented each time we shut down.
+ */
+int _dbus_current_generation = 1;
+
+/**
+ * Represents a function to be called on shutdown.
+ */
+typedef struct ShutdownClosure ShutdownClosure;
+
+/**
+ * This struct represents a function to be called on shutdown.
+ */
+struct ShutdownClosure
+{
+  ShutdownClosure *next;     /**< Next ShutdownClosure */
+  DBusShutdownFunction func; /**< Function to call */
+  void *data;                /**< Data for function */
+};
+
+_DBUS_DEFINE_GLOBAL_LOCK (shutdown_funcs);
+static ShutdownClosure *registered_globals = NULL;
+
+/**
+ * Register a cleanup function to be called exactly once
+ * the next time dbus_shutdown() is called.
+ *
+ * @param func the function
+ * @param data data to pass to the function
+ * @returns #FALSE on not enough memory
+ */
+dbus_bool_t
+_dbus_register_shutdown_func (DBusShutdownFunction  func,
+                              void                 *data)
+{
+  ShutdownClosure *c;
+
+  c = dbus_new (ShutdownClosure, 1);
+
+  if (c == NULL)
+    return FALSE;
+
+  c->func = func;
+  c->data = data;
+
+  _DBUS_LOCK (shutdown_funcs);
+  
+  c->next = registered_globals;
+  registered_globals = c;
+
+  _DBUS_UNLOCK (shutdown_funcs);
+  
+  return TRUE;
+}
+
+/** @} */ /* End of private API docs block */
+
+
+/**
+ * @addtogroup DBusMemory
+ *
+ * @{
+ */
+
+/**
+ * Frees all memory allocated internally by libdbus and
+ * reverses the effects of dbus_threads_init(). libdbus keeps internal
+ * global variables, for example caches and thread locks, and it
+ * can be useful to free these internal data structures.
+ *
+ * dbus_shutdown() does NOT free memory that was returned
+ * to the application. It only returns libdbus-internal
+ * data structures.
+ *
+ * You MUST free all memory and release all reference counts
+ * returned to you by libdbus prior to calling dbus_shutdown().
+ *
+ * You can't continue to use any D-Bus objects, such as connections,
+ * that were allocated prior to dbus_shutdown(). You can, however,
+ * start over; call dbus_threads_init() again, create new connections,
+ * and so forth.
+ *
+ * WARNING: dbus_shutdown() is NOT thread safe, it must be called
+ * while NO other threads are using D-Bus. (Remember, you have to free
+ * all D-Bus objects and memory before you call dbus_shutdown(), so no
+ * thread can be using libdbus.)
+ *
+ * The purpose of dbus_shutdown() is to allow applications to get
+ * clean output from memory leak checkers. dbus_shutdown() may also be
+ * useful if you want to dlopen() libdbus instead of linking to it,
+ * and want to be able to unload the library again.
+ *
+ * There is absolutely no requirement to call dbus_shutdown() - in fact,
+ * most applications won't bother and should not feel guilty.
+ * 
+ * You have to know that nobody is using libdbus in your application's
+ * process before you can call dbus_shutdown(). One implication of this
+ * is that calling dbus_shutdown() from a library is almost certainly
+ * wrong, since you don't know what the rest of the app is up to.
+ * 
+ */
+void
+dbus_shutdown (void)
+{
+  while (registered_globals != NULL)
+    {
+      ShutdownClosure *c;
+
+      c = registered_globals;
+      registered_globals = c->next;
+      
+      (* c->func) (c->data);
+      
+      dbus_free (c);
+    }
+
+  _dbus_current_generation += 1;
+}
+
+/** @} */ /** End of public API docs block */
+
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-test.h"
+
+/**
+ * @ingroup DBusMemoryInternals
+ * Unit test for DBusMemory
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_memory_test (void)
+{
+  dbus_bool_t old_guards;
+  void *p;
+  size_t size;
+
+  old_guards = guards;
+  guards = TRUE;
+  p = dbus_malloc (4);
+  if (p == NULL)
+    _dbus_assert_not_reached ("no memory");
+  for (size = 4; size < 256; size += 4)
+    {
+      p = dbus_realloc (p, size);
+      if (p == NULL)
+       _dbus_assert_not_reached ("no memory");
+    }
+  for (size = 256; size != 0; size -= 4)
+    {
+      p = dbus_realloc (p, size);
+      if (p == NULL)
+       _dbus_assert_not_reached ("no memory");
+    }
+  dbus_free (p);
+  guards = old_guards;
+  return TRUE;
+}
+
+#endif
diff --git a/src/dbus/dbus-memory.h b/src/dbus/dbus-memory.h
new file mode 100644 (file)
index 0000000..6aab4e2
--- /dev/null
@@ -0,0 +1,59 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-memory.h  D-Bus memory handling
+ *
+ * Copyright (C) 2002  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_MEMORY_H
+#define DBUS_MEMORY_H
+
+#include <dbus/dbus-macros.h>
+#include <stddef.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusMemory
+ * @{
+ */
+
+void* dbus_malloc        (size_t bytes);
+void* dbus_malloc0       (size_t bytes);
+void* dbus_realloc       (void  *memory,
+                          size_t bytes);
+void  dbus_free          (void  *memory);
+
+#define dbus_new(type, count)  ((type*)dbus_malloc (sizeof (type) * (count)));
+#define dbus_new0(type, count) ((type*)dbus_malloc0 (sizeof (type) * (count)));
+
+void dbus_free_string_array (char **str_array);
+
+typedef void (* DBusFreeFunction) (void *memory);
+
+void dbus_shutdown (void);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_MEMORY_H */
diff --git a/src/dbus/dbus-mempool.c b/src/dbus/dbus-mempool.c
new file mode 100644 (file)
index 0000000..f94134d
--- /dev/null
@@ -0,0 +1,577 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-mempool.h Memory pools
+ * 
+ * Copyright (C) 2002, 2003  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-mempool.h"
+#include "dbus-internals.h"
+
+/**
+ * @defgroup DBusMemPool memory pools
+ * @ingroup  DBusInternals
+ * @brief DBusMemPool object
+ *
+ * Types and functions related to DBusMemPool.  A memory pool is used
+ * to decrease memory fragmentation/overhead and increase speed for
+ * blocks of small uniformly-sized objects. The main point is to avoid
+ * the overhead of a malloc block for each small object, speed is
+ * secondary.
+ */
+
+/**
+ * @defgroup DBusMemPoolInternals Memory pool implementation details
+ * @ingroup  DBusInternals
+ * @brief DBusMemPool implementation details
+ *
+ * The guts of DBusMemPool.
+ *
+ * @{
+ */
+
+/**
+ * typedef so DBusFreedElement struct can refer to itself.
+ */
+typedef struct DBusFreedElement DBusFreedElement;
+
+/**
+ * struct representing an element on the free list.
+ * We just cast freed elements to this so we can
+ * make a list out of them.
+ */
+struct DBusFreedElement
+{
+  DBusFreedElement *next; /**< next element of the free list */
+};
+
+/**
+ * The dummy size of the variable-length "elements"
+ * field in DBusMemBlock
+ */
+#define ELEMENT_PADDING 4
+
+/**
+ * Typedef for DBusMemBlock so the struct can recursively
+ * point to itself.
+ */
+typedef struct DBusMemBlock DBusMemBlock;
+
+/**
+ * DBusMemBlock object represents a single malloc()-returned
+ * block that gets chunked up into objects in the memory pool.
+ */
+struct DBusMemBlock
+{
+  DBusMemBlock *next;  /**< next block in the list, which is already used up;
+                        *   only saved so we can free all the blocks
+                        *   when we free the mem pool.
+                        */
+
+  /* this is a long so that "elements" is aligned */
+  long used_so_far;     /**< bytes of this block already allocated as elements. */
+  
+  unsigned char elements[ELEMENT_PADDING]; /**< the block data, actually allocated to required size */
+};
+
+/**
+ * Internals fields of DBusMemPool
+ */
+struct DBusMemPool
+{
+  int element_size;                /**< size of a single object in the pool */
+  int block_size;                  /**< size of most recently allocated block */
+  unsigned int zero_elements : 1;  /**< whether to zero-init allocated elements */
+
+  DBusFreedElement *free_elements; /**< a free list of elements to recycle */
+  DBusMemBlock *blocks;            /**< blocks of memory from malloc() */
+  int allocated_elements;          /**< Count of outstanding allocated elements */
+};
+
+/** @} */
+
+/**
+ * @addtogroup DBusMemPool
+ *
+ * @{
+ */
+
+/**
+ * @typedef DBusMemPool
+ *
+ * Opaque object representing a memory pool. Memory pools allow
+ * avoiding per-malloc-block memory overhead when allocating a lot of
+ * small objects that are all the same size. They are slightly
+ * faster than calling malloc() also.
+ */
+
+/**
+ * Creates a new memory pool, or returns #NULL on failure.  Objects in
+ * the pool must be at least sizeof(void*) bytes each, due to the way
+ * memory pools work. To avoid creating 64 bit problems, this means at
+ * least 8 bytes on all platforms, unless you are 4 bytes on 32-bit
+ * and 8 bytes on 64-bit.
+ *
+ * @param element_size size of an element allocated from the pool.
+ * @param zero_elements whether to zero-initialize elements
+ * @returns the new pool or #NULL
+ */
+DBusMemPool*
+_dbus_mem_pool_new (int element_size,
+                    dbus_bool_t zero_elements)
+{
+  DBusMemPool *pool;
+
+  pool = dbus_new0 (DBusMemPool, 1);
+  if (pool == NULL)
+    return NULL;
+
+  /* Make the element size at least 8 bytes. */
+  if (element_size < 8)
+    element_size = 8;
+  
+  /* these assertions are equivalent but the first is more clear
+   * to programmers that see it fail.
+   */
+  _dbus_assert (element_size >= (int) sizeof (void*));
+  _dbus_assert (element_size >= (int) sizeof (DBusFreedElement));
+
+  /* align the element size to a pointer boundary so we won't get bus
+   * errors under other architectures.  
+   */
+  pool->element_size = _DBUS_ALIGN_VALUE (element_size, sizeof (void *));
+
+  pool->zero_elements = zero_elements != FALSE;
+
+  pool->allocated_elements = 0;
+  
+  /* pick a size for the first block; it increases
+   * for each block we need to allocate. This is
+   * actually half the initial block size
+   * since _dbus_mem_pool_alloc() unconditionally
+   * doubles it prior to creating a new block.  */
+  pool->block_size = pool->element_size * 8;
+
+  _dbus_assert ((pool->block_size %
+                 pool->element_size) == 0);
+  
+  return pool;
+}
+
+/**
+ * Frees a memory pool (and all elements allocated from it).
+ *
+ * @param pool the memory pool.
+ */
+void
+_dbus_mem_pool_free (DBusMemPool *pool)
+{
+  DBusMemBlock *block;
+
+  block = pool->blocks;
+  while (block != NULL)
+    {
+      DBusMemBlock *next = block->next;
+
+      dbus_free (block);
+
+      block = next;
+    }
+
+  dbus_free (pool);
+}
+
+/**
+ * Allocates an object from the memory pool.
+ * The object must be freed with _dbus_mem_pool_dealloc().
+ *
+ * @param pool the memory pool
+ * @returns the allocated object or #NULL if no memory.
+ */
+void*
+_dbus_mem_pool_alloc (DBusMemPool *pool)
+{
+#ifdef DBUS_BUILD_TESTS
+  if (_dbus_disable_mem_pools ())
+    {
+      DBusMemBlock *block;
+      int alloc_size;
+      
+      /* This is obviously really silly, but it's
+       * debug-mode-only code that is compiled out
+       * when tests are disabled (_dbus_disable_mem_pools()
+       * is a constant expression FALSE so this block
+       * should vanish)
+       */
+      
+      alloc_size = sizeof (DBusMemBlock) - ELEMENT_PADDING +
+        pool->element_size;
+      
+      if (pool->zero_elements)
+        block = dbus_malloc0 (alloc_size);
+      else
+        block = dbus_malloc (alloc_size);
+
+      if (block != NULL)
+        {
+          block->next = pool->blocks;
+          pool->blocks = block;
+          pool->allocated_elements += 1;
+
+          return (void*) &block->elements[0];
+        }
+      else
+        return NULL;
+    }
+  else
+#endif
+    {
+      if (_dbus_decrement_fail_alloc_counter ())
+        {
+          _dbus_verbose (" FAILING mempool alloc\n");
+          return NULL;
+        }
+      else if (pool->free_elements)
+        {
+          DBusFreedElement *element = pool->free_elements;
+
+          pool->free_elements = pool->free_elements->next;
+
+          if (pool->zero_elements)
+            memset (element, '\0', pool->element_size);
+
+          pool->allocated_elements += 1;
+          
+          return element;
+        }
+      else
+        {
+          void *element;
+      
+          if (pool->blocks == NULL ||
+              pool->blocks->used_so_far == pool->block_size)
+            {
+              /* Need a new block */
+              DBusMemBlock *block;
+              int alloc_size;
+#ifdef DBUS_BUILD_TESTS
+              int saved_counter;
+#endif
+          
+              if (pool->block_size <= _DBUS_INT_MAX / 4) /* avoid overflow */
+                {
+                  /* use a larger block size for our next block */
+                  pool->block_size *= 2;
+                  _dbus_assert ((pool->block_size %
+                                 pool->element_size) == 0);
+                }
+
+              alloc_size = sizeof (DBusMemBlock) - ELEMENT_PADDING + pool->block_size;
+
+#ifdef DBUS_BUILD_TESTS
+              /* We save/restore the counter, so that memory pools won't
+               * cause a given function to have different number of
+               * allocations on different invocations. i.e.  when testing
+               * we want consistent alloc patterns. So we skip our
+               * malloc here for purposes of failed alloc simulation.
+               */
+              saved_counter = _dbus_get_fail_alloc_counter ();
+              _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
+#endif
+          
+              if (pool->zero_elements)
+                block = dbus_malloc0 (alloc_size);
+              else
+                block = dbus_malloc (alloc_size);
+
+#ifdef DBUS_BUILD_TESTS
+              _dbus_set_fail_alloc_counter (saved_counter);
+              _dbus_assert (saved_counter == _dbus_get_fail_alloc_counter ());
+#endif
+          
+              if (block == NULL)
+                return NULL;
+
+              block->used_so_far = 0;
+              block->next = pool->blocks;
+              pool->blocks = block;          
+            }
+      
+          element = &pool->blocks->elements[pool->blocks->used_so_far];
+          
+          pool->blocks->used_so_far += pool->element_size;
+
+          pool->allocated_elements += 1;
+          
+          return element;
+        }
+    }
+}
+
+/**
+ * Deallocates an object previously created with
+ * _dbus_mem_pool_alloc(). The previous object
+ * must have come from this same pool.
+ * @param pool the memory pool
+ * @param element the element earlier allocated.
+ * @returns #TRUE if there are no remaining allocated elements
+ */
+dbus_bool_t
+_dbus_mem_pool_dealloc (DBusMemPool *pool,
+                        void        *element)
+{
+#ifdef DBUS_BUILD_TESTS
+  if (_dbus_disable_mem_pools ())
+    {
+      DBusMemBlock *block;
+      DBusMemBlock *prev;
+
+      /* mmm, fast. ;-) debug-only code, so doesn't matter. */
+      
+      prev = NULL;
+      block = pool->blocks;
+
+      while (block != NULL)
+        {
+          if (block->elements == (unsigned char*) element)
+            {
+              if (prev)
+                prev->next = block->next;
+              else
+                pool->blocks = block->next;
+              
+              dbus_free (block);
+
+              _dbus_assert (pool->allocated_elements > 0);
+              pool->allocated_elements -= 1;
+              
+              if (pool->allocated_elements == 0)
+                _dbus_assert (pool->blocks == NULL);
+              
+              return pool->blocks == NULL;
+            }
+          prev = block;
+          block = block->next;
+        }
+      
+      _dbus_assert_not_reached ("freed nonexistent block");
+      return FALSE;
+    }
+  else
+#endif
+    {
+      DBusFreedElement *freed;
+      
+      freed = element;
+      freed->next = pool->free_elements;
+      pool->free_elements = freed;
+      
+      _dbus_assert (pool->allocated_elements > 0);
+      pool->allocated_elements -= 1;
+      
+      return pool->allocated_elements == 0;
+    }
+}
+
+/** @} */
+
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-test.h"
+#include <stdio.h>
+#include <time.h>
+
+static void
+time_for_size (int size)
+{
+  int i;
+  int j;
+  clock_t start;
+  clock_t end;
+#define FREE_ARRAY_SIZE 512
+#define N_ITERATIONS FREE_ARRAY_SIZE * 512
+  void *to_free[FREE_ARRAY_SIZE];
+  DBusMemPool *pool;
+
+  _dbus_verbose ("Timings for size %d\n", size);
+  
+  _dbus_verbose (" malloc\n");
+  
+  start = clock ();
+  
+  i = 0;
+  j = 0;
+  while (i < N_ITERATIONS)
+    {
+      to_free[j] = dbus_malloc (size);
+      _dbus_assert (to_free[j] != NULL); /* in a real app of course this is wrong */
+
+      ++j;
+
+      if (j == FREE_ARRAY_SIZE)
+        {
+          j = 0;
+          while (j < FREE_ARRAY_SIZE)
+            {
+              dbus_free (to_free[j]);
+              ++j;
+            }
+
+          j = 0;
+        }
+      
+      ++i;
+    }
+
+  end = clock ();
+
+  _dbus_verbose ("  created/destroyed %d elements in %g seconds\n",
+                 N_ITERATIONS, (end - start) / (double) CLOCKS_PER_SEC);
+
+
+
+  _dbus_verbose (" mempools\n");
+  
+  start = clock ();
+
+  pool = _dbus_mem_pool_new (size, FALSE);
+  
+  i = 0;
+  j = 0;
+  while (i < N_ITERATIONS)
+    {
+      to_free[j] = _dbus_mem_pool_alloc (pool); 
+      _dbus_assert (to_free[j] != NULL);  /* in a real app of course this is wrong */
+
+      ++j;
+
+      if (j == FREE_ARRAY_SIZE)
+        {
+          j = 0;
+          while (j < FREE_ARRAY_SIZE)
+            {
+              _dbus_mem_pool_dealloc (pool, to_free[j]);
+              ++j;
+            }
+
+          j = 0;
+        }
+      
+      ++i;
+    }
+
+  _dbus_mem_pool_free (pool);
+  
+  end = clock ();
+
+  _dbus_verbose ("  created/destroyed %d elements in %g seconds\n",
+                 N_ITERATIONS, (end - start) / (double) CLOCKS_PER_SEC);
+
+  _dbus_verbose (" zeroed malloc\n");
+    
+  start = clock ();
+  
+  i = 0;
+  j = 0;
+  while (i < N_ITERATIONS)
+    {
+      to_free[j] = dbus_malloc0 (size);
+      _dbus_assert (to_free[j] != NULL); /* in a real app of course this is wrong */
+
+      ++j;
+
+      if (j == FREE_ARRAY_SIZE)
+        {
+          j = 0;
+          while (j < FREE_ARRAY_SIZE)
+            {
+              dbus_free (to_free[j]);
+              ++j;
+            }
+
+          j = 0;
+        }
+      
+      ++i;
+    }
+
+  end = clock ();
+
+  _dbus_verbose ("  created/destroyed %d elements in %g seconds\n",
+                 N_ITERATIONS, (end - start) / (double) CLOCKS_PER_SEC);
+  
+  _dbus_verbose (" zeroed mempools\n");
+  
+  start = clock ();
+
+  pool = _dbus_mem_pool_new (size, TRUE);
+  
+  i = 0;
+  j = 0;
+  while (i < N_ITERATIONS)
+    {
+      to_free[j] = _dbus_mem_pool_alloc (pool); 
+      _dbus_assert (to_free[j] != NULL);  /* in a real app of course this is wrong */
+
+      ++j;
+
+      if (j == FREE_ARRAY_SIZE)
+        {
+          j = 0;
+          while (j < FREE_ARRAY_SIZE)
+            {
+              _dbus_mem_pool_dealloc (pool, to_free[j]);
+              ++j;
+            }
+
+          j = 0;
+        }
+      
+      ++i;
+    }
+
+  _dbus_mem_pool_free (pool);
+  
+  end = clock ();
+
+  _dbus_verbose ("  created/destroyed %d elements in %g seconds\n",
+                 N_ITERATIONS, (end - start) / (double) CLOCKS_PER_SEC);
+}
+
+/**
+ * @ingroup DBusMemPoolInternals
+ * Unit test for DBusMemPool
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_mem_pool_test (void)
+{
+  int i;
+  int element_sizes[] = { 4, 8, 16, 50, 124 };
+  
+  i = 0;
+  while (i < _DBUS_N_ELEMENTS (element_sizes))
+    {
+      time_for_size (element_sizes[i]);
+      ++i;
+    }
+  
+  return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-mempool.h b/src/dbus/dbus-mempool.h
new file mode 100644 (file)
index 0000000..459b45e
--- /dev/null
@@ -0,0 +1,44 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-mempool.h Memory pools
+ * 
+ * Copyright (C) 2002  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_MEMPOOL_H
+#define DBUS_MEMPOOL_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-memory.h>
+#include <dbus/dbus-types.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusMemPool DBusMemPool;
+
+DBusMemPool* _dbus_mem_pool_new     (int          element_size,
+                                     dbus_bool_t  zero_elements);
+void         _dbus_mem_pool_free    (DBusMemPool *pool);
+void*        _dbus_mem_pool_alloc   (DBusMemPool *pool);
+dbus_bool_t  _dbus_mem_pool_dealloc (DBusMemPool *pool,
+                                     void        *element);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_MEMPOOL_H */
diff --git a/src/dbus/dbus-message-factory.c b/src/dbus/dbus-message-factory.c
new file mode 100644 (file)
index 0000000..8550ee8
--- /dev/null
@@ -0,0 +1,1228 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-message-factory.c Generator of valid and invalid message data for test suite
+ *
+ * Copyright (C) 2005 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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 <config.h>
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-message-factory.h"
+#include "dbus-message-private.h"
+#include "dbus-test.h"
+#include <stdio.h>
+
+typedef enum
+  {
+    CHANGE_TYPE_ADJUST,
+    CHANGE_TYPE_ABSOLUTE
+  } ChangeType;
+
+#define BYTE_ORDER_OFFSET  0
+#define TYPE_OFFSET        1
+#define BODY_LENGTH_OFFSET 4
+#define FIELDS_ARRAY_LENGTH_OFFSET 12
+
+static void
+iter_recurse (DBusMessageDataIter *iter)
+{
+  iter->depth += 1;
+  _dbus_assert (iter->depth < _DBUS_MESSAGE_DATA_MAX_NESTING);
+  _dbus_assert (iter->sequence_nos[iter->depth] >= 0);
+}
+
+static int
+iter_get_sequence (DBusMessageDataIter *iter)
+{
+  _dbus_assert (iter->sequence_nos[iter->depth] >= 0);
+  return iter->sequence_nos[iter->depth];
+}
+
+static void
+iter_set_sequence (DBusMessageDataIter *iter,
+                   int                  sequence)
+{
+  _dbus_assert (sequence >= 0);
+  iter->sequence_nos[iter->depth] = sequence;
+}
+
+static void
+iter_unrecurse (DBusMessageDataIter *iter)
+{
+  iter->depth -= 1;
+  _dbus_assert (iter->depth >= 0);
+}
+
+static void
+iter_next (DBusMessageDataIter *iter)
+{
+  iter->sequence_nos[iter->depth] += 1;
+}
+
+static dbus_bool_t
+iter_first_in_series (DBusMessageDataIter *iter)
+{
+  int i;
+
+  i = iter->depth;
+  while (i < _DBUS_MESSAGE_DATA_MAX_NESTING)
+    {
+      if (iter->sequence_nos[i] != 0)
+        return FALSE;
+      ++i;
+    }
+  return TRUE;
+}
+
+typedef dbus_bool_t (* DBusInnerGeneratorFunc)   (DBusMessageDataIter *iter,
+                                                  DBusMessage        **message_p);
+typedef dbus_bool_t (* DBusMessageGeneratorFunc) (DBusMessageDataIter *iter,
+                                                  DBusString          *data,
+                                                  DBusValidity        *expected_validity);
+
+static void
+set_reply_serial (DBusMessage *message)
+{
+  if (message == NULL)
+    _dbus_assert_not_reached ("oom");
+  if (!dbus_message_set_reply_serial (message, 100))
+    _dbus_assert_not_reached ("oom");
+}
+
+static dbus_bool_t
+generate_trivial_inner (DBusMessageDataIter *iter,
+                        DBusMessage        **message_p)
+{
+  DBusMessage *message;
+
+  switch (iter_get_sequence (iter))
+    {
+    case 0:
+      message = dbus_message_new_method_call ("org.freedesktop.TextEditor",
+                                              "/foo/bar",
+                                              "org.freedesktop.DocumentFactory",
+                                              "Create");
+      break;
+    case 1:
+      message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_RETURN);
+      set_reply_serial (message);
+      break;
+    case 2:
+      message = dbus_message_new_signal ("/foo/bar",
+                                         "org.freedesktop.DocumentFactory",
+                                         "Created");
+      break;
+    case 3:
+      message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR);
+
+      if (!dbus_message_set_error_name (message,
+                                        "org.freedesktop.TestErrorName"))
+        _dbus_assert_not_reached ("oom");
+      
+      {
+        DBusMessageIter iter;
+        const char *v_STRING = "This is an error";
+        
+        dbus_message_iter_init_append (message, &iter);
+        if (!dbus_message_iter_append_basic (&iter,
+                                             DBUS_TYPE_STRING,
+                                             &v_STRING))
+          _dbus_assert_not_reached ("oom");
+      }
+      
+      set_reply_serial (message);
+      break;
+    default:
+      return FALSE;
+    }
+  
+  if (message == NULL)
+    _dbus_assert_not_reached ("oom");
+
+  *message_p = message;
+  
+  return TRUE;
+}
+
+static dbus_bool_t
+generate_many_bodies_inner (DBusMessageDataIter *iter,
+                            DBusMessage        **message_p)
+{
+  DBusMessage *message;
+  DBusString signature;
+  DBusString body;
+
+  /* Keeping this small makes things go faster */
+  message = dbus_message_new_method_call ("o.z.F",
+                                          "/",
+                                          "o.z.B",
+                                          "Nah");
+  if (message == NULL)
+    _dbus_assert_not_reached ("oom");
+
+  set_reply_serial (message);
+
+  if (!_dbus_string_init (&signature) || !_dbus_string_init (&body))
+    _dbus_assert_not_reached ("oom");
+  
+  if (dbus_internal_do_not_use_generate_bodies (iter_get_sequence (iter),
+                                                message->byte_order,
+                                                &signature, &body))
+    {
+      const char *v_SIGNATURE;
+
+      v_SIGNATURE = _dbus_string_get_const_data (&signature);
+      if (!_dbus_header_set_field_basic (&message->header,
+                                         DBUS_HEADER_FIELD_SIGNATURE,
+                                         DBUS_TYPE_SIGNATURE,
+                                         &v_SIGNATURE))
+        _dbus_assert_not_reached ("oom");
+
+      if (!_dbus_string_move (&body, 0, &message->body, 0))
+        _dbus_assert_not_reached ("oom");
+
+      _dbus_marshal_set_uint32 (&message->header.data, BODY_LENGTH_OFFSET,
+                                _dbus_string_get_length (&message->body),
+                                message->byte_order);
+      
+      *message_p = message;
+    }
+  else
+    {
+      dbus_message_unref (message);
+      *message_p = NULL;
+    }
+  
+  _dbus_string_free (&signature);
+  _dbus_string_free (&body);
+
+  return *message_p != NULL;
+}
+
+static void
+generate_from_message (DBusString            *data,
+                       DBusValidity          *expected_validity,
+                       DBusMessage           *message)
+{
+  dbus_message_set_serial (message, 1);
+  dbus_message_lock (message);
+
+  *expected_validity = DBUS_VALID;
+  
+  /* move for efficiency, since we'll nuke the message anyway */
+  if (!_dbus_string_move (&message->header.data, 0,
+                          data, 0))
+    _dbus_assert_not_reached ("oom");
+
+  if (!_dbus_string_copy (&message->body, 0,
+                          data, _dbus_string_get_length (data)))
+    _dbus_assert_not_reached ("oom");
+}
+
+static dbus_bool_t
+generate_outer (DBusMessageDataIter   *iter,
+                DBusString            *data,
+                DBusValidity          *expected_validity,
+                DBusInnerGeneratorFunc func)
+{
+  DBusMessage *message;
+
+  message = NULL;
+  if (!(*func)(iter, &message))
+    return FALSE;
+
+  iter_next (iter);
+  
+  _dbus_assert (message != NULL);
+
+  generate_from_message (data, expected_validity, message);
+
+  dbus_message_unref (message);
+
+  return TRUE;
+}
+
+static dbus_bool_t
+generate_trivial (DBusMessageDataIter   *iter,
+                  DBusString            *data,
+                  DBusValidity          *expected_validity)
+{
+  return generate_outer (iter, data, expected_validity,
+                         generate_trivial_inner);
+}
+
+static dbus_bool_t
+generate_many_bodies (DBusMessageDataIter   *iter,
+                      DBusString            *data,
+                      DBusValidity          *expected_validity)
+{
+  return generate_outer (iter, data, expected_validity,
+                         generate_many_bodies_inner);
+}
+
+static DBusMessage*
+simple_method_call (void)
+{
+  DBusMessage *message;
+  /* Keeping this small makes stuff go faster */
+  message = dbus_message_new_method_call ("o.b.Q",
+                                          "/f/b",
+                                          "o.b.Z",
+                                          "Fro");
+  if (message == NULL)
+    _dbus_assert_not_reached ("oom");
+  return message;
+}
+
+static DBusMessage*
+simple_signal (void)
+{
+  DBusMessage *message;
+  message = dbus_message_new_signal ("/f/b",
+                                     "o.b.Z",
+                                     "Fro");
+  if (message == NULL)
+    _dbus_assert_not_reached ("oom");
+  return message;
+}
+
+static DBusMessage*
+simple_method_return (void)
+{
+  DBusMessage *message;
+  message =  dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_RETURN);
+  if (message == NULL)
+    _dbus_assert_not_reached ("oom");
+
+  set_reply_serial (message);
+  
+  return message;
+}
+
+static DBusMessage*
+simple_error (void)
+{
+  DBusMessage *message;
+  message =  dbus_message_new (DBUS_MESSAGE_TYPE_ERROR);
+  if (message == NULL)
+    _dbus_assert_not_reached ("oom");
+
+  if (!dbus_message_set_error_name (message, "foo.bar"))
+    _dbus_assert_not_reached ("oom");
+  
+  set_reply_serial (message);
+  
+  return message;
+}
+
+static dbus_bool_t
+generate_special (DBusMessageDataIter   *iter,
+                  DBusString            *data,
+                  DBusValidity          *expected_validity)
+{
+  int item_seq;
+  DBusMessage *message;
+  int pos;
+  dbus_int32_t v_INT32;
+
+  _dbus_assert (_dbus_string_get_length (data) == 0);
+  
+  message = NULL;
+  pos = -1;
+  v_INT32 = 42;
+  item_seq = iter_get_sequence (iter);
+
+  if (item_seq == 0)
+    {
+      message = simple_method_call ();
+      if (!dbus_message_append_args (message,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INVALID))
+        _dbus_assert_not_reached ("oom");
+                                     
+      _dbus_header_get_field_raw (&message->header,
+                                  DBUS_HEADER_FIELD_SIGNATURE,
+                                  NULL, &pos);
+      generate_from_message (data, expected_validity, message);
+      
+      /* set an invalid typecode */
+      _dbus_string_set_byte (data, pos + 1, '$');
+
+      *expected_validity = DBUS_INVALID_UNKNOWN_TYPECODE;
+    }
+  else if (item_seq == 1)
+    {
+      char long_sig[DBUS_MAXIMUM_TYPE_RECURSION_DEPTH+2];
+      const char *v_STRING;
+      int i;
+      
+      message = simple_method_call ();
+      if (!dbus_message_append_args (message,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INVALID))
+        _dbus_assert_not_reached ("oom");
+
+      i = 0;
+      while (i < (DBUS_MAXIMUM_TYPE_RECURSION_DEPTH + 1))
+        {
+          long_sig[i] = DBUS_TYPE_ARRAY;
+          ++i;
+        }
+      long_sig[i] = DBUS_TYPE_INVALID;
+
+      v_STRING = long_sig;
+      if (!_dbus_header_set_field_basic (&message->header,
+                                         DBUS_HEADER_FIELD_SIGNATURE,
+                                         DBUS_TYPE_SIGNATURE,
+                                         &v_STRING))
+        _dbus_assert_not_reached ("oom");
+      
+      _dbus_header_get_field_raw (&message->header,
+                                  DBUS_HEADER_FIELD_SIGNATURE,
+                                  NULL, &pos);
+      generate_from_message (data, expected_validity, message);
+      
+      *expected_validity = DBUS_INVALID_EXCEEDED_MAXIMUM_ARRAY_RECURSION;
+    }
+  else if (item_seq == 2)
+    {
+      char long_sig[DBUS_MAXIMUM_TYPE_RECURSION_DEPTH*2+4];
+      const char *v_STRING;
+      int i;
+      
+      message = simple_method_call ();
+      if (!dbus_message_append_args (message,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INVALID))
+        _dbus_assert_not_reached ("oom");
+
+      i = 0;
+      while (i <= (DBUS_MAXIMUM_TYPE_RECURSION_DEPTH + 1))
+        {
+          long_sig[i] = DBUS_STRUCT_BEGIN_CHAR;
+          ++i;
+        }
+
+      long_sig[i] = DBUS_TYPE_INT32;
+      ++i;
+
+      while (i < (DBUS_MAXIMUM_TYPE_RECURSION_DEPTH*2 + 3))
+        {
+          long_sig[i] = DBUS_STRUCT_END_CHAR;
+          ++i;
+        }
+      long_sig[i] = DBUS_TYPE_INVALID;
+      
+      v_STRING = long_sig;
+      if (!_dbus_header_set_field_basic (&message->header,
+                                         DBUS_HEADER_FIELD_SIGNATURE,
+                                         DBUS_TYPE_SIGNATURE,
+                                         &v_STRING))
+        _dbus_assert_not_reached ("oom");
+      
+      _dbus_header_get_field_raw (&message->header,
+                                  DBUS_HEADER_FIELD_SIGNATURE,
+                                  NULL, &pos);
+      generate_from_message (data, expected_validity, message);
+      
+      *expected_validity = DBUS_INVALID_EXCEEDED_MAXIMUM_STRUCT_RECURSION;
+    }
+  else if (item_seq == 3)
+    {
+      message = simple_method_call ();
+      if (!dbus_message_append_args (message,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INVALID))
+        _dbus_assert_not_reached ("oom");
+                                     
+      _dbus_header_get_field_raw (&message->header,
+                                  DBUS_HEADER_FIELD_SIGNATURE,
+                                  NULL, &pos);
+      generate_from_message (data, expected_validity, message);
+      
+      _dbus_string_set_byte (data, pos + 1, DBUS_STRUCT_BEGIN_CHAR);
+      
+      *expected_validity = DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED;
+    }
+  else if (item_seq == 4)
+    {
+      message = simple_method_call ();
+      if (!dbus_message_append_args (message,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INVALID))
+        _dbus_assert_not_reached ("oom");
+                                     
+      _dbus_header_get_field_raw (&message->header,
+                                  DBUS_HEADER_FIELD_SIGNATURE,
+                                  NULL, &pos);
+      generate_from_message (data, expected_validity, message);
+      
+      _dbus_string_set_byte (data, pos + 1, DBUS_STRUCT_END_CHAR);
+      
+      *expected_validity = DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED;
+    }
+  else if (item_seq == 5)
+    {
+      message = simple_method_call ();
+      if (!dbus_message_append_args (message,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INVALID))
+        _dbus_assert_not_reached ("oom");
+                                     
+      _dbus_header_get_field_raw (&message->header,
+                                  DBUS_HEADER_FIELD_SIGNATURE,
+                                  NULL, &pos);
+      generate_from_message (data, expected_validity, message);
+      
+      _dbus_string_set_byte (data, pos + 1, DBUS_STRUCT_BEGIN_CHAR);
+      _dbus_string_set_byte (data, pos + 2, DBUS_STRUCT_END_CHAR);
+      
+      *expected_validity = DBUS_INVALID_STRUCT_HAS_NO_FIELDS;
+    }
+  else if (item_seq == 6)
+    {
+      message = simple_method_call ();
+      generate_from_message (data, expected_validity, message);
+      
+      _dbus_string_set_byte (data, TYPE_OFFSET, DBUS_MESSAGE_TYPE_INVALID);
+      
+      *expected_validity = DBUS_INVALID_BAD_MESSAGE_TYPE;
+    }
+  else if (item_seq == 7)
+    {
+      /* Messages of unknown type are considered valid */
+      message = simple_method_call ();
+      generate_from_message (data, expected_validity, message);
+      
+      _dbus_string_set_byte (data, TYPE_OFFSET, 100);
+      
+      *expected_validity = DBUS_VALID;
+    }
+  else if (item_seq == 8)
+    {
+      message = simple_method_call ();
+      generate_from_message (data, expected_validity, message);
+      
+      _dbus_marshal_set_uint32 (data, BODY_LENGTH_OFFSET,
+                                DBUS_MAXIMUM_MESSAGE_LENGTH / 2 + 4,
+                                message->byte_order);
+      _dbus_marshal_set_uint32 (data, FIELDS_ARRAY_LENGTH_OFFSET,
+                                DBUS_MAXIMUM_MESSAGE_LENGTH / 2 + 4,
+                                message->byte_order);
+      *expected_validity = DBUS_INVALID_MESSAGE_TOO_LONG;
+    }
+  else if (item_seq == 9)
+    {
+      const char *v_STRING = "not a valid bus name";
+      message = simple_method_call ();
+
+      if (!_dbus_header_set_field_basic (&message->header,
+                                         DBUS_HEADER_FIELD_SENDER,
+                                         DBUS_TYPE_STRING, &v_STRING))
+        _dbus_assert_not_reached ("oom");
+      
+      generate_from_message (data, expected_validity, message);
+
+      *expected_validity = DBUS_INVALID_BAD_SENDER;
+    }
+  else if (item_seq == 10)
+    {
+      message = simple_method_call ();
+
+      if (!dbus_message_set_interface (message, DBUS_INTERFACE_LOCAL))
+        _dbus_assert_not_reached ("oom");
+      
+      generate_from_message (data, expected_validity, message);
+
+      *expected_validity = DBUS_INVALID_USES_LOCAL_INTERFACE;
+    }
+  else if (item_seq == 11)
+    {
+      message = simple_method_call ();
+
+      if (!dbus_message_set_path (message, DBUS_PATH_LOCAL))
+        _dbus_assert_not_reached ("oom");
+      
+      generate_from_message (data, expected_validity, message);
+
+      *expected_validity = DBUS_INVALID_USES_LOCAL_PATH;
+    }
+  else if (item_seq == 12)
+    {
+      /* Method calls don't have to have interface */
+      message = simple_method_call ();
+
+      if (!dbus_message_set_interface (message, NULL))
+        _dbus_assert_not_reached ("oom");
+      
+      generate_from_message (data, expected_validity, message);
+      
+      *expected_validity = DBUS_VALID;
+    }
+  else if (item_seq == 13)
+    {
+      /* Signals require an interface */
+      message = simple_signal ();
+
+      if (!dbus_message_set_interface (message, NULL))
+        _dbus_assert_not_reached ("oom");
+      
+      generate_from_message (data, expected_validity, message);
+      
+      *expected_validity = DBUS_INVALID_MISSING_INTERFACE;
+    }
+  else if (item_seq == 14)
+    {
+      message = simple_method_return ();
+
+      if (!_dbus_header_delete_field (&message->header, DBUS_HEADER_FIELD_REPLY_SERIAL))
+        _dbus_assert_not_reached ("oom");
+      
+      generate_from_message (data, expected_validity, message);
+      
+      *expected_validity = DBUS_INVALID_MISSING_REPLY_SERIAL;
+    }
+  else if (item_seq == 15)
+    {
+      message = simple_error ();
+
+      if (!dbus_message_set_error_name (message, NULL))
+        _dbus_assert_not_reached ("oom");
+      
+      generate_from_message (data, expected_validity, message);
+      
+      *expected_validity = DBUS_INVALID_MISSING_ERROR_NAME;
+    }
+  else if (item_seq == 16)
+    {
+      char long_sig[DBUS_MAXIMUM_TYPE_RECURSION_DEPTH*4+10];
+      const char *v_STRING;
+      int i;
+      int n_begins;
+      
+      message = simple_method_call ();
+      if (!dbus_message_append_args (message,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INVALID))
+        _dbus_assert_not_reached ("oom");
+
+      i = 0;
+      while (i <= (DBUS_MAXIMUM_TYPE_RECURSION_DEPTH*3 + 3))
+        {
+          long_sig[i] = DBUS_TYPE_ARRAY;
+          ++i;
+          long_sig[i] = DBUS_DICT_ENTRY_BEGIN_CHAR;
+          ++i;
+          long_sig[i] = DBUS_TYPE_INT32;
+          ++i;
+        }
+      n_begins = i / 3;
+
+      long_sig[i] = DBUS_TYPE_INT32;
+      ++i;
+      
+      while (n_begins > 0)
+        {
+          long_sig[i] = DBUS_DICT_ENTRY_END_CHAR;
+          ++i;
+          n_begins -= 1;
+        }
+      long_sig[i] = DBUS_TYPE_INVALID;
+      
+      v_STRING = long_sig;
+      if (!_dbus_header_set_field_basic (&message->header,
+                                         DBUS_HEADER_FIELD_SIGNATURE,
+                                         DBUS_TYPE_SIGNATURE,
+                                         &v_STRING))
+        _dbus_assert_not_reached ("oom");
+      
+      _dbus_header_get_field_raw (&message->header,
+                                  DBUS_HEADER_FIELD_SIGNATURE,
+                                  NULL, &pos);
+      generate_from_message (data, expected_validity, message);
+      
+      *expected_validity = DBUS_INVALID_EXCEEDED_MAXIMUM_DICT_ENTRY_RECURSION;
+    }
+  else if (item_seq == 17)
+    {
+      message = simple_method_call ();
+      if (!dbus_message_append_args (message,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INVALID))
+        _dbus_assert_not_reached ("oom");
+                                     
+      _dbus_header_get_field_raw (&message->header,
+                                  DBUS_HEADER_FIELD_SIGNATURE,
+                                  NULL, &pos);
+      generate_from_message (data, expected_validity, message);
+
+      _dbus_string_set_byte (data, pos + 1, DBUS_TYPE_ARRAY);
+      _dbus_string_set_byte (data, pos + 2, DBUS_DICT_ENTRY_BEGIN_CHAR);
+      
+      *expected_validity = DBUS_INVALID_DICT_ENTRY_STARTED_BUT_NOT_ENDED;
+    }
+  else if (item_seq == 18)
+    {
+      message = simple_method_call ();
+      if (!dbus_message_append_args (message,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INVALID))
+        _dbus_assert_not_reached ("oom");
+                                     
+      _dbus_header_get_field_raw (&message->header,
+                                  DBUS_HEADER_FIELD_SIGNATURE,
+                                  NULL, &pos);
+      generate_from_message (data, expected_validity, message);
+      
+      _dbus_string_set_byte (data, pos + 1, DBUS_DICT_ENTRY_END_CHAR);
+      
+      *expected_validity = DBUS_INVALID_DICT_ENTRY_ENDED_BUT_NOT_STARTED;
+    }
+  else if (item_seq == 19)
+    {
+      message = simple_method_call ();
+      if (!dbus_message_append_args (message,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INT32, &v_INT32,
+                                     DBUS_TYPE_INVALID))
+        _dbus_assert_not_reached ("oom");
+                                     
+      _dbus_header_get_field_raw (&message->header,
+                                  DBUS_HEADER_FIELD_SIGNATURE,
+                                  NULL, &pos);
+      generate_from_message (data, expected_validity, message);
+
+      _dbus_string_set_byte (data, pos + 1, DBUS_TYPE_ARRAY);
+      _dbus_string_set_byte (data, pos + 2, DBUS_DICT_ENTRY_BEGIN_CHAR);
+      _dbus_string_set_byte (data, pos + 3, DBUS_DICT_ENTRY_END_CHAR);
+      
+      *expected_validity = DBUS_INVALID_DICT_ENTRY_HAS_NO_FIELDS;
+    }
+  else
+    {
+      return FALSE;
+    }
+
+  if (message)
+    dbus_message_unref (message);
+
+  iter_next (iter);
+  return TRUE;
+}
+
+static dbus_bool_t
+generate_wrong_length (DBusMessageDataIter *iter,
+                       DBusString          *data,
+                       DBusValidity        *expected_validity)
+{
+  int lengths[] = { -42, -17, -16, -15, -9, -8, -7, -6, -5, -4, -3, -2, -1,
+                    1, 2, 3, 4, 5, 6, 7, 8, 9, 15, 16, 30 };
+  int adjust;
+  int len_seq;
+
+ restart:
+  len_seq = iter_get_sequence (iter);
+  if (len_seq == _DBUS_N_ELEMENTS (lengths))
+    return FALSE;
+
+  _dbus_assert (len_seq < _DBUS_N_ELEMENTS (lengths));
+  
+  iter_recurse (iter);
+  if (!generate_many_bodies (iter, data, expected_validity))
+    {
+      iter_set_sequence (iter, 0); /* reset to first body */
+      iter_unrecurse (iter);
+      iter_next (iter);            /* next length adjustment */
+      goto restart;
+    }
+  iter_unrecurse (iter);
+
+  adjust = lengths[len_seq];
+
+  if (adjust < 0)
+    {
+      if ((_dbus_string_get_length (data) + adjust) < DBUS_MINIMUM_HEADER_SIZE)
+        _dbus_string_set_length (data, DBUS_MINIMUM_HEADER_SIZE);
+      else
+        _dbus_string_shorten (data, - adjust);
+      *expected_validity = DBUS_INVALID_FOR_UNKNOWN_REASON;
+    }
+  else
+    {      
+      if (!_dbus_string_lengthen (data, adjust))
+        _dbus_assert_not_reached ("oom");
+      *expected_validity = DBUS_INVALID_TOO_MUCH_DATA;
+    }
+
+  /* Fixup lengths */
+  {
+    int old_body_len;
+    int new_body_len;
+    int byte_order;
+    
+    _dbus_assert (_dbus_string_get_length (data) >= DBUS_MINIMUM_HEADER_SIZE);
+    
+    byte_order = _dbus_string_get_byte (data, BYTE_ORDER_OFFSET);
+    old_body_len = _dbus_marshal_read_uint32 (data,
+                                              BODY_LENGTH_OFFSET,
+                                              byte_order,
+                                              NULL);
+    _dbus_assert (old_body_len < _dbus_string_get_length (data));
+    new_body_len = old_body_len + adjust;
+    if (new_body_len < 0)
+      {
+        new_body_len = 0;
+        /* we just munged the header, and aren't sure how */
+        *expected_validity = DBUS_VALIDITY_UNKNOWN;
+      }
+
+    _dbus_verbose ("changing body len from %u to %u by adjust %d\n",
+                   old_body_len, new_body_len, adjust);
+    
+    _dbus_marshal_set_uint32 (data, BODY_LENGTH_OFFSET,
+                              new_body_len,
+                              byte_order);
+  }
+
+  return TRUE;
+}
+
+static dbus_bool_t
+generate_byte_changed (DBusMessageDataIter *iter,
+                       DBusString          *data,
+                       DBusValidity        *expected_validity)
+{
+  int byte_seq;
+  int v_BYTE;
+
+  /* This is a little convoluted to make the bodies the
+   * outer loop and each byte of each body the inner
+   * loop
+   */
+
+ restart:
+  if (!generate_many_bodies (iter, data, expected_validity))
+    return FALSE;
+
+  iter_recurse (iter);
+  byte_seq = iter_get_sequence (iter);
+  iter_next (iter);
+  iter_unrecurse (iter);
+  
+  if (byte_seq == _dbus_string_get_length (data))
+    {
+      _dbus_string_set_length (data, 0);
+      /* reset byte count */
+      iter_recurse (iter);
+      iter_set_sequence (iter, 0);
+      iter_unrecurse (iter);
+      goto restart;
+    }
+  else
+    {
+      /* Undo the "next" in generate_many_bodies */
+      iter_set_sequence (iter, iter_get_sequence (iter) - 1);
+    }
+
+  _dbus_assert (byte_seq < _dbus_string_get_length (data));
+  v_BYTE = _dbus_string_get_byte (data, byte_seq);
+  v_BYTE += byte_seq; /* arbitrary but deterministic change to the byte */
+  _dbus_string_set_byte (data, byte_seq, v_BYTE);
+  *expected_validity = DBUS_VALIDITY_UNKNOWN;
+
+  return TRUE;
+}
+
+static dbus_bool_t
+find_next_typecode (DBusMessageDataIter *iter,
+                    DBusString          *data,
+                    DBusValidity        *expected_validity)
+{
+  int body_seq;
+  int byte_seq;
+  int base_depth;
+
+  base_depth = iter->depth;
+
+ restart:
+  _dbus_assert (iter->depth == (base_depth + 0));
+  _dbus_string_set_length (data, 0);
+
+  body_seq = iter_get_sequence (iter);
+  
+  if (!generate_many_bodies (iter, data, expected_validity))
+    return FALSE;
+  /* Undo the "next" in generate_many_bodies */
+  iter_set_sequence (iter, body_seq);
+  
+  iter_recurse (iter);
+  while (TRUE)
+    {
+      _dbus_assert (iter->depth == (base_depth + 1));
+      
+      byte_seq = iter_get_sequence (iter);
+
+      _dbus_assert (byte_seq <= _dbus_string_get_length (data));
+      
+      if (byte_seq == _dbus_string_get_length (data))
+        {
+          /* reset byte count */
+          iter_set_sequence (iter, 0);
+          iter_unrecurse (iter);
+          _dbus_assert (iter->depth == (base_depth + 0));
+          iter_next (iter); /* go to the next body */
+          goto restart;
+        }
+
+      _dbus_assert (byte_seq < _dbus_string_get_length (data));
+
+      if (_dbus_type_is_valid (_dbus_string_get_byte (data, byte_seq)))
+        break;
+      else
+        iter_next (iter);
+    }
+
+  _dbus_assert (byte_seq == iter_get_sequence (iter));
+  _dbus_assert (byte_seq < _dbus_string_get_length (data));
+
+  iter_unrecurse (iter);
+
+  _dbus_assert (iter->depth == (base_depth + 0));
+  
+  return TRUE;
+}
+
+static const int typecodes[] = {
+  DBUS_TYPE_INVALID,
+  DBUS_TYPE_BYTE,
+  DBUS_TYPE_BOOLEAN,
+  DBUS_TYPE_INT16,
+  DBUS_TYPE_UINT16,
+  DBUS_TYPE_INT32,
+  DBUS_TYPE_UINT32,
+  DBUS_TYPE_INT64,
+  DBUS_TYPE_UINT64,
+  DBUS_TYPE_DOUBLE,
+  DBUS_TYPE_STRING,
+  DBUS_TYPE_OBJECT_PATH,
+  DBUS_TYPE_SIGNATURE,
+  DBUS_TYPE_ARRAY,
+  DBUS_TYPE_VARIANT,
+  DBUS_STRUCT_BEGIN_CHAR,
+  DBUS_STRUCT_END_CHAR,
+  DBUS_DICT_ENTRY_BEGIN_CHAR,
+  DBUS_DICT_ENTRY_END_CHAR,
+  255 /* random invalid typecode */
+};
+  
+static dbus_bool_t
+generate_typecode_changed (DBusMessageDataIter *iter,
+                           DBusString          *data,
+                           DBusValidity        *expected_validity)
+{
+  int byte_seq;
+  int typecode_seq;
+  int base_depth;
+
+  base_depth = iter->depth;
+
+ restart:
+  _dbus_assert (iter->depth == (base_depth + 0));
+  _dbus_string_set_length (data, 0);
+  
+  if (!find_next_typecode (iter, data, expected_validity))
+    return FALSE;
+
+  iter_recurse (iter);
+  byte_seq = iter_get_sequence (iter);
+
+  _dbus_assert (byte_seq < _dbus_string_get_length (data));
+  
+  iter_recurse (iter);
+  typecode_seq = iter_get_sequence (iter);
+  iter_next (iter);
+
+  _dbus_assert (typecode_seq <= _DBUS_N_ELEMENTS (typecodes));
+  
+  if (typecode_seq == _DBUS_N_ELEMENTS (typecodes))
+    {
+      _dbus_assert (iter->depth == (base_depth + 2));
+      iter_set_sequence (iter, 0); /* reset typecode sequence */
+      iter_unrecurse (iter);
+      _dbus_assert (iter->depth == (base_depth + 1));
+      iter_next (iter); /* go to the next byte_seq */
+      iter_unrecurse (iter);
+      _dbus_assert (iter->depth == (base_depth + 0));
+      goto restart;
+    }
+
+  _dbus_assert (iter->depth == (base_depth + 2));
+  iter_unrecurse (iter);
+  _dbus_assert (iter->depth == (base_depth + 1));
+  iter_unrecurse (iter);
+  _dbus_assert (iter->depth == (base_depth + 0));
+
+#if 0
+  printf ("Changing byte %d in message %d to %c\n",
+          byte_seq, iter_get_sequence (iter), typecodes[typecode_seq]);
+#endif
+  
+  _dbus_string_set_byte (data, byte_seq, typecodes[typecode_seq]);
+  *expected_validity = DBUS_VALIDITY_UNKNOWN;
+  return TRUE;
+}
+
+typedef struct
+{
+  ChangeType type;
+  dbus_uint32_t value; /* cast to signed for adjusts */
+} UIntChange;
+
+static const UIntChange uint32_changes[] = {
+  { CHANGE_TYPE_ADJUST, (dbus_uint32_t) -1 },
+  { CHANGE_TYPE_ADJUST, (dbus_uint32_t) -2 },
+  { CHANGE_TYPE_ADJUST, (dbus_uint32_t) -3 },
+  { CHANGE_TYPE_ADJUST, (dbus_uint32_t) 1 },
+  { CHANGE_TYPE_ADJUST, (dbus_uint32_t) 2 },
+  { CHANGE_TYPE_ADJUST, (dbus_uint32_t) 3 },
+  { CHANGE_TYPE_ABSOLUTE, _DBUS_UINT32_MAX },
+  { CHANGE_TYPE_ABSOLUTE, 0 },
+  { CHANGE_TYPE_ABSOLUTE, 1 },
+  { CHANGE_TYPE_ABSOLUTE, _DBUS_UINT32_MAX - 1 },
+  { CHANGE_TYPE_ABSOLUTE, _DBUS_UINT32_MAX - 5 }
+};
+
+static dbus_bool_t
+generate_uint32_changed (DBusMessageDataIter *iter,
+                         DBusString          *data,
+                         DBusValidity        *expected_validity)
+{
+  int body_seq;
+  int byte_seq;
+  int change_seq;
+  dbus_uint32_t v_UINT32;
+  int byte_order;
+  const UIntChange *change;
+  int base_depth;
+
+  /* Outer loop is each body, next loop is each change,
+   * inner loop is each change location
+   */
+
+  base_depth = iter->depth;
+  
+ next_body:
+  _dbus_assert (iter->depth == (base_depth + 0));
+  _dbus_string_set_length (data, 0);
+  body_seq = iter_get_sequence (iter);
+  
+  if (!generate_many_bodies (iter, data, expected_validity))
+    return FALSE;
+
+  _dbus_assert (iter->depth == (base_depth + 0));
+
+  iter_set_sequence (iter, body_seq); /* undo the "next" from generate_many_bodies */
+  iter_recurse (iter);
+ next_change:
+  _dbus_assert (iter->depth == (base_depth + 1));
+  change_seq = iter_get_sequence (iter);
+  
+  if (change_seq == _DBUS_N_ELEMENTS (uint32_changes))
+    {
+      /* Reset change count */
+      iter_set_sequence (iter, 0);
+      iter_unrecurse (iter);
+      iter_next (iter);
+      goto next_body;
+    }
+
+  _dbus_assert (iter->depth == (base_depth + 1));
+  
+  iter_recurse (iter);
+  _dbus_assert (iter->depth == (base_depth + 2));
+  byte_seq = iter_get_sequence (iter);
+  /* skip 4 bytes at a time */
+  iter_next (iter);
+  iter_next (iter);
+  iter_next (iter);
+  iter_next (iter);
+  iter_unrecurse (iter);
+
+  _dbus_assert (_DBUS_ALIGN_VALUE (byte_seq, 4) == (unsigned) byte_seq);
+  if (byte_seq >= (_dbus_string_get_length (data) - 4))
+    {
+      /* reset byte count */
+      _dbus_assert (iter->depth == (base_depth + 1));
+      iter_recurse (iter);
+      _dbus_assert (iter->depth == (base_depth + 2));
+      iter_set_sequence (iter, 0);
+      iter_unrecurse (iter);
+      _dbus_assert (iter->depth == (base_depth + 1));
+      iter_next (iter);
+      goto next_change;
+    }
+  
+  _dbus_assert (byte_seq <= (_dbus_string_get_length (data) - 4));
+
+  byte_order = _dbus_string_get_byte (data, BYTE_ORDER_OFFSET);
+  
+  v_UINT32 = _dbus_marshal_read_uint32 (data, byte_seq, byte_order, NULL);
+
+  change = &uint32_changes[change_seq];
+
+  if (change->type == CHANGE_TYPE_ADJUST)
+    {
+      v_UINT32 += (int) change->value;
+    }
+  else
+    {
+      v_UINT32 = change->value;
+    }
+
+#if 0
+  printf ("body %d change %d pos %d ",
+          body_seq, change_seq, byte_seq);
+
+  if (change->type == CHANGE_TYPE_ADJUST)
+    printf ("adjust by %d", (int) change->value);
+  else
+    printf ("set to %u", change->value);
+  
+  printf (" \t%u -> %u\n",
+          _dbus_marshal_read_uint32 (data, byte_seq, byte_order, NULL),
+          v_UINT32);
+#endif
+  
+  _dbus_marshal_set_uint32 (data, byte_seq, v_UINT32, byte_order);
+  *expected_validity = DBUS_VALIDITY_UNKNOWN;
+
+  _dbus_assert (iter->depth == (base_depth + 1));
+  iter_unrecurse (iter);
+  _dbus_assert (iter->depth == (base_depth + 0));
+          
+  return TRUE;
+}
+
+typedef struct
+{
+  const char *name;
+  DBusMessageGeneratorFunc func;  
+} DBusMessageGenerator;
+
+static const DBusMessageGenerator generators[] = {
+  { "trivial example of each message type", generate_trivial },
+  { "assorted arguments", generate_many_bodies },
+  { "assorted special cases", generate_special },
+  { "each uint32 modified", generate_uint32_changed },
+  { "wrong body lengths", generate_wrong_length },
+  { "each byte modified", generate_byte_changed },
+#if 0
+  /* This is really expensive and doesn't add too much coverage */
+  { "change each typecode", generate_typecode_changed }
+#endif
+};
+
+void
+_dbus_message_data_free (DBusMessageData *data)
+{
+  _dbus_string_free (&data->data);
+}
+
+void
+_dbus_message_data_iter_init (DBusMessageDataIter *iter)
+{
+  int i;
+  
+  iter->depth = 0;
+  i = 0;
+  while (i < _DBUS_MESSAGE_DATA_MAX_NESTING)
+    {
+      iter->sequence_nos[i] = 0;
+      ++i;
+    }
+  iter->count = 0;
+}
+
+dbus_bool_t
+_dbus_message_data_iter_get_and_next (DBusMessageDataIter *iter,
+                                      DBusMessageData     *data)
+{
+  DBusMessageGeneratorFunc func;
+  int generator;
+
+ restart:
+  generator = iter_get_sequence (iter);
+  
+  if (generator == _DBUS_N_ELEMENTS (generators))
+    return FALSE;
+
+  iter_recurse (iter);
+  
+  if (iter_first_in_series (iter))
+    {
+      printf (" testing message loading: %s ", generators[generator].name);
+      fflush (stdout);
+    }
+  
+  func = generators[generator].func;
+
+  if (!_dbus_string_init (&data->data))
+    _dbus_assert_not_reached ("oom");
+  
+  if ((*func)(iter, &data->data, &data->expected_validity))
+    ;
+  else
+    {
+      iter_set_sequence (iter, 0);
+      iter_unrecurse (iter);
+      iter_next (iter); /* next generator */
+      _dbus_string_free (&data->data);
+      printf ("%d test loads cumulative\n", iter->count);
+      goto restart;
+    }
+  iter_unrecurse (iter);
+
+  iter->count += 1;
+  return TRUE;
+}
+
+#endif /* !DOXYGEN_SHOULD_SKIP_THIS */
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-message-factory.h b/src/dbus/dbus-message-factory.h
new file mode 100644 (file)
index 0000000..de5cc65
--- /dev/null
@@ -0,0 +1,61 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-message-factory.h Generator of valid and invalid message data for test suite
+ *
+ * Copyright (C) 2005 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_MESSAGE_FACTORY_H
+#define DBUS_MESSAGE_FACTORY_H
+
+#ifdef DBUS_BUILD_TESTS
+
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-marshal-basic.h>
+#include <dbus/dbus-marshal-validate.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct
+{
+  DBusValidity expected_validity;
+  
+  DBusString data;
+
+} DBusMessageData;
+
+#define _DBUS_MESSAGE_DATA_MAX_NESTING 10
+typedef struct
+{
+  int sequence_nos[_DBUS_MESSAGE_DATA_MAX_NESTING];
+  int depth;
+  int count;
+} DBusMessageDataIter;
+
+void        _dbus_message_data_free              (DBusMessageData     *data);
+void        _dbus_message_data_iter_init         (DBusMessageDataIter *iter);
+dbus_bool_t _dbus_message_data_iter_get_and_next (DBusMessageDataIter *iter,
+                                                  DBusMessageData     *data);
+
+
+DBUS_END_DECLS
+
+#endif /* DBUS_BUILD_TESTS */
+
+#endif /* DBUS_MESSAGE_FACTORY_H */
diff --git a/src/dbus/dbus-message-internal.h b/src/dbus/dbus-message-internal.h
new file mode 100644 (file)
index 0000000..2e995b4
--- /dev/null
@@ -0,0 +1,72 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-message-internal.h DBusMessage object internal interfaces
+ *
+ * Copyright (C) 2002  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_MESSAGE_INTERNAL_H
+#define DBUS_MESSAGE_INTERNAL_H
+
+#include <dbus/dbus-message.h>
+#include <dbus/dbus-resources.h>
+#include <dbus/dbus-list.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusMessageLoader DBusMessageLoader;
+
+void _dbus_message_get_network_data  (DBusMessage       *message,
+                                     const DBusString **header,
+                                     const DBusString **body);
+
+void        _dbus_message_lock                  (DBusMessage  *message);
+void        _dbus_message_unlock                (DBusMessage  *message);
+dbus_bool_t _dbus_message_add_size_counter      (DBusMessage  *message,
+                                                 DBusCounter  *counter);
+void        _dbus_message_add_size_counter_link (DBusMessage  *message,
+                                                 DBusList     *link);
+void        _dbus_message_remove_size_counter   (DBusMessage  *message,
+                                                 DBusCounter  *counter,
+                                                 DBusList    **link_return);
+
+DBusMessageLoader* _dbus_message_loader_new                   (void);
+DBusMessageLoader* _dbus_message_loader_ref                   (DBusMessageLoader  *loader);
+void               _dbus_message_loader_unref                 (DBusMessageLoader  *loader);
+
+void               _dbus_message_loader_get_buffer            (DBusMessageLoader  *loader,
+                                                               DBusString        **buffer);
+void               _dbus_message_loader_return_buffer         (DBusMessageLoader  *loader,
+                                                               DBusString         *buffer,
+                                                               int                 bytes_read);
+dbus_bool_t        _dbus_message_loader_queue_messages        (DBusMessageLoader  *loader);
+DBusMessage*       _dbus_message_loader_peek_message          (DBusMessageLoader  *loader);
+DBusMessage*       _dbus_message_loader_pop_message           (DBusMessageLoader  *loader);
+DBusList*          _dbus_message_loader_pop_message_link      (DBusMessageLoader  *loader);
+void               _dbus_message_loader_putback_message_link  (DBusMessageLoader  *loader,
+                                                               DBusList           *link);
+
+dbus_bool_t        _dbus_message_loader_get_is_corrupted      (DBusMessageLoader  *loader);
+
+void               _dbus_message_loader_set_max_message_size  (DBusMessageLoader  *loader,
+                                                               long                size);
+long               _dbus_message_loader_get_max_message_size  (DBusMessageLoader  *loader);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_MESSAGE_INTERNAL_H */
diff --git a/src/dbus/dbus-message-private.h b/src/dbus/dbus-message-private.h
new file mode 100644 (file)
index 0000000..c1e368f
--- /dev/null
@@ -0,0 +1,125 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-message-private.h header shared between dbus-message.c and dbus-message-util.c
+ *
+ * Copyright (C) 2005  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_MESSAGE_PRIVATE_H
+#define DBUS_MESSAGE_PRIVATE_H
+
+#include <dbus/dbus-message.h>
+#include <dbus/dbus-message-internal.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-dataslot.h>
+#include <dbus/dbus-marshal-header.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusMessageInternals
+ * @{
+ */
+
+/**
+ * @typedef DBusMessageLoader
+ *
+ * The DBusMessageLoader object encapsulates the process of converting
+ * a byte stream into a series of DBusMessage. It buffers the incoming
+ * bytes as efficiently as possible, and generates a queue of
+ * messages. DBusMessageLoader is typically used as part of a
+ * DBusTransport implementation. The DBusTransport then hands off
+ * the loaded messages to a DBusConnection, making the messages
+ * visible to the application.
+ *
+ * @todo write tests for break-loader that a) randomly delete header
+ * fields and b) set string fields to zero-length and other funky
+ * values.
+ *
+ */
+
+/**
+ * Implementation details of DBusMessageLoader.
+ * All members are private.
+ */
+struct DBusMessageLoader
+{
+  int refcount;        /**< Reference count. */
+
+  DBusString data;     /**< Buffered data */
+
+  DBusList *messages;  /**< Complete messages. */
+
+  long max_message_size; /**< Maximum size of a message */
+
+  unsigned int buffer_outstanding : 1; /**< Someone is using the buffer to read */
+
+  unsigned int corrupted : 1; /**< We got broken data, and are no longer working */
+
+  DBusValidity corruption_reason; /**< why we were corrupted */
+};
+
+
+/** How many bits are in the changed_stamp used to validate iterators */
+#define CHANGED_STAMP_BITS 21
+
+/**
+ * @brief Internals of DBusMessage
+ *
+ * Object representing a message received from or to be sent to
+ * another application. This is an opaque object, all members
+ * are private.
+ */
+struct DBusMessage
+{
+  DBusAtomic refcount; /**< Reference count */
+
+  DBusHeader header; /**< Header network data and associated cache */
+
+  DBusString body;   /**< Body network data. */
+
+  char byte_order; /**< Message byte order. */
+
+  unsigned int locked : 1; /**< Message being sent, no modifications allowed. */
+
+#ifndef DBUS_DISABLE_CHECKS
+  unsigned int in_cache : 1; /**< Has been "freed" since it's in the cache (this is a debug feature) */
+#endif
+  
+  DBusList *size_counters;   /**< 0-N DBusCounter used to track message size. */
+  long size_counter_delta;   /**< Size we incremented the size counters by.   */
+
+  dbus_uint32_t changed_stamp : CHANGED_STAMP_BITS; /**< Incremented when iterators are invalidated. */
+
+  DBusDataSlotList slot_list;   /**< Data stored by allocated integer ID */
+
+#ifndef DBUS_DISABLE_CHECKS
+  int generation; /**< _dbus_current_generation when message was created */
+#endif
+};
+
+dbus_bool_t _dbus_message_iter_get_args_valist (DBusMessageIter *iter,
+                                                DBusError       *error,
+                                                int              first_arg_type,
+                                                va_list          var_args);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_MESSAGE_H */
diff --git a/src/dbus/dbus-message-util.c b/src/dbus/dbus-message-util.c
new file mode 100644 (file)
index 0000000..46cbe4e
--- /dev/null
@@ -0,0 +1,1320 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-message-util.c Would be in dbus-message.c, but only used by bus/tests
+ *
+ * Copyright (C) 2002, 2003, 2004, 2005  Red Hat Inc.
+ * Copyright (C) 2002, 2003  CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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-internals.h"
+#include "dbus-test.h"
+#include "dbus-message-private.h"
+#include "dbus-marshal-recursive.h"
+#include "dbus-string.h"
+
+/**
+ * @addtogroup DBusMessage
+ * @{
+ */
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Reads arguments from a message iterator given a variable argument
+ * list. Only arguments of basic type and arrays of fixed-length
+ * basic type may be read with this function. See
+ * dbus_message_get_args() for more details.
+ *
+ * @param iter the message iterator
+ * @param error error to be filled in on failure
+ * @param first_arg_type the first argument type
+ * @param ... location for first argument value, then list of type-location pairs
+ * @returns #FALSE if the error was set
+ */
+static dbus_bool_t
+dbus_message_iter_get_args (DBusMessageIter *iter,
+                           DBusError       *error,
+                           int              first_arg_type,
+                           ...)
+{
+  dbus_bool_t retval;
+  va_list var_args;
+
+  _dbus_return_val_if_fail (iter != NULL, FALSE);
+  _dbus_return_val_if_error_is_set (error, FALSE);
+
+  va_start (var_args, first_arg_type);
+  retval = _dbus_message_iter_get_args_valist (iter, error, first_arg_type, var_args);
+  va_end (var_args);
+
+  return retval;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/** @} */
+
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-test.h"
+#include "dbus-message-factory.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+static int validities_seen[DBUS_VALIDITY_LAST + _DBUS_NEGATIVE_VALIDITY_COUNT];
+
+static void
+reset_validities_seen (void)
+{
+  int i;
+  i = 0;
+  while (i < _DBUS_N_ELEMENTS (validities_seen))
+    {
+      validities_seen[i] = 0;
+      ++i;
+    }
+}
+
+static void
+record_validity_seen (DBusValidity validity)
+{
+  validities_seen[validity + _DBUS_NEGATIVE_VALIDITY_COUNT] += 1;
+}
+
+static void
+print_validities_seen (dbus_bool_t not_seen)
+{
+  int i;
+  i = 0;
+  while (i < _DBUS_N_ELEMENTS (validities_seen))
+    {
+      if ((i - _DBUS_NEGATIVE_VALIDITY_COUNT) == DBUS_VALIDITY_UNKNOWN ||
+          (i - _DBUS_NEGATIVE_VALIDITY_COUNT) == DBUS_INVALID_FOR_UNKNOWN_REASON)
+        ;
+      else if ((not_seen && validities_seen[i] == 0) ||
+               (!not_seen && validities_seen[i] > 0))
+        printf ("validity %3d seen %d times\n",
+                i - _DBUS_NEGATIVE_VALIDITY_COUNT,
+                validities_seen[i]);
+      ++i;
+    }
+}
+
+static void
+check_memleaks (void)
+{
+  dbus_shutdown ();
+
+  if (_dbus_get_malloc_blocks_outstanding () != 0)
+    {
+      _dbus_warn ("%d dbus_malloc blocks were not freed in %s\n",
+                  _dbus_get_malloc_blocks_outstanding (), __FILE__);
+      _dbus_assert_not_reached ("memleaks");
+    }
+}
+
+static dbus_bool_t
+check_have_valid_message (DBusMessageLoader *loader)
+{
+  DBusMessage *message;
+  dbus_bool_t retval;
+
+  message = NULL;
+  retval = FALSE;
+
+  if (_dbus_message_loader_get_is_corrupted (loader))
+    {
+      _dbus_warn ("loader corrupted on message that was expected to be valid; invalid reason %d\n",
+                  loader->corruption_reason);
+      goto failed;
+    }
+
+  message = _dbus_message_loader_pop_message (loader);
+  if (message == NULL)
+    {
+      _dbus_warn ("didn't load message that was expected to be valid (message not popped)\n");
+      goto failed;
+    }
+
+  if (_dbus_string_get_length (&loader->data) > 0)
+    {
+      _dbus_warn ("had leftover bytes from expected-to-be-valid single message\n");
+      goto failed;
+    }
+
+#if 0
+  /* FIXME */
+  /* Verify that we're able to properly deal with the message.
+   * For example, this would detect improper handling of messages
+   * in nonstandard byte order.
+   */
+  if (!check_message_handling (message))
+    goto failed;
+#endif
+
+  record_validity_seen (DBUS_VALID);
+  
+  retval = TRUE;
+
+ failed:
+  if (message)
+    dbus_message_unref (message);
+
+  return retval;
+}
+
+static dbus_bool_t
+check_invalid_message (DBusMessageLoader *loader,
+                       DBusValidity       expected_validity)
+{
+  dbus_bool_t retval;
+
+  retval = FALSE;
+
+  if (!_dbus_message_loader_get_is_corrupted (loader))
+    {
+      _dbus_warn ("loader not corrupted on message that was expected to be invalid\n");
+      goto failed;
+    }
+
+  record_validity_seen (loader->corruption_reason);
+  
+  if (expected_validity != DBUS_INVALID_FOR_UNKNOWN_REASON &&
+      loader->corruption_reason != expected_validity)
+    {
+      _dbus_warn ("expected message to be corrupted for reason %d and was corrupted for %d instead\n",
+                  expected_validity, loader->corruption_reason);
+      goto failed;
+    }
+
+  retval = TRUE;
+
+ failed:
+  return retval;
+}
+
+static dbus_bool_t
+check_incomplete_message (DBusMessageLoader *loader)
+{
+  DBusMessage *message;
+  dbus_bool_t retval;
+
+  message = NULL;
+  retval = FALSE;
+
+  if (_dbus_message_loader_get_is_corrupted (loader))
+    {
+      _dbus_warn ("loader corrupted on message that was expected to be valid (but incomplete), corruption reason %d\n",
+                  loader->corruption_reason);
+      goto failed;
+    }
+
+  message = _dbus_message_loader_pop_message (loader);
+  if (message != NULL)
+    {
+      _dbus_warn ("loaded message that was expected to be incomplete\n");
+      goto failed;
+    }
+
+  record_validity_seen (DBUS_VALID_BUT_INCOMPLETE);
+  retval = TRUE;
+
+ failed:
+  if (message)
+    dbus_message_unref (message);
+  return retval;
+}
+
+static dbus_bool_t
+check_loader_results (DBusMessageLoader      *loader,
+                      DBusValidity            expected_validity)
+{
+  if (!_dbus_message_loader_queue_messages (loader))
+    _dbus_assert_not_reached ("no memory to queue messages");
+
+  if (expected_validity == DBUS_VALID)
+    return check_have_valid_message (loader);
+  else if (expected_validity == DBUS_VALID_BUT_INCOMPLETE)
+    return check_incomplete_message (loader);
+  else if (expected_validity == DBUS_VALIDITY_UNKNOWN)
+    {
+      /* here we just know we didn't segfault and that was the
+       * only test. Also, we record that we got coverage
+       * for the validity reason.
+       */
+      if (_dbus_message_loader_get_is_corrupted (loader))
+        record_validity_seen (loader->corruption_reason);
+      
+      return TRUE;
+    }
+  else
+    return check_invalid_message (loader, expected_validity);
+}
+
+/**
+ * Loads the message in the given message file.
+ *
+ * @param filename filename to load
+ * @param data string to load message into
+ * @returns #TRUE if the message was loaded
+ */
+dbus_bool_t
+dbus_internal_do_not_use_load_message_file (const DBusString    *filename,
+                                            DBusString          *data)
+{
+  dbus_bool_t retval;
+  DBusError error = DBUS_ERROR_INIT;
+
+  retval = FALSE;
+
+  _dbus_verbose ("Loading raw %s\n", _dbus_string_get_const_data (filename));
+  if (!_dbus_file_get_contents (data, filename, &error))
+    {
+      _dbus_warn ("Could not load message file %s: %s\n",
+                  _dbus_string_get_const_data (filename),
+                  error.message);
+      dbus_error_free (&error);
+      goto failed;
+    }
+
+  retval = TRUE;
+
+ failed:
+
+  return retval;
+}
+
+/**
+ * Tries loading the message in the given message file
+ * and verifies that DBusMessageLoader can handle it.
+ *
+ * @param filename filename to load
+ * @param expected_validity what the message has to be like to return #TRUE
+ * @returns #TRUE if the message has the expected validity
+ */
+dbus_bool_t
+dbus_internal_do_not_use_try_message_file (const DBusString    *filename,
+                                           DBusValidity         expected_validity)
+{
+  DBusString data;
+  dbus_bool_t retval;
+
+  retval = FALSE;
+
+  if (!_dbus_string_init (&data))
+    _dbus_assert_not_reached ("could not allocate string\n");
+
+  if (!dbus_internal_do_not_use_load_message_file (filename, &data))
+    goto failed;
+
+  retval = dbus_internal_do_not_use_try_message_data (&data, expected_validity);
+
+ failed:
+
+  if (!retval)
+    {
+      if (_dbus_string_get_length (&data) > 0)
+        _dbus_verbose_bytes_of_string (&data, 0,
+                                       _dbus_string_get_length (&data));
+
+      _dbus_warn ("Failed message loader test on %s\n",
+                  _dbus_string_get_const_data (filename));
+    }
+
+  _dbus_string_free (&data);
+
+  return retval;
+}
+
+/**
+ * Tries loading the given message data.
+ *
+ *
+ * @param data the message data
+ * @param expected_validity what the message has to be like to return #TRUE
+ * @returns #TRUE if the message has the expected validity
+ */
+dbus_bool_t
+dbus_internal_do_not_use_try_message_data (const DBusString    *data,
+                                           DBusValidity         expected_validity)
+{
+  DBusMessageLoader *loader;
+  dbus_bool_t retval;
+  int len;
+  int i;
+
+  loader = NULL;
+  retval = FALSE;
+
+  /* Write the data one byte at a time */
+
+  loader = _dbus_message_loader_new ();
+
+  /* check some trivial loader functions */
+  _dbus_message_loader_ref (loader);
+  _dbus_message_loader_unref (loader);
+  _dbus_message_loader_get_max_message_size (loader);
+
+  len = _dbus_string_get_length (data);
+  for (i = 0; i < len; i++)
+    {
+      DBusString *buffer;
+
+      _dbus_message_loader_get_buffer (loader, &buffer);
+      _dbus_string_append_byte (buffer,
+                                _dbus_string_get_byte (data, i));
+      _dbus_message_loader_return_buffer (loader, buffer, 1);
+    }
+
+  if (!check_loader_results (loader, expected_validity))
+    goto failed;
+
+  _dbus_message_loader_unref (loader);
+  loader = NULL;
+
+  /* Write the data all at once */
+
+  loader = _dbus_message_loader_new ();
+
+  {
+    DBusString *buffer;
+
+    _dbus_message_loader_get_buffer (loader, &buffer);
+    _dbus_string_copy (data, 0, buffer,
+                       _dbus_string_get_length (buffer));
+    _dbus_message_loader_return_buffer (loader, buffer, 1);
+  }
+
+  if (!check_loader_results (loader, expected_validity))
+    goto failed;
+
+  _dbus_message_loader_unref (loader);
+  loader = NULL;
+
+  /* Write the data 2 bytes at a time */
+
+  loader = _dbus_message_loader_new ();
+
+  len = _dbus_string_get_length (data);
+  for (i = 0; i < len; i += 2)
+    {
+      DBusString *buffer;
+
+      _dbus_message_loader_get_buffer (loader, &buffer);
+      _dbus_string_append_byte (buffer,
+                                _dbus_string_get_byte (data, i));
+      if ((i+1) < len)
+        _dbus_string_append_byte (buffer,
+                                  _dbus_string_get_byte (data, i+1));
+      _dbus_message_loader_return_buffer (loader, buffer, 1);
+    }
+
+  if (!check_loader_results (loader, expected_validity))
+    goto failed;
+
+  _dbus_message_loader_unref (loader);
+  loader = NULL;
+
+  retval = TRUE;
+
+ failed:
+
+  if (loader)
+    _dbus_message_loader_unref (loader);
+
+  return retval;
+}
+
+static dbus_bool_t
+process_test_subdir (const DBusString          *test_base_dir,
+                     const char                *subdir,
+                     DBusValidity               expected_validity,
+                     DBusForeachMessageFileFunc function,
+                     void                      *user_data)
+{
+  DBusString test_directory;
+  DBusString filename;
+  DBusDirIter *dir;
+  dbus_bool_t retval;
+  DBusError error = DBUS_ERROR_INIT;
+
+  retval = FALSE;
+  dir = NULL;
+
+  if (!_dbus_string_init (&test_directory))
+    _dbus_assert_not_reached ("didn't allocate test_directory\n");
+
+  _dbus_string_init_const (&filename, subdir);
+
+  if (!_dbus_string_copy (test_base_dir, 0,
+                          &test_directory, 0))
+    _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
+
+  if (!_dbus_concat_dir_and_file (&test_directory, &filename))
+    _dbus_assert_not_reached ("couldn't allocate full path");
+
+  _dbus_string_free (&filename);
+  if (!_dbus_string_init (&filename))
+    _dbus_assert_not_reached ("didn't allocate filename string\n");
+
+  dir = _dbus_directory_open (&test_directory, &error);
+  if (dir == NULL)
+    {
+      _dbus_warn ("Could not open %s: %s\n",
+                  _dbus_string_get_const_data (&test_directory),
+                  error.message);
+      dbus_error_free (&error);
+      goto failed;
+    }
+
+  printf ("Testing %s:\n", subdir);
+
+ next:
+  while (_dbus_directory_get_next_file (dir, &filename, &error))
+    {
+      DBusString full_path;
+
+      if (!_dbus_string_init (&full_path))
+        _dbus_assert_not_reached ("couldn't init string");
+
+      if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
+        _dbus_assert_not_reached ("couldn't copy dir to full_path");
+
+      if (!_dbus_concat_dir_and_file (&full_path, &filename))
+        _dbus_assert_not_reached ("couldn't concat file to dir");
+
+      if (_dbus_string_ends_with_c_str (&filename, ".message-raw"))
+        ;
+      else
+        {
+          if (_dbus_string_ends_with_c_str (&filename, ".message"))
+            {
+              _dbus_warn ("Could not load %s, message builder language no longer supported\n",
+                          _dbus_string_get_const_data (&filename));
+            }
+          
+          _dbus_verbose ("Skipping non-.message file %s\n",
+                         _dbus_string_get_const_data (&filename));
+         _dbus_string_free (&full_path);
+          goto next;
+        }
+
+      printf ("    %s\n",
+              _dbus_string_get_const_data (&filename));
+
+      if (! (*function) (&full_path,
+                         expected_validity, user_data))
+        {
+          _dbus_string_free (&full_path);
+          goto failed;
+        }
+      else
+        _dbus_string_free (&full_path);
+    }
+
+  if (dbus_error_is_set (&error))
+    {
+      _dbus_warn ("Could not get next file in %s: %s\n",
+                  _dbus_string_get_const_data (&test_directory),
+                  error.message);
+      dbus_error_free (&error);
+      goto failed;
+    }
+
+  retval = TRUE;
+
+ failed:
+
+  if (dir)
+    _dbus_directory_close (dir);
+  _dbus_string_free (&test_directory);
+  _dbus_string_free (&filename);
+
+  return retval;
+}
+
+/**
+ * Runs the given function on every message file in the test suite.
+ * The function should return #FALSE on test failure or fatal error.
+ *
+ * @param test_data_dir root dir of the test suite data files (top_srcdir/test/data)
+ * @param func the function to run
+ * @param user_data data for function
+ * @returns #FALSE if there's a failure
+ */
+dbus_bool_t
+dbus_internal_do_not_use_foreach_message_file (const char                *test_data_dir,
+                                               DBusForeachMessageFileFunc func,
+                                               void                      *user_data)
+{
+  DBusString test_directory;
+  dbus_bool_t retval;
+
+  retval = FALSE;
+
+  _dbus_string_init_const (&test_directory, test_data_dir);
+  
+  if (!process_test_subdir (&test_directory, "valid-messages",
+                            DBUS_VALID, func, user_data))
+    goto failed;
+
+  check_memleaks ();
+  
+  if (!process_test_subdir (&test_directory, "invalid-messages",
+                            DBUS_INVALID_FOR_UNKNOWN_REASON, func, user_data))
+    goto failed;
+
+  check_memleaks ();
+  
+  if (!process_test_subdir (&test_directory, "incomplete-messages",
+                            DBUS_VALID_BUT_INCOMPLETE, func, user_data))
+    goto failed;
+
+  check_memleaks ();
+  
+  retval = TRUE;
+  
+ failed:
+
+  _dbus_string_free (&test_directory);
+
+  return retval;
+}
+
+#if 0
+#define GET_AND_CHECK(iter, typename, literal)                                  \
+  do {                                                                          \
+    if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_##typename)         \
+      _dbus_assert_not_reached ("got wrong argument type from message iter");   \
+    dbus_message_iter_get_basic (&iter, &v_##typename);                         \
+    if (v_##typename != literal)                                                \
+      _dbus_assert_not_reached ("got wrong value from message iter");           \
+  } while (0)
+
+#define GET_AND_CHECK_STRCMP(iter, typename, literal)                           \
+  do {                                                                          \
+    if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_##typename)         \
+      _dbus_assert_not_reached ("got wrong argument type from message iter");   \
+    dbus_message_iter_get_basic (&iter, &v_##typename);                         \
+    if (strcmp (v_##typename, literal) != 0)                                    \
+      _dbus_assert_not_reached ("got wrong value from message iter");           \
+  } while (0)
+
+#define GET_AND_CHECK_AND_NEXT(iter, typename, literal)         \
+  do {                                                          \
+    GET_AND_CHECK(iter, typename, literal);                     \
+    if (!dbus_message_iter_next (&iter))                        \
+      _dbus_assert_not_reached ("failed to move iter to next"); \
+  } while (0)
+
+#define GET_AND_CHECK_STRCMP_AND_NEXT(iter, typename, literal)  \
+  do {                                                          \
+    GET_AND_CHECK_STRCMP(iter, typename, literal);              \
+    if (!dbus_message_iter_next (&iter))                        \
+      _dbus_assert_not_reached ("failed to move iter to next"); \
+  } while (0)
+
+static void
+message_iter_test (DBusMessage *message)
+{
+  DBusMessageIter iter, array, array2;
+  const char *v_STRING;
+  double v_DOUBLE;
+  dbus_int16_t v_INT16;
+  dbus_uint16_t v_UINT16;
+  dbus_int32_t v_INT32;
+  dbus_uint32_t v_UINT32;
+#ifdef DBUS_HAVE_INT64
+  dbus_int64_t v_INT64;
+  dbus_uint64_t v_UINT64;
+#endif
+  unsigned char v_BYTE;
+  dbus_bool_t v_BOOLEAN;
+
+  const dbus_int32_t *our_int_array;
+  int len;
+
+  dbus_message_iter_init (message, &iter);
+
+  GET_AND_CHECK_STRCMP_AND_NEXT (iter, STRING, "Test string");
+  GET_AND_CHECK_AND_NEXT (iter, INT32, -0x12345678);
+  GET_AND_CHECK_AND_NEXT (iter, UINT32, 0xedd1e);
+  GET_AND_CHECK_AND_NEXT (iter, DOUBLE, 3.14159);
+
+  if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY)
+    _dbus_assert_not_reached ("Argument type not an array");
+
+  if (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_DOUBLE)
+    _dbus_assert_not_reached ("Array type not double");
+
+  dbus_message_iter_recurse (&iter, &array);
+
+  GET_AND_CHECK_AND_NEXT (array, DOUBLE, 1.5);
+  GET_AND_CHECK (array, DOUBLE, 2.5);
+
+  if (dbus_message_iter_next (&array))
+    _dbus_assert_not_reached ("Didn't reach end of array");
+
+  if (!dbus_message_iter_next (&iter))
+    _dbus_assert_not_reached ("Reached end of arguments");
+
+  GET_AND_CHECK_AND_NEXT (iter, BYTE, 0xF0);
+
+  if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY)
+    _dbus_assert_not_reached ("no array");
+
+  if (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_INT32)
+    _dbus_assert_not_reached ("Array type not int32");
+
+  /* Empty array */
+  dbus_message_iter_recurse (&iter, &array);
+
+  if (dbus_message_iter_next (&array))
+    _dbus_assert_not_reached ("Didn't reach end of array");
+
+  if (!dbus_message_iter_next (&iter))
+    _dbus_assert_not_reached ("Reached end of arguments");
+
+  GET_AND_CHECK (iter, BYTE, 0xF0);
+
+  if (dbus_message_iter_next (&iter))
+    _dbus_assert_not_reached ("Didn't reach end of arguments");
+}
+#endif
+
+static void
+verify_test_message (DBusMessage *message)
+{
+  DBusMessageIter iter;
+  DBusError error = DBUS_ERROR_INIT;
+  dbus_int16_t our_int16;
+  dbus_uint16_t our_uint16;
+  dbus_int32_t our_int;
+  dbus_uint32_t our_uint;
+  const char *our_str;
+  double our_double;
+  double v_DOUBLE;
+  dbus_bool_t our_bool;
+  unsigned char our_byte_1, our_byte_2;
+  const dbus_uint32_t *our_uint32_array = (void*)0xdeadbeef;
+  int our_uint32_array_len;
+  dbus_int32_t *our_int32_array = (void*)0xdeadbeef;
+  int our_int32_array_len;
+#ifdef DBUS_HAVE_INT64
+  dbus_int64_t our_int64;
+  dbus_uint64_t our_uint64;
+  dbus_int64_t *our_uint64_array = (void*)0xdeadbeef;
+  int our_uint64_array_len;
+  const dbus_int64_t *our_int64_array = (void*)0xdeadbeef;
+  int our_int64_array_len;
+#endif
+  const double *our_double_array = (void*)0xdeadbeef;
+  int our_double_array_len;
+  const unsigned char *our_byte_array = (void*)0xdeadbeef;
+  int our_byte_array_len;
+  const dbus_bool_t *our_boolean_array = (void*)0xdeadbeef;
+  int our_boolean_array_len;
+  char **our_string_array;
+  int our_string_array_len;
+
+  dbus_message_iter_init (message, &iter);
+
+  if (!dbus_message_iter_get_args (&iter, &error,
+                                   DBUS_TYPE_INT16, &our_int16,
+                                   DBUS_TYPE_UINT16, &our_uint16,
+                                  DBUS_TYPE_INT32, &our_int,
+                                   DBUS_TYPE_UINT32, &our_uint,
+#ifdef DBUS_HAVE_INT64
+                                   DBUS_TYPE_INT64, &our_int64,
+                                   DBUS_TYPE_UINT64, &our_uint64,
+#endif
+                                  DBUS_TYPE_STRING, &our_str,
+                                  DBUS_TYPE_DOUBLE, &our_double,
+                                  DBUS_TYPE_BOOLEAN, &our_bool,
+                                  DBUS_TYPE_BYTE, &our_byte_1,
+                                  DBUS_TYPE_BYTE, &our_byte_2,
+                                  DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
+                                   &our_uint32_array, &our_uint32_array_len,
+                                   DBUS_TYPE_ARRAY, DBUS_TYPE_INT32,
+                                   &our_int32_array, &our_int32_array_len,
+#ifdef DBUS_HAVE_INT64
+                                  DBUS_TYPE_ARRAY, DBUS_TYPE_UINT64,
+                                   &our_uint64_array, &our_uint64_array_len,
+                                   DBUS_TYPE_ARRAY, DBUS_TYPE_INT64,
+                                   &our_int64_array, &our_int64_array_len,
+#endif
+                                   DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE,
+                                   &our_double_array, &our_double_array_len,
+                                   DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+                                   &our_byte_array, &our_byte_array_len,
+                                   DBUS_TYPE_ARRAY, DBUS_TYPE_BOOLEAN,
+                                   &our_boolean_array, &our_boolean_array_len,
+                                   DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
+                                   &our_string_array, &our_string_array_len,
+                                  0))
+    {
+      _dbus_warn ("error: %s - %s\n", error.name,
+                  (error.message != NULL) ? error.message : "no message");
+      _dbus_assert_not_reached ("Could not get arguments");
+    }
+
+  if (our_int16 != -0x123)
+    _dbus_assert_not_reached ("16-bit integers differ!");
+
+  if (our_uint16 != 0x123)
+    _dbus_assert_not_reached ("16-bit uints differ!");
+  
+  if (our_int != -0x12345678)
+    _dbus_assert_not_reached ("integers differ!");
+
+  if (our_uint != 0x12300042)
+    _dbus_assert_not_reached ("uints differ!");
+
+#ifdef DBUS_HAVE_INT64
+  if (our_int64 != DBUS_INT64_CONSTANT (-0x123456789abcd))
+    _dbus_assert_not_reached ("64-bit integers differ!");
+  if (our_uint64 != DBUS_UINT64_CONSTANT (0x123456789abcd))
+    _dbus_assert_not_reached ("64-bit unsigned integers differ!");
+#endif
+
+  v_DOUBLE = 3.14159;
+  if (! _DBUS_DOUBLES_BITWISE_EQUAL (our_double, v_DOUBLE))
+    _dbus_assert_not_reached ("doubles differ!");
+
+  if (strcmp (our_str, "Test string") != 0)
+    _dbus_assert_not_reached ("strings differ!");
+
+  if (!our_bool)
+    _dbus_assert_not_reached ("booleans differ");
+
+  if (our_byte_1 != 42)
+    _dbus_assert_not_reached ("bytes differ!");
+
+  if (our_byte_2 != 24)
+    _dbus_assert_not_reached ("bytes differ!");
+
+  if (our_uint32_array_len != 4 ||
+      our_uint32_array[0] != 0x12345678 ||
+      our_uint32_array[1] != 0x23456781 ||
+      our_uint32_array[2] != 0x34567812 ||
+      our_uint32_array[3] != 0x45678123)
+    _dbus_assert_not_reached ("uint array differs");
+
+  if (our_int32_array_len != 4 ||
+      our_int32_array[0] != 0x12345678 ||
+      our_int32_array[1] != -0x23456781 ||
+      our_int32_array[2] != 0x34567812 ||
+      our_int32_array[3] != -0x45678123)
+    _dbus_assert_not_reached ("int array differs");
+
+#ifdef DBUS_HAVE_INT64
+  if (our_uint64_array_len != 4 ||
+      our_uint64_array[0] != 0x12345678 ||
+      our_uint64_array[1] != 0x23456781 ||
+      our_uint64_array[2] != 0x34567812 ||
+      our_uint64_array[3] != 0x45678123)
+    _dbus_assert_not_reached ("uint64 array differs");
+
+  if (our_int64_array_len != 4 ||
+      our_int64_array[0] != 0x12345678 ||
+      our_int64_array[1] != -0x23456781 ||
+      our_int64_array[2] != 0x34567812 ||
+      our_int64_array[3] != -0x45678123)
+    _dbus_assert_not_reached ("int64 array differs");
+#endif /* DBUS_HAVE_INT64 */
+
+  if (our_double_array_len != 3)
+    _dbus_assert_not_reached ("double array had wrong length");
+
+  /* On all IEEE machines (i.e. everything sane) exact equality
+   * should be preserved over the wire
+   */
+  v_DOUBLE = 0.1234;
+  if (! _DBUS_DOUBLES_BITWISE_EQUAL (our_double_array[0], v_DOUBLE))
+    _dbus_assert_not_reached ("double array had wrong values");
+  v_DOUBLE = 9876.54321;
+  if (! _DBUS_DOUBLES_BITWISE_EQUAL (our_double_array[1], v_DOUBLE))
+    _dbus_assert_not_reached ("double array had wrong values");
+  v_DOUBLE = -300.0;
+  if (! _DBUS_DOUBLES_BITWISE_EQUAL (our_double_array[2], v_DOUBLE))
+    _dbus_assert_not_reached ("double array had wrong values");
+
+  if (our_byte_array_len != 4)
+    _dbus_assert_not_reached ("byte array had wrong length");
+
+  if (our_byte_array[0] != 'a' ||
+      our_byte_array[1] != 'b' ||
+      our_byte_array[2] != 'c' ||
+      our_byte_array[3] != 234)
+    _dbus_assert_not_reached ("byte array had wrong values");
+
+  if (our_boolean_array_len != 5)
+    _dbus_assert_not_reached ("bool array had wrong length");
+
+  if (our_boolean_array[0] != TRUE ||
+      our_boolean_array[1] != FALSE ||
+      our_boolean_array[2] != TRUE ||
+      our_boolean_array[3] != TRUE ||
+      our_boolean_array[4] != FALSE)
+    _dbus_assert_not_reached ("bool array had wrong values");
+
+  if (our_string_array_len != 4)
+    _dbus_assert_not_reached ("string array was wrong length");
+
+  if (strcmp (our_string_array[0], "Foo") != 0 ||
+      strcmp (our_string_array[1], "bar") != 0 ||
+      strcmp (our_string_array[2], "") != 0 ||
+      strcmp (our_string_array[3], "woo woo woo woo") != 0)
+    _dbus_assert_not_reached ("string array had wrong values");
+
+  dbus_free_string_array (our_string_array);
+  
+  if (dbus_message_iter_next (&iter))
+    _dbus_assert_not_reached ("Didn't reach end of arguments");
+}
+
+/**
+ * @ingroup DBusMessageInternals
+ * Unit test for DBusMessage.
+ *
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_message_test (const char *test_data_dir)
+{
+  DBusMessage *message;
+  DBusMessageLoader *loader;
+  int i;
+  const char *data;
+  DBusMessage *copy;
+  const char *name1;
+  const char *name2;
+  const dbus_uint32_t our_uint32_array[] =
+    { 0x12345678, 0x23456781, 0x34567812, 0x45678123 };
+  const dbus_int32_t our_int32_array[] =
+    { 0x12345678, -0x23456781, 0x34567812, -0x45678123 };
+  const dbus_uint32_t *v_ARRAY_UINT32 = our_uint32_array;
+  const dbus_int32_t *v_ARRAY_INT32 = our_int32_array;
+#ifdef DBUS_HAVE_INT64
+  const dbus_uint64_t our_uint64_array[] =
+    { 0x12345678, 0x23456781, 0x34567812, 0x45678123 };
+  const dbus_int64_t our_int64_array[] =
+    { 0x12345678, -0x23456781, 0x34567812, -0x45678123 };
+  const dbus_uint64_t *v_ARRAY_UINT64 = our_uint64_array;
+  const dbus_int64_t *v_ARRAY_INT64 = our_int64_array;
+#endif
+  const char *our_string_array[] = { "Foo", "bar", "", "woo woo woo woo" };
+  const char **v_ARRAY_STRING = our_string_array;
+  const double our_double_array[] = { 0.1234, 9876.54321, -300.0 };
+  const double *v_ARRAY_DOUBLE = our_double_array;
+  const unsigned char our_byte_array[] = { 'a', 'b', 'c', 234 };
+  const unsigned char *v_ARRAY_BYTE = our_byte_array;
+  const dbus_bool_t our_boolean_array[] = { TRUE, FALSE, TRUE, TRUE, FALSE };
+  const dbus_bool_t *v_ARRAY_BOOLEAN = our_boolean_array;
+  char sig[64];
+  const char *s;
+  const char *v_STRING;
+  double v_DOUBLE;
+  dbus_int16_t v_INT16;
+  dbus_uint16_t v_UINT16;
+  dbus_int32_t v_INT32;
+  dbus_uint32_t v_UINT32;
+#ifdef DBUS_HAVE_INT64
+  dbus_int64_t v_INT64;
+  dbus_uint64_t v_UINT64;
+#endif
+  unsigned char v_BYTE;
+  unsigned char v2_BYTE;
+  dbus_bool_t v_BOOLEAN;
+
+  message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService",
+                                          "/org/freedesktop/TestPath",
+                                          "Foo.TestInterface",
+                                          "TestMethod");
+  _dbus_assert (dbus_message_has_destination (message, "org.freedesktop.DBus.TestService"));
+  _dbus_assert (dbus_message_is_method_call (message, "Foo.TestInterface",
+                                             "TestMethod"));
+  _dbus_assert (strcmp (dbus_message_get_path (message),
+                        "/org/freedesktop/TestPath") == 0);
+  dbus_message_set_serial (message, 1234);
+
+  /* string length including nul byte not a multiple of 4 */
+  if (!dbus_message_set_sender (message, "org.foo.bar1"))
+    _dbus_assert_not_reached ("out of memory");
+
+  _dbus_assert (dbus_message_has_sender (message, "org.foo.bar1"));
+  dbus_message_set_reply_serial (message, 5678);
+
+  _dbus_verbose_bytes_of_string (&message->header.data, 0,
+                                 _dbus_string_get_length (&message->header.data));
+  _dbus_verbose_bytes_of_string (&message->body, 0,
+                                 _dbus_string_get_length (&message->body));
+
+  if (!dbus_message_set_sender (message, NULL))
+    _dbus_assert_not_reached ("out of memory");
+
+
+  _dbus_verbose_bytes_of_string (&message->header.data, 0,
+                                 _dbus_string_get_length (&message->header.data));
+  _dbus_verbose_bytes_of_string (&message->body, 0,
+                                 _dbus_string_get_length (&message->body));
+
+
+  _dbus_assert (!dbus_message_has_sender (message, "org.foo.bar1"));
+  _dbus_assert (dbus_message_get_serial (message) == 1234);
+  _dbus_assert (dbus_message_get_reply_serial (message) == 5678);
+  _dbus_assert (dbus_message_has_destination (message, "org.freedesktop.DBus.TestService"));
+
+  _dbus_assert (dbus_message_get_no_reply (message) == FALSE);
+  dbus_message_set_no_reply (message, TRUE);
+  _dbus_assert (dbus_message_get_no_reply (message) == TRUE);
+  dbus_message_set_no_reply (message, FALSE);
+  _dbus_assert (dbus_message_get_no_reply (message) == FALSE);
+
+  /* Set/get some header fields */
+
+  if (!dbus_message_set_path (message, "/foo"))
+    _dbus_assert_not_reached ("out of memory");
+  _dbus_assert (strcmp (dbus_message_get_path (message),
+                        "/foo") == 0);
+
+  if (!dbus_message_set_interface (message, "org.Foo"))
+    _dbus_assert_not_reached ("out of memory");
+  _dbus_assert (strcmp (dbus_message_get_interface (message),
+                        "org.Foo") == 0);
+
+  if (!dbus_message_set_member (message, "Bar"))
+    _dbus_assert_not_reached ("out of memory");
+  _dbus_assert (strcmp (dbus_message_get_member (message),
+                        "Bar") == 0);
+
+  /* Set/get them with longer values */
+  if (!dbus_message_set_path (message, "/foo/bar"))
+    _dbus_assert_not_reached ("out of memory");
+  _dbus_assert (strcmp (dbus_message_get_path (message),
+                        "/foo/bar") == 0);
+
+  if (!dbus_message_set_interface (message, "org.Foo.Bar"))
+    _dbus_assert_not_reached ("out of memory");
+  _dbus_assert (strcmp (dbus_message_get_interface (message),
+                        "org.Foo.Bar") == 0);
+
+  if (!dbus_message_set_member (message, "BarFoo"))
+    _dbus_assert_not_reached ("out of memory");
+  _dbus_assert (strcmp (dbus_message_get_member (message),
+                        "BarFoo") == 0);
+
+  /* Realloc shorter again */
+
+  if (!dbus_message_set_path (message, "/foo"))
+    _dbus_assert_not_reached ("out of memory");
+  _dbus_assert (strcmp (dbus_message_get_path (message),
+                        "/foo") == 0);
+
+  if (!dbus_message_set_interface (message, "org.Foo"))
+    _dbus_assert_not_reached ("out of memory");
+  _dbus_assert (strcmp (dbus_message_get_interface (message),
+                        "org.Foo") == 0);
+
+  if (!dbus_message_set_member (message, "Bar"))
+    _dbus_assert_not_reached ("out of memory");
+  _dbus_assert (strcmp (dbus_message_get_member (message),
+                        "Bar") == 0);
+
+  dbus_message_unref (message);
+
+  /* Test the vararg functions */
+  message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService",
+                                          "/org/freedesktop/TestPath",
+                                          "Foo.TestInterface",
+                                          "TestMethod");
+  dbus_message_set_serial (message, 1);
+  dbus_message_set_reply_serial (message, 5678);
+
+  v_INT16 = -0x123;
+  v_UINT16 = 0x123;
+  v_INT32 = -0x12345678;
+  v_UINT32 = 0x12300042;
+#ifdef DBUS_HAVE_INT64
+  v_INT64 = DBUS_INT64_CONSTANT (-0x123456789abcd);
+  v_UINT64 = DBUS_UINT64_CONSTANT (0x123456789abcd);
+#endif
+  v_STRING = "Test string";
+  v_DOUBLE = 3.14159;
+  v_BOOLEAN = TRUE;
+  v_BYTE = 42;
+  v2_BYTE = 24;
+
+  dbus_message_append_args (message,
+                            DBUS_TYPE_INT16, &v_INT16,
+                            DBUS_TYPE_UINT16, &v_UINT16,
+                           DBUS_TYPE_INT32, &v_INT32,
+                            DBUS_TYPE_UINT32, &v_UINT32,
+#ifdef DBUS_HAVE_INT64
+                            DBUS_TYPE_INT64, &v_INT64,
+                            DBUS_TYPE_UINT64, &v_UINT64,
+#endif
+                           DBUS_TYPE_STRING, &v_STRING,
+                           DBUS_TYPE_DOUBLE, &v_DOUBLE,
+                           DBUS_TYPE_BOOLEAN, &v_BOOLEAN,
+                           DBUS_TYPE_BYTE, &v_BYTE,
+                           DBUS_TYPE_BYTE, &v2_BYTE,
+                           DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &v_ARRAY_UINT32,
+                            _DBUS_N_ELEMENTS (our_uint32_array),
+                            DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &v_ARRAY_INT32,
+                            _DBUS_N_ELEMENTS (our_int32_array),
+#ifdef DBUS_HAVE_INT64
+                            DBUS_TYPE_ARRAY, DBUS_TYPE_UINT64, &v_ARRAY_UINT64,
+                            _DBUS_N_ELEMENTS (our_uint64_array),
+                            DBUS_TYPE_ARRAY, DBUS_TYPE_INT64, &v_ARRAY_INT64,
+                            _DBUS_N_ELEMENTS (our_int64_array),
+#endif
+                            DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &v_ARRAY_DOUBLE,
+                            _DBUS_N_ELEMENTS (our_double_array),
+                            DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &v_ARRAY_BYTE,
+                            _DBUS_N_ELEMENTS (our_byte_array),
+                            DBUS_TYPE_ARRAY, DBUS_TYPE_BOOLEAN, &v_ARRAY_BOOLEAN,
+                            _DBUS_N_ELEMENTS (our_boolean_array),
+                            DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &v_ARRAY_STRING,
+                            _DBUS_N_ELEMENTS (our_string_array),
+                           DBUS_TYPE_INVALID);
+
+  i = 0;
+  sig[i++] = DBUS_TYPE_INT16;
+  sig[i++] = DBUS_TYPE_UINT16;
+  sig[i++] = DBUS_TYPE_INT32;
+  sig[i++] = DBUS_TYPE_UINT32;
+#ifdef DBUS_HAVE_INT64
+  sig[i++] = DBUS_TYPE_INT64;
+  sig[i++] = DBUS_TYPE_UINT64;
+#endif
+  sig[i++] = DBUS_TYPE_STRING;
+  sig[i++] = DBUS_TYPE_DOUBLE;
+  sig[i++] = DBUS_TYPE_BOOLEAN;
+  sig[i++] = DBUS_TYPE_BYTE;
+  sig[i++] = DBUS_TYPE_BYTE;
+  sig[i++] = DBUS_TYPE_ARRAY;
+  sig[i++] = DBUS_TYPE_UINT32;
+  sig[i++] = DBUS_TYPE_ARRAY;
+  sig[i++] = DBUS_TYPE_INT32;
+#ifdef DBUS_HAVE_INT64
+  sig[i++] = DBUS_TYPE_ARRAY;
+  sig[i++] = DBUS_TYPE_UINT64;
+  sig[i++] = DBUS_TYPE_ARRAY;
+  sig[i++] = DBUS_TYPE_INT64;
+#endif
+  sig[i++] = DBUS_TYPE_ARRAY;
+  sig[i++] = DBUS_TYPE_DOUBLE;
+  sig[i++] = DBUS_TYPE_ARRAY;
+  sig[i++] = DBUS_TYPE_BYTE;
+  sig[i++] = DBUS_TYPE_ARRAY;
+  sig[i++] = DBUS_TYPE_BOOLEAN;
+  sig[i++] = DBUS_TYPE_ARRAY;
+  sig[i++] = DBUS_TYPE_STRING;
+  sig[i++] = DBUS_TYPE_INVALID;  
+
+  _dbus_assert (i < (int) _DBUS_N_ELEMENTS (sig));
+
+  _dbus_verbose ("HEADER\n");
+  _dbus_verbose_bytes_of_string (&message->header.data, 0,
+                                 _dbus_string_get_length (&message->header.data));
+  _dbus_verbose ("BODY\n");
+  _dbus_verbose_bytes_of_string (&message->body, 0,
+                                 _dbus_string_get_length (&message->body));
+
+  _dbus_verbose ("Signature expected \"%s\" actual \"%s\"\n",
+                 sig, dbus_message_get_signature (message));
+
+  s = dbus_message_get_signature (message);
+
+  _dbus_assert (dbus_message_has_signature (message, sig));
+  _dbus_assert (strcmp (s, sig) == 0);
+
+  verify_test_message (message);
+
+  copy = dbus_message_copy (message);
+
+  _dbus_assert (dbus_message_get_reply_serial (message) ==
+                dbus_message_get_reply_serial (copy));
+  _dbus_assert (message->header.padding == copy->header.padding);
+
+  _dbus_assert (_dbus_string_get_length (&message->header.data) ==
+                _dbus_string_get_length (&copy->header.data));
+
+  _dbus_assert (_dbus_string_get_length (&message->body) ==
+                _dbus_string_get_length (&copy->body));
+
+  verify_test_message (copy);
+
+  name1 = dbus_message_get_interface (message);
+  name2 = dbus_message_get_interface (copy);
+
+  _dbus_assert (strcmp (name1, name2) == 0);
+
+  name1 = dbus_message_get_member (message);
+  name2 = dbus_message_get_member (copy);
+
+  _dbus_assert (strcmp (name1, name2) == 0);
+
+  dbus_message_unref (copy);
+
+  /* Message loader test */
+  dbus_message_lock (message);
+  loader = _dbus_message_loader_new ();
+  
+  /* check ref/unref */
+  _dbus_message_loader_ref (loader);
+  _dbus_message_loader_unref (loader);
+
+  /* Write the header data one byte at a time */
+  data = _dbus_string_get_const_data (&message->header.data);
+  for (i = 0; i < _dbus_string_get_length (&message->header.data); i++)
+    {
+      DBusString *buffer;
+
+      _dbus_message_loader_get_buffer (loader, &buffer);
+      _dbus_string_append_byte (buffer, data[i]);
+      _dbus_message_loader_return_buffer (loader, buffer, 1);
+    }
+
+  /* Write the body data one byte at a time */
+  data = _dbus_string_get_const_data (&message->body);
+  for (i = 0; i < _dbus_string_get_length (&message->body); i++)
+    {
+      DBusString *buffer;
+
+      _dbus_message_loader_get_buffer (loader, &buffer);
+      _dbus_string_append_byte (buffer, data[i]);
+      _dbus_message_loader_return_buffer (loader, buffer, 1);
+    }
+
+  dbus_message_unref (message);
+
+  /* Now pop back the message */
+  if (!_dbus_message_loader_queue_messages (loader))
+    _dbus_assert_not_reached ("no memory to queue messages");
+
+  if (_dbus_message_loader_get_is_corrupted (loader))
+    _dbus_assert_not_reached ("message loader corrupted");
+
+  message = _dbus_message_loader_pop_message (loader);
+  if (!message)
+    _dbus_assert_not_reached ("received a NULL message");
+
+  if (dbus_message_get_reply_serial (message) != 5678)
+    _dbus_assert_not_reached ("reply serial fields differ");
+
+  verify_test_message (message);
+
+    {
+      /* Marshal and demarshal the message. */
+
+      DBusMessage *message2;
+      DBusError error = DBUS_ERROR_INIT;
+      char *marshalled = NULL;
+      int len = 0;
+      char garbage_header[DBUS_MINIMUM_HEADER_SIZE] = "xxx";
+
+      if (!dbus_message_marshal (message, &marshalled, &len))
+        _dbus_assert_not_reached ("failed to marshal message");
+
+      _dbus_assert (len != 0);
+      _dbus_assert (marshalled != NULL);
+
+      _dbus_assert (dbus_message_demarshal_bytes_needed (marshalled, len) == len);
+      message2 = dbus_message_demarshal (marshalled, len, &error);
+
+      _dbus_assert (message2 != NULL);
+      _dbus_assert (!dbus_error_is_set (&error));
+      verify_test_message (message2);
+
+      dbus_message_unref (message2);
+      dbus_free (marshalled);
+
+      /* Demarshal invalid message. */
+
+      message2 = dbus_message_demarshal ("invalid", 7, &error);
+      _dbus_assert (message2 == NULL);
+      _dbus_assert (dbus_error_is_set (&error));
+      dbus_error_free (&error);
+
+      /* Demarshal invalid (empty) message. */
+
+      message2 = dbus_message_demarshal ("", 0, &error);
+      _dbus_assert (message2 == NULL);
+      _dbus_assert (dbus_error_is_set (&error));
+      dbus_error_free (&error);
+
+      /* Bytes needed to demarshal empty message: 0 (more) */
+
+      _dbus_assert (dbus_message_demarshal_bytes_needed ("", 0) == 0);
+      
+      /* Bytes needed to demarshal invalid message: -1 (error). */
+
+      _dbus_assert (dbus_message_demarshal_bytes_needed (garbage_header, DBUS_MINIMUM_HEADER_SIZE) == -1);
+    }
+
+  dbus_message_unref (message);
+  _dbus_message_loader_unref (loader);
+
+  check_memleaks ();
+
+  /* Load all the sample messages from the message factory */
+  {
+    DBusMessageDataIter diter;
+    DBusMessageData mdata;
+    int count;
+
+    reset_validities_seen ();
+    
+    count = 0;
+    _dbus_message_data_iter_init (&diter);
+    
+    while (_dbus_message_data_iter_get_and_next (&diter,
+                                                 &mdata))
+      {
+        if (!dbus_internal_do_not_use_try_message_data (&mdata.data,
+                                                        mdata.expected_validity))
+          {
+            _dbus_warn ("expected validity %d and did not get it\n",
+                        mdata.expected_validity);
+            _dbus_assert_not_reached ("message data failed");
+          }
+
+        _dbus_message_data_free (&mdata);
+
+        count += 1;
+      }
+
+    printf ("%d sample messages tested\n", count);
+
+    print_validities_seen (FALSE);
+    print_validities_seen (TRUE);
+  }
+  
+  check_memleaks ();
+  
+  /* Now load every message in test_data_dir if we have one */
+  if (test_data_dir == NULL)
+    return TRUE;
+
+  return dbus_internal_do_not_use_foreach_message_file (test_data_dir,
+                                                        (DBusForeachMessageFileFunc)
+                                                        dbus_internal_do_not_use_try_message_file,
+                                                        NULL);  
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-message.c b/src/dbus/dbus-message.c
new file mode 100644 (file)
index 0000000..edae425
--- /dev/null
@@ -0,0 +1,4083 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-message.c  DBusMessage object
+ *
+ * Copyright (C) 2002, 2003, 2004, 2005  Red Hat Inc.
+ * Copyright (C) 2002, 2003  CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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-internals.h"
+#include "dbus-marshal-recursive.h"
+#include "dbus-marshal-validate.h"
+#include "dbus-marshal-byteswap.h"
+#include "dbus-marshal-header.h"
+#include "dbus-signature.h"
+#include "dbus-message-private.h"
+#include "dbus-object-tree.h"
+#include "dbus-memory.h"
+#include "dbus-list.h"
+#include "dbus-threads-internal.h"
+#include <string.h>
+
+static void dbus_message_finalize (DBusMessage *message);
+
+/**
+ * @defgroup DBusMessageInternals DBusMessage implementation details
+ * @ingroup DBusInternals
+ * @brief DBusMessage private implementation details.
+ *
+ * The guts of DBusMessage and its methods.
+ *
+ * @{
+ */
+
+/* Not thread locked, but strictly const/read-only so should be OK
+ */
+/** An static string representing an empty signature */
+_DBUS_STRING_DEFINE_STATIC(_dbus_empty_signature_str,  "");
+
+/* these have wacky values to help trap uninitialized iterators;
+ * but has to fit in 3 bits
+ */
+enum {
+  DBUS_MESSAGE_ITER_TYPE_READER = 3,
+  DBUS_MESSAGE_ITER_TYPE_WRITER = 7
+};
+
+/** typedef for internals of message iterator */
+typedef struct DBusMessageRealIter DBusMessageRealIter;
+
+/**
+ * @brief Internals of DBusMessageIter
+ *
+ * Object representing a position in a message. All fields are internal.
+ */
+struct DBusMessageRealIter
+{
+  DBusMessage *message; /**< Message used */
+  dbus_uint32_t changed_stamp : CHANGED_STAMP_BITS; /**< stamp to detect invalid iters */
+  dbus_uint32_t iter_type : 3;      /**< whether this is a reader or writer iter */
+  dbus_uint32_t sig_refcount : 8;   /**< depth of open_signature() */
+  union
+  {
+    DBusTypeWriter writer; /**< writer */
+    DBusTypeReader reader; /**< reader */
+  } u; /**< the type writer or reader that does all the work */
+};
+
+static void
+get_const_signature (DBusHeader        *header,
+                     const DBusString **type_str_p,
+                     int               *type_pos_p)
+{
+  if (_dbus_header_get_field_raw (header,
+                                  DBUS_HEADER_FIELD_SIGNATURE,
+                                  type_str_p,
+                                  type_pos_p))
+    {
+      *type_pos_p += 1; /* skip the signature length which is 1 byte */
+    }
+  else
+    {
+      *type_str_p = &_dbus_empty_signature_str;
+      *type_pos_p = 0;
+    }
+}
+
+/**
+ * Swaps the message to compiler byte order if required
+ *
+ * @param message the message
+ */
+static void
+_dbus_message_byteswap (DBusMessage *message)
+{
+  const DBusString *type_str;
+  int type_pos;
+  
+  if (message->byte_order == DBUS_COMPILER_BYTE_ORDER)
+    return;
+
+  _dbus_verbose ("Swapping message into compiler byte order\n");
+  
+  get_const_signature (&message->header, &type_str, &type_pos);
+  
+  _dbus_marshal_byteswap (type_str, type_pos,
+                          message->byte_order,
+                          DBUS_COMPILER_BYTE_ORDER,
+                          &message->body, 0);
+
+  message->byte_order = DBUS_COMPILER_BYTE_ORDER;
+  
+  _dbus_header_byteswap (&message->header, DBUS_COMPILER_BYTE_ORDER);
+}
+
+/** byte-swap the message if it doesn't match our byte order.
+ *  Called only when we need the message in our own byte order,
+ *  normally when reading arrays of integers or doubles.
+ *  Otherwise should not be called since it would do needless
+ *  work.
+ */
+#define ensure_byte_order(message)                      \
+ if (message->byte_order != DBUS_COMPILER_BYTE_ORDER)   \
+   _dbus_message_byteswap (message)
+
+/**
+ * Gets the data to be sent over the network for this message.
+ * The header and then the body should be written out.
+ * This function is guaranteed to always return the same
+ * data once a message is locked (with dbus_message_lock()).
+ *
+ * @param message the message.
+ * @param header return location for message header data.
+ * @param body return location for message body data.
+ */
+void
+_dbus_message_get_network_data (DBusMessage          *message,
+                                const DBusString    **header,
+                                const DBusString    **body)
+{
+  _dbus_assert (message->locked);
+
+  *header = &message->header.data;
+  *body = &message->body;
+}
+
+/**
+ * Sets the serial number of a message.
+ * This can only be done once on a message.
+ *
+ * DBusConnection will automatically set the serial to an appropriate value 
+ * when the message is sent; this function is only needed when encapsulating 
+ * messages in another protocol, or otherwise bypassing DBusConnection.
+ *
+ * @param message the message
+ * @param serial the serial
+ */
+void 
+dbus_message_set_serial (DBusMessage   *message,
+                         dbus_uint32_t  serial)
+{
+  _dbus_return_if_fail (message != NULL);
+  _dbus_return_if_fail (!message->locked);
+
+  _dbus_header_set_serial (&message->header, serial);
+}
+
+/**
+ * Adds a counter to be incremented immediately with the
+ * size of this message, and decremented by the size
+ * of this message when this message if finalized.
+ * The link contains a counter with its refcount already
+ * incremented, but the counter itself not incremented.
+ * Ownership of link and counter refcount is passed to
+ * the message.
+ *
+ * @param message the message
+ * @param link link with counter as data
+ */
+void
+_dbus_message_add_size_counter_link (DBusMessage  *message,
+                                     DBusList     *link)
+{
+  /* right now we don't recompute the delta when message
+   * size changes, and that's OK for current purposes
+   * I think, but could be important to change later.
+   * Do recompute it whenever there are no outstanding counters,
+   * since it's basically free.
+   */
+  if (message->size_counters == NULL)
+    {
+      message->size_counter_delta =
+        _dbus_string_get_length (&message->header.data) +
+        _dbus_string_get_length (&message->body);
+
+#if 0
+      _dbus_verbose ("message has size %ld\n",
+                     message->size_counter_delta);
+#endif
+    }
+
+  _dbus_list_append_link (&message->size_counters, link);
+
+  _dbus_counter_adjust (link->data, message->size_counter_delta);
+}
+
+/**
+ * Adds a counter to be incremented immediately with the
+ * size of this message, and decremented by the size
+ * of this message when this message if finalized.
+ *
+ * @param message the message
+ * @param counter the counter
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_message_add_size_counter (DBusMessage *message,
+                                DBusCounter *counter)
+{
+  DBusList *link;
+
+  link = _dbus_list_alloc_link (counter);
+  if (link == NULL)
+    return FALSE;
+
+  _dbus_counter_ref (counter);
+  _dbus_message_add_size_counter_link (message, link);
+
+  return TRUE;
+}
+
+/**
+ * Removes a counter tracking the size of this message, and decrements
+ * the counter by the size of this message.
+ *
+ * @param message the message
+ * @param link_return return the link used
+ * @param counter the counter
+ */
+void
+_dbus_message_remove_size_counter (DBusMessage  *message,
+                                   DBusCounter  *counter,
+                                   DBusList    **link_return)
+{
+  DBusList *link;
+
+  link = _dbus_list_find_last (&message->size_counters,
+                               counter);
+  _dbus_assert (link != NULL);
+
+  _dbus_list_unlink (&message->size_counters,
+                     link);
+  if (link_return)
+    *link_return = link;
+  else
+    _dbus_list_free_link (link);
+
+  _dbus_counter_adjust (counter, - message->size_counter_delta);
+
+  _dbus_counter_unref (counter);
+}
+
+/**
+ * Locks a message. Allows checking that applications don't keep a
+ * reference to a message in the outgoing queue and change it
+ * underneath us. Messages are locked when they enter the outgoing
+ * queue (dbus_connection_send_message()), and the library complains
+ * if the message is modified while locked. This function may also 
+ * called externally, for applications wrapping D-Bus in another protocol.
+ *
+ * @param message the message to lock.
+ */
+void
+dbus_message_lock (DBusMessage  *message)
+{
+  if (!message->locked)
+    {
+      _dbus_header_update_lengths (&message->header,
+                                   _dbus_string_get_length (&message->body));
+
+      /* must have a signature if you have a body */
+      _dbus_assert (_dbus_string_get_length (&message->body) == 0 ||
+                    dbus_message_get_signature (message) != NULL);
+
+      message->locked = TRUE;
+    }
+}
+
+static dbus_bool_t
+set_or_delete_string_field (DBusMessage *message,
+                            int          field,
+                            int          typecode,
+                            const char  *value)
+{
+  if (value == NULL)
+    return _dbus_header_delete_field (&message->header, field);
+  else
+    return _dbus_header_set_field_basic (&message->header,
+                                         field,
+                                         typecode,
+                                         &value);
+}
+
+#if 0
+/* Probably we don't need to use this */
+/**
+ * Sets the signature of the message, i.e. the arguments in the
+ * message payload. The signature includes only "in" arguments for
+ * #DBUS_MESSAGE_TYPE_METHOD_CALL and only "out" arguments for
+ * #DBUS_MESSAGE_TYPE_METHOD_RETURN, so is slightly different from
+ * what you might expect (it does not include the signature of the
+ * entire C++-style method).
+ *
+ * The signature is a string made up of type codes such as
+ * #DBUS_TYPE_INT32. The string is terminated with nul (nul is also
+ * the value of #DBUS_TYPE_INVALID). The macros such as
+ * #DBUS_TYPE_INT32 evaluate to integers; to assemble a signature you
+ * may find it useful to use the string forms, such as
+ * #DBUS_TYPE_INT32_AS_STRING.
+ *
+ * An "unset" or #NULL signature is considered the same as an empty
+ * signature. In fact dbus_message_get_signature() will never return
+ * #NULL.
+ *
+ * @param message the message
+ * @param signature the type signature or #NULL to unset
+ * @returns #FALSE if no memory
+ */
+static dbus_bool_t
+_dbus_message_set_signature (DBusMessage *message,
+                             const char  *signature)
+{
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_fail (!message->locked, FALSE);
+  _dbus_return_val_if_fail (signature == NULL ||
+                            _dbus_check_is_valid_signature (signature));
+  /* can't delete the signature if you have a message body */
+  _dbus_return_val_if_fail (_dbus_string_get_length (&message->body) == 0 ||
+                            signature != NULL);
+
+  return set_or_delete_string_field (message,
+                                     DBUS_HEADER_FIELD_SIGNATURE,
+                                     DBUS_TYPE_SIGNATURE,
+                                     signature);
+}
+#endif
+
+/* Message Cache
+ *
+ * We cache some DBusMessage to reduce the overhead of allocating
+ * them.  In my profiling this consistently made about an 8%
+ * difference.  It avoids the malloc for the message, the malloc for
+ * the slot list, the malloc for the header string and body string,
+ * and the associated free() calls. It does introduce another global
+ * lock which could be a performance issue in certain cases.
+ *
+ * For the echo client/server the round trip time goes from around
+ * .000077 to .000069 with the message cache on my laptop. The sysprof
+ * change is as follows (numbers are cumulative percentage):
+ *
+ *  with message cache implemented as array as it is now (0.000069 per):
+ *    new_empty_header           1.46
+ *      mutex_lock               0.56    # i.e. _DBUS_LOCK(message_cache)
+ *      mutex_unlock             0.25
+ *      self                     0.41
+ *    unref                      2.24
+ *      self                     0.68
+ *      list_clear               0.43
+ *      mutex_lock               0.33    # i.e. _DBUS_LOCK(message_cache)
+ *      mutex_unlock             0.25
+ *
+ *  with message cache implemented as list (0.000070 per roundtrip):
+ *    new_empty_header           2.72
+ *      list_pop_first           1.88
+ *    unref                      3.3
+ *      list_prepend             1.63
+ *
+ * without cache (0.000077 per roundtrip):
+ *    new_empty_header           6.7
+ *      string_init_preallocated 3.43
+ *        dbus_malloc            2.43
+ *      dbus_malloc0             2.59
+ *
+ *    unref                      4.02
+ *      string_free              1.82
+ *        dbus_free              1.63
+ *      dbus_free                0.71
+ *
+ * If you implement the message_cache with a list, the primary reason
+ * it's slower is that you add another thread lock (on the DBusList
+ * mempool).
+ */
+
+/** Avoid caching huge messages */
+#define MAX_MESSAGE_SIZE_TO_CACHE 10 * _DBUS_ONE_KILOBYTE
+
+/** Avoid caching too many messages */
+#define MAX_MESSAGE_CACHE_SIZE    5
+
+_DBUS_DEFINE_GLOBAL_LOCK (message_cache);
+static DBusMessage *message_cache[MAX_MESSAGE_CACHE_SIZE];
+static int message_cache_count = 0;
+static dbus_bool_t message_cache_shutdown_registered = FALSE;
+
+static void
+dbus_message_cache_shutdown (void *data)
+{
+  int i;
+
+  _DBUS_LOCK (message_cache);
+
+  i = 0;
+  while (i < MAX_MESSAGE_CACHE_SIZE)
+    {
+      if (message_cache[i])
+        dbus_message_finalize (message_cache[i]);
+
+      ++i;
+    }
+
+  message_cache_count = 0;
+  message_cache_shutdown_registered = FALSE;
+
+  _DBUS_UNLOCK (message_cache);
+}
+
+/**
+ * Tries to get a message from the message cache.  The retrieved
+ * message will have junk in it, so it still needs to be cleared out
+ * in dbus_message_new_empty_header()
+ *
+ * @returns the message, or #NULL if none cached
+ */
+static DBusMessage*
+dbus_message_get_cached (void)
+{
+  DBusMessage *message;
+  int i;
+
+  message = NULL;
+
+  _DBUS_LOCK (message_cache);
+
+  _dbus_assert (message_cache_count >= 0);
+
+  if (message_cache_count == 0)
+    {
+      _DBUS_UNLOCK (message_cache);
+      return NULL;
+    }
+
+  /* This is not necessarily true unless count > 0, and
+   * message_cache is uninitialized until the shutdown is
+   * registered
+   */
+  _dbus_assert (message_cache_shutdown_registered);
+
+  i = 0;
+  while (i < MAX_MESSAGE_CACHE_SIZE)
+    {
+      if (message_cache[i])
+        {
+          message = message_cache[i];
+          message_cache[i] = NULL;
+          message_cache_count -= 1;
+          break;
+        }
+      ++i;
+    }
+  _dbus_assert (message_cache_count >= 0);
+  _dbus_assert (i < MAX_MESSAGE_CACHE_SIZE);
+  _dbus_assert (message != NULL);
+
+  _dbus_assert (message->refcount.value == 0);
+  _dbus_assert (message->size_counters == NULL);
+  
+  _DBUS_UNLOCK (message_cache);
+
+  return message;
+}
+
+static void
+free_size_counter (void *element,
+                   void *data)
+{
+  DBusCounter *counter = element;
+  DBusMessage *message = data;
+
+  _dbus_counter_adjust (counter, - message->size_counter_delta);
+
+  _dbus_counter_unref (counter);
+}
+
+/**
+ * Tries to cache a message, otherwise finalize it.
+ *
+ * @param message the message
+ */
+static void
+dbus_message_cache_or_finalize (DBusMessage *message)
+{
+  dbus_bool_t was_cached;
+  int i;
+  
+  _dbus_assert (message->refcount.value == 0);
+
+  /* This calls application code and has to be done first thing
+   * without holding the lock
+   */
+  _dbus_data_slot_list_clear (&message->slot_list);
+
+  _dbus_list_foreach (&message->size_counters,
+                      free_size_counter, message);
+  _dbus_list_clear (&message->size_counters);
+
+  was_cached = FALSE;
+
+  _DBUS_LOCK (message_cache);
+
+  if (!message_cache_shutdown_registered)
+    {
+      _dbus_assert (message_cache_count == 0);
+
+      if (!_dbus_register_shutdown_func (dbus_message_cache_shutdown, NULL))
+        goto out;
+
+      i = 0;
+      while (i < MAX_MESSAGE_CACHE_SIZE)
+        {
+          message_cache[i] = NULL;
+          ++i;
+        }
+
+      message_cache_shutdown_registered = TRUE;
+    }
+
+  _dbus_assert (message_cache_count >= 0);
+
+  if ((_dbus_string_get_length (&message->header.data) +
+       _dbus_string_get_length (&message->body)) >
+      MAX_MESSAGE_SIZE_TO_CACHE)
+    goto out;
+
+  if (message_cache_count >= MAX_MESSAGE_CACHE_SIZE)
+    goto out;
+
+  /* Find empty slot */
+  i = 0;
+  while (message_cache[i] != NULL)
+    ++i;
+
+  _dbus_assert (i < MAX_MESSAGE_CACHE_SIZE);
+
+  _dbus_assert (message_cache[i] == NULL);
+  message_cache[i] = message;
+  message_cache_count += 1;
+  was_cached = TRUE;
+#ifndef DBUS_DISABLE_CHECKS
+  message->in_cache = TRUE;
+#endif
+
+ out:
+  _dbus_assert (message->refcount.value == 0);
+  
+  _DBUS_UNLOCK (message_cache);
+  
+  if (!was_cached)
+    dbus_message_finalize (message);
+}
+
+#ifndef DBUS_DISABLE_CHECKS
+static dbus_bool_t
+_dbus_message_iter_check (DBusMessageRealIter *iter)
+{
+  if (iter == NULL)
+    {
+      _dbus_warn_check_failed ("dbus message iterator is NULL\n");
+      return FALSE;
+    }
+
+  if (iter->iter_type == DBUS_MESSAGE_ITER_TYPE_READER)
+    {
+      if (iter->u.reader.byte_order != iter->message->byte_order)
+        {
+          _dbus_warn_check_failed ("dbus message changed byte order since iterator was created\n");
+          return FALSE;
+        }
+      /* because we swap the message into compiler order when you init an iter */
+      _dbus_assert (iter->u.reader.byte_order == DBUS_COMPILER_BYTE_ORDER);
+    }
+  else if (iter->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER)
+    {
+      if (iter->u.writer.byte_order != iter->message->byte_order)
+        {
+          _dbus_warn_check_failed ("dbus message changed byte order since append iterator was created\n");
+          return FALSE;
+        }
+      /* because we swap the message into compiler order when you init an iter */
+      _dbus_assert (iter->u.writer.byte_order == DBUS_COMPILER_BYTE_ORDER);
+    }
+  else
+    {
+      _dbus_warn_check_failed ("dbus message iterator looks uninitialized or corrupted\n");
+      return FALSE;
+    }
+
+  if (iter->changed_stamp != iter->message->changed_stamp)
+    {
+      _dbus_warn_check_failed ("dbus message iterator invalid because the message has been modified (or perhaps the iterator is just uninitialized)\n");
+      return FALSE;
+    }
+
+  return TRUE;
+}
+#endif /* DBUS_DISABLE_CHECKS */
+
+/**
+ * Implementation of the varargs arg-getting functions.
+ * dbus_message_get_args() is the place to go for complete
+ * documentation.
+ *
+ * @see dbus_message_get_args
+ * @param iter the message iter
+ * @param error error to be filled in
+ * @param first_arg_type type of the first argument
+ * @param var_args return location for first argument, followed by list of type/location pairs
+ * @returns #FALSE if error was set
+ */
+dbus_bool_t
+_dbus_message_iter_get_args_valist (DBusMessageIter *iter,
+                                    DBusError       *error,
+                                    int              first_arg_type,
+                                    va_list          var_args)
+{
+  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+  int spec_type, msg_type, i;
+  dbus_bool_t retval;
+
+  _dbus_assert (_dbus_message_iter_check (real));
+
+  retval = FALSE;
+
+  spec_type = first_arg_type;
+  i = 0;
+
+  while (spec_type != DBUS_TYPE_INVALID)
+    {
+      msg_type = dbus_message_iter_get_arg_type (iter);
+
+      if (msg_type != spec_type)
+       {
+          dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+                          "Argument %d is specified to be of type \"%s\", but "
+                          "is actually of type \"%s\"\n", i,
+                          _dbus_type_to_string (spec_type),
+                          _dbus_type_to_string (msg_type));
+
+          goto out;
+       }
+
+      if (dbus_type_is_basic (spec_type))
+        {
+          DBusBasicValue *ptr;
+
+          ptr = va_arg (var_args, DBusBasicValue*);
+
+          _dbus_assert (ptr != NULL);
+
+          _dbus_type_reader_read_basic (&real->u.reader,
+                                        ptr);
+        }
+      else if (spec_type == DBUS_TYPE_ARRAY)
+        {
+          int element_type;
+          int spec_element_type;
+          const DBusBasicValue **ptr;
+          int *n_elements_p;
+          DBusTypeReader array;
+
+          spec_element_type = va_arg (var_args, int);
+          element_type = _dbus_type_reader_get_element_type (&real->u.reader);
+
+          if (spec_element_type != element_type)
+            {
+              dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+                              "Argument %d is specified to be an array of \"%s\", but "
+                              "is actually an array of \"%s\"\n",
+                              i,
+                              _dbus_type_to_string (spec_element_type),
+                              _dbus_type_to_string (element_type));
+
+              goto out;
+            }
+
+          if (dbus_type_is_fixed (spec_element_type))
+            {
+              ptr = va_arg (var_args, const DBusBasicValue**);
+              n_elements_p = va_arg (var_args, int*);
+
+              _dbus_assert (ptr != NULL);
+              _dbus_assert (n_elements_p != NULL);
+
+              _dbus_type_reader_recurse (&real->u.reader, &array);
+
+              _dbus_type_reader_read_fixed_multi (&array,
+                                                  ptr, n_elements_p);
+            }
+          else if (spec_element_type == DBUS_TYPE_STRING ||
+                   spec_element_type == DBUS_TYPE_SIGNATURE ||
+                   spec_element_type == DBUS_TYPE_OBJECT_PATH)
+            {
+              char ***str_array_p;
+              int n_elements;
+              char **str_array;
+
+              str_array_p = va_arg (var_args, char***);
+              n_elements_p = va_arg (var_args, int*);
+
+              _dbus_assert (str_array_p != NULL);
+              _dbus_assert (n_elements_p != NULL);
+
+              /* Count elements in the array */
+              _dbus_type_reader_recurse (&real->u.reader, &array);
+
+              n_elements = 0;
+              while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID)
+                {
+                  ++n_elements;
+                  _dbus_type_reader_next (&array);
+                }
+
+              str_array = dbus_new0 (char*, n_elements + 1);
+              if (str_array == NULL)
+                {
+                  _DBUS_SET_OOM (error);
+                  goto out;
+                }
+
+              /* Now go through and dup each string */
+              _dbus_type_reader_recurse (&real->u.reader, &array);
+
+              i = 0;
+              while (i < n_elements)
+                {
+                  const char *s;
+                  _dbus_type_reader_read_basic (&array,
+                                                &s);
+                  
+                  str_array[i] = _dbus_strdup (s);
+                  if (str_array[i] == NULL)
+                    {
+                      dbus_free_string_array (str_array);
+                      _DBUS_SET_OOM (error);
+                      goto out;
+                    }
+                  
+                  ++i;
+                  
+                  if (!_dbus_type_reader_next (&array))
+                    _dbus_assert (i == n_elements);
+                }
+
+              _dbus_assert (_dbus_type_reader_get_current_type (&array) == DBUS_TYPE_INVALID);
+              _dbus_assert (i == n_elements);
+              _dbus_assert (str_array[i] == NULL);
+
+              *str_array_p = str_array;
+              *n_elements_p = n_elements;
+            }
+#ifndef DBUS_DISABLE_CHECKS
+          else
+            {
+              _dbus_warn ("you can't read arrays of container types (struct, variant, array) with %s for now\n",
+                          _DBUS_FUNCTION_NAME);
+              goto out;
+            }
+#endif
+        }
+#ifndef DBUS_DISABLE_CHECKS
+      else
+        {
+          _dbus_warn ("you can only read arrays and basic types with %s for now\n",
+                      _DBUS_FUNCTION_NAME);
+          goto out;
+        }
+#endif
+
+      spec_type = va_arg (var_args, int);
+      if (!_dbus_type_reader_next (&real->u.reader) && spec_type != DBUS_TYPE_INVALID)
+        {
+          dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+                          "Message has only %d arguments, but more were expected", i);
+          goto out;
+        }
+
+      i++;
+    }
+
+  retval = TRUE;
+
+ out:
+
+  return retval;
+}
+
+/** @} */
+
+/**
+ * @defgroup DBusMessage DBusMessage
+ * @ingroup  DBus
+ * @brief Message to be sent or received over a #DBusConnection.
+ *
+ * A DBusMessage is the most basic unit of communication over a
+ * DBusConnection. A DBusConnection represents a stream of messages
+ * received from a remote application, and a stream of messages
+ * sent to a remote application.
+ *
+ * A message has a message type, returned from
+ * dbus_message_get_type().  This indicates whether the message is a
+ * method call, a reply to a method call, a signal, or an error reply.
+ *
+ * A message has header fields such as the sender, destination, method
+ * or signal name, and so forth. DBusMessage has accessor functions for
+ * these, such as dbus_message_get_member().
+ *
+ * Convenience functions dbus_message_is_method_call(), dbus_message_is_signal(),
+ * and dbus_message_is_error() check several header fields at once and are
+ * slightly more efficient than checking the header fields with individual
+ * accessor functions.
+ *
+ * Finally, a message has arguments. The number and types of arguments
+ * are in the message's signature header field (accessed with
+ * dbus_message_get_signature()).  Simple argument values are usually
+ * retrieved with dbus_message_get_args() but more complex values such
+ * as structs may require the use of #DBusMessageIter.
+ *
+ * The D-Bus specification goes into some more detail about header fields and
+ * message types.
+ * 
+ * @{
+ */
+
+/**
+ * @typedef DBusMessage
+ *
+ * Opaque data type representing a message received from or to be
+ * sent to another application.
+ */
+
+/**
+ * Returns the serial of a message or 0 if none has been specified.
+ * The message's serial number is provided by the application sending
+ * the message and is used to identify replies to this message.
+ *
+ * All messages received on a connection will have a serial provided
+ * by the remote application.
+ *
+ * For messages you're sending, dbus_connection_send() will assign a
+ * serial and return it to you.
+ *
+ * @param message the message
+ * @returns the serial
+ */
+dbus_uint32_t
+dbus_message_get_serial (DBusMessage *message)
+{
+  _dbus_return_val_if_fail (message != NULL, 0);
+
+  return _dbus_header_get_serial (&message->header);
+}
+
+/**
+ * Sets the reply serial of a message (the serial of the message this
+ * is a reply to).
+ *
+ * @param message the message
+ * @param reply_serial the serial we're replying to
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_message_set_reply_serial (DBusMessage   *message,
+                               dbus_uint32_t  reply_serial)
+{
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_fail (!message->locked, FALSE);
+  _dbus_return_val_if_fail (reply_serial != 0, FALSE); /* 0 is invalid */
+
+  return _dbus_header_set_field_basic (&message->header,
+                                       DBUS_HEADER_FIELD_REPLY_SERIAL,
+                                       DBUS_TYPE_UINT32,
+                                       &reply_serial);
+}
+
+/**
+ * Returns the serial that the message is a reply to or 0 if none.
+ *
+ * @param message the message
+ * @returns the reply serial
+ */
+dbus_uint32_t
+dbus_message_get_reply_serial  (DBusMessage *message)
+{
+  dbus_uint32_t v_UINT32;
+
+  _dbus_return_val_if_fail (message != NULL, 0);
+
+  if (_dbus_header_get_field_basic (&message->header,
+                                    DBUS_HEADER_FIELD_REPLY_SERIAL,
+                                    DBUS_TYPE_UINT32,
+                                    &v_UINT32))
+    return v_UINT32;
+  else
+    return 0;
+}
+
+static void
+dbus_message_finalize (DBusMessage *message)
+{
+  _dbus_assert (message->refcount.value == 0);
+
+  /* This calls application callbacks! */
+  _dbus_data_slot_list_free (&message->slot_list);
+
+  _dbus_list_foreach (&message->size_counters,
+                      free_size_counter, message);
+  _dbus_list_clear (&message->size_counters);
+
+  _dbus_header_free (&message->header);
+  _dbus_string_free (&message->body);
+
+  _dbus_assert (message->refcount.value == 0);
+  
+  dbus_free (message);
+}
+
+static DBusMessage*
+dbus_message_new_empty_header (void)
+{
+  DBusMessage *message;
+  dbus_bool_t from_cache;
+
+  message = dbus_message_get_cached ();
+
+  if (message != NULL)
+    {
+      from_cache = TRUE;
+    }
+  else
+    {
+      from_cache = FALSE;
+      message = dbus_new (DBusMessage, 1);
+      if (message == NULL)
+        return NULL;
+#ifndef DBUS_DISABLE_CHECKS
+      message->generation = _dbus_current_generation;
+#endif
+    }
+  
+  message->refcount.value = 1;
+  message->byte_order = DBUS_COMPILER_BYTE_ORDER;
+  message->locked = FALSE;
+#ifndef DBUS_DISABLE_CHECKS
+  message->in_cache = FALSE;
+#endif
+  message->size_counters = NULL;
+  message->size_counter_delta = 0;
+  message->changed_stamp = 0;
+
+  if (!from_cache)
+    _dbus_data_slot_list_init (&message->slot_list);
+
+  if (from_cache)
+    {
+      _dbus_header_reinit (&message->header, message->byte_order);
+      _dbus_string_set_length (&message->body, 0);
+    }
+  else
+    {
+      if (!_dbus_header_init (&message->header, message->byte_order))
+        {
+          dbus_free (message);
+          return NULL;
+        }
+
+      if (!_dbus_string_init_preallocated (&message->body, 32))
+        {
+          _dbus_header_free (&message->header);
+          dbus_free (message);
+          return NULL;
+        }
+    }
+
+  return message;
+}
+
+/**
+ * Constructs a new message of the given message type.
+ * Types include #DBUS_MESSAGE_TYPE_METHOD_CALL,
+ * #DBUS_MESSAGE_TYPE_SIGNAL, and so forth.
+ *
+ * Usually you want to use dbus_message_new_method_call(),
+ * dbus_message_new_method_return(), dbus_message_new_signal(),
+ * or dbus_message_new_error() instead.
+ * 
+ * @param message_type type of message
+ * @returns new message or #NULL if no memory
+ */
+DBusMessage*
+dbus_message_new (int message_type)
+{
+  DBusMessage *message;
+
+  _dbus_return_val_if_fail (message_type != DBUS_MESSAGE_TYPE_INVALID, NULL);
+
+  message = dbus_message_new_empty_header ();
+  if (message == NULL)
+    return NULL;
+
+  if (!_dbus_header_create (&message->header,
+                            message_type,
+                            NULL, NULL, NULL, NULL, NULL))
+    {
+      dbus_message_unref (message);
+      return NULL;
+    }
+
+  return message;
+}
+
+/**
+ * Constructs a new message to invoke a method on a remote
+ * object. Returns #NULL if memory can't be allocated for the
+ * message. The destination may be #NULL in which case no destination
+ * is set; this is appropriate when using D-Bus in a peer-to-peer
+ * context (no message bus). The interface may be #NULL, which means
+ * that if multiple methods with the given name exist it is undefined
+ * which one will be invoked.
+ *
+ * The path and method names may not be #NULL.
+ *
+ * Destination, path, interface, and method name can't contain
+ * any invalid characters (see the D-Bus specification).
+ * 
+ * @param destination name that the message should be sent to or #NULL
+ * @param path object path the message should be sent to
+ * @param interface interface to invoke method on, or #NULL
+ * @param method method to invoke
+ *
+ * @returns a new DBusMessage, free with dbus_message_unref()
+ */
+DBusMessage*
+dbus_message_new_method_call (const char *destination,
+                              const char *path,
+                              const char *interface,
+                              const char *method)
+{
+  DBusMessage *message;
+
+  _dbus_return_val_if_fail (path != NULL, NULL);
+  _dbus_return_val_if_fail (method != NULL, NULL);
+  _dbus_return_val_if_fail (destination == NULL ||
+                            _dbus_check_is_valid_bus_name (destination), NULL);
+  _dbus_return_val_if_fail (_dbus_check_is_valid_path (path), NULL);
+  _dbus_return_val_if_fail (interface == NULL ||
+                            _dbus_check_is_valid_interface (interface), NULL);
+  _dbus_return_val_if_fail (_dbus_check_is_valid_member (method), NULL);
+
+  message = dbus_message_new_empty_header ();
+  if (message == NULL)
+    return NULL;
+
+  if (!_dbus_header_create (&message->header,
+                            DBUS_MESSAGE_TYPE_METHOD_CALL,
+                            destination, path, interface, method, NULL))
+    {
+      dbus_message_unref (message);
+      return NULL;
+    }
+
+  return message;
+}
+
+/**
+ * Constructs a message that is a reply to a method call. Returns
+ * #NULL if memory can't be allocated for the message.
+ *
+ * @param method_call the message being replied to
+ * @returns a new DBusMessage, free with dbus_message_unref()
+ */
+DBusMessage*
+dbus_message_new_method_return (DBusMessage *method_call)
+{
+  DBusMessage *message;
+  const char *sender;
+
+  _dbus_return_val_if_fail (method_call != NULL, NULL);
+
+  sender = dbus_message_get_sender (method_call);
+
+  /* sender is allowed to be null here in peer-to-peer case */
+
+  message = dbus_message_new_empty_header ();
+  if (message == NULL)
+    return NULL;
+
+  if (!_dbus_header_create (&message->header,
+                            DBUS_MESSAGE_TYPE_METHOD_RETURN,
+                            sender, NULL, NULL, NULL, NULL))
+    {
+      dbus_message_unref (message);
+      return NULL;
+    }
+
+  dbus_message_set_no_reply (message, TRUE);
+
+  if (!dbus_message_set_reply_serial (message,
+                                      dbus_message_get_serial (method_call)))
+    {
+      dbus_message_unref (message);
+      return NULL;
+    }
+
+  return message;
+}
+
+/**
+ * Constructs a new message representing a signal emission. Returns
+ * #NULL if memory can't be allocated for the message.  A signal is
+ * identified by its originating object path, interface, and the name
+ * of the signal.
+ *
+ * Path, interface, and signal name must all be valid (the D-Bus
+ * specification defines the syntax of these fields).
+ * 
+ * @param path the path to the object emitting the signal
+ * @param interface the interface the signal is emitted from
+ * @param name name of the signal
+ * @returns a new DBusMessage, free with dbus_message_unref()
+ */
+DBusMessage*
+dbus_message_new_signal (const char *path,
+                         const char *interface,
+                         const char *name)
+{
+  DBusMessage *message;
+
+  _dbus_return_val_if_fail (path != NULL, NULL);
+  _dbus_return_val_if_fail (interface != NULL, NULL);
+  _dbus_return_val_if_fail (name != NULL, NULL);
+  _dbus_return_val_if_fail (_dbus_check_is_valid_path (path), NULL);
+  _dbus_return_val_if_fail (_dbus_check_is_valid_interface (interface), NULL);
+  _dbus_return_val_if_fail (_dbus_check_is_valid_member (name), NULL);
+
+  message = dbus_message_new_empty_header ();
+  if (message == NULL)
+    return NULL;
+
+  if (!_dbus_header_create (&message->header,
+                            DBUS_MESSAGE_TYPE_SIGNAL,
+                            NULL, path, interface, name, NULL))
+    {
+      dbus_message_unref (message);
+      return NULL;
+    }
+
+  dbus_message_set_no_reply (message, TRUE);
+
+  return message;
+}
+
+/**
+ * Creates a new message that is an error reply to another message.
+ * Error replies are most common in response to method calls, but
+ * can be returned in reply to any message.
+ *
+ * The error name must be a valid error name according to the syntax
+ * given in the D-Bus specification. If you don't want to make
+ * up an error name just use #DBUS_ERROR_FAILED.
+ *
+ * @param reply_to the message we're replying to
+ * @param error_name the error name
+ * @param error_message the error message string (or #NULL for none, but please give a message)
+ * @returns a new error message object, free with dbus_message_unref()
+ */
+DBusMessage*
+dbus_message_new_error (DBusMessage *reply_to,
+                        const char  *error_name,
+                        const char  *error_message)
+{
+  DBusMessage *message;
+  const char *sender;
+  DBusMessageIter iter;
+
+  _dbus_return_val_if_fail (reply_to != NULL, NULL);
+  _dbus_return_val_if_fail (error_name != NULL, NULL);
+  _dbus_return_val_if_fail (_dbus_check_is_valid_error_name (error_name), NULL);
+
+  sender = dbus_message_get_sender (reply_to);
+
+  /* sender may be NULL for non-message-bus case or
+   * when the message bus is dealing with an unregistered
+   * connection.
+   */
+  message = dbus_message_new_empty_header ();
+  if (message == NULL)
+    return NULL;
+
+  if (!_dbus_header_create (&message->header,
+                            DBUS_MESSAGE_TYPE_ERROR,
+                            sender, NULL, NULL, NULL, error_name))
+    {
+      dbus_message_unref (message);
+      return NULL;
+    }
+
+  dbus_message_set_no_reply (message, TRUE);
+
+  if (!dbus_message_set_reply_serial (message,
+                                      dbus_message_get_serial (reply_to)))
+    {
+      dbus_message_unref (message);
+      return NULL;
+    }
+
+  if (error_message != NULL)
+    {
+      dbus_message_iter_init_append (message, &iter);
+      if (!dbus_message_iter_append_basic (&iter,
+                                           DBUS_TYPE_STRING,
+                                           &error_message))
+        {
+          dbus_message_unref (message);
+          return NULL;
+        }
+    }
+
+  return message;
+}
+
+/**
+ * Creates a new message that is an error reply to another message, allowing
+ * you to use printf formatting.
+ *
+ * See dbus_message_new_error() for details - this function is the same
+ * aside from the printf formatting.
+ *
+ * @todo add _DBUS_GNUC_PRINTF to this (requires moving _DBUS_GNUC_PRINTF to
+ * public header, see DBUS_DEPRECATED for an example)
+ * 
+ * @param reply_to the original message
+ * @param error_name the error name
+ * @param error_format the error message format as with printf
+ * @param ... format string arguments
+ * @returns a new error message
+ */
+DBusMessage*
+dbus_message_new_error_printf (DBusMessage *reply_to,
+                              const char  *error_name,
+                              const char  *error_format,
+                              ...)
+{
+  va_list args;
+  DBusString str;
+  DBusMessage *message;
+
+  _dbus_return_val_if_fail (reply_to != NULL, NULL);
+  _dbus_return_val_if_fail (error_name != NULL, NULL);
+  _dbus_return_val_if_fail (_dbus_check_is_valid_error_name (error_name), NULL);
+
+  if (!_dbus_string_init (&str))
+    return NULL;
+
+  va_start (args, error_format);
+
+  if (_dbus_string_append_printf_valist (&str, error_format, args))
+    message = dbus_message_new_error (reply_to, error_name,
+                                     _dbus_string_get_const_data (&str));
+  else
+    message = NULL;
+
+  _dbus_string_free (&str);
+
+  va_end (args);
+
+  return message;
+}
+
+
+/**
+ * Creates a new message that is an exact replica of the message
+ * specified, except that its refcount is set to 1, its message serial
+ * is reset to 0, and if the original message was "locked" (in the
+ * outgoing message queue and thus not modifiable) the new message
+ * will not be locked.
+ *
+ * @param message the message
+ * @returns the new message.or #NULL if not enough memory
+ */
+DBusMessage *
+dbus_message_copy (const DBusMessage *message)
+{
+  DBusMessage *retval;
+
+  _dbus_return_val_if_fail (message != NULL, NULL);
+
+  retval = dbus_new0 (DBusMessage, 1);
+  if (retval == NULL)
+    return NULL;
+
+  retval->refcount.value = 1;
+  retval->byte_order = message->byte_order;
+  retval->locked = FALSE;
+#ifndef DBUS_DISABLE_CHECKS
+  retval->generation = message->generation;
+#endif
+
+  if (!_dbus_header_copy (&message->header, &retval->header))
+    {
+      dbus_free (retval);
+      return NULL;
+    }
+
+  if (!_dbus_string_init_preallocated (&retval->body,
+                                       _dbus_string_get_length (&message->body)))
+    {
+      _dbus_header_free (&retval->header);
+      dbus_free (retval);
+      return NULL;
+    }
+
+  if (!_dbus_string_copy (&message->body, 0,
+                         &retval->body, 0))
+    goto failed_copy;
+
+  return retval;
+
+ failed_copy:
+  _dbus_header_free (&retval->header);
+  _dbus_string_free (&retval->body);
+  dbus_free (retval);
+
+  return NULL;
+}
+
+
+/**
+ * Increments the reference count of a DBusMessage.
+ *
+ * @param message the message
+ * @returns the message
+ * @see dbus_message_unref
+ */
+DBusMessage *
+dbus_message_ref (DBusMessage *message)
+{
+  dbus_int32_t old_refcount;
+
+  _dbus_return_val_if_fail (message != NULL, NULL);
+  _dbus_return_val_if_fail (message->generation == _dbus_current_generation, NULL);
+  _dbus_return_val_if_fail (!message->in_cache, NULL);
+  
+  old_refcount = _dbus_atomic_inc (&message->refcount);
+  _dbus_assert (old_refcount >= 1);
+
+  return message;
+}
+
+/**
+ * Decrements the reference count of a DBusMessage, freeing the
+ * message if the count reaches 0.
+ *
+ * @param message the message
+ * @see dbus_message_ref
+ */
+void
+dbus_message_unref (DBusMessage *message)
+{
+ dbus_int32_t old_refcount;
+
+  _dbus_return_if_fail (message != NULL);
+  _dbus_return_if_fail (message->generation == _dbus_current_generation);
+  _dbus_return_if_fail (!message->in_cache);
+
+  old_refcount = _dbus_atomic_dec (&message->refcount);
+
+  _dbus_assert (old_refcount >= 0);
+
+  if (old_refcount == 1)
+    {
+      /* Calls application callbacks! */
+      dbus_message_cache_or_finalize (message);
+    }
+}
+
+/**
+ * Gets the type of a message. Types include
+ * #DBUS_MESSAGE_TYPE_METHOD_CALL, #DBUS_MESSAGE_TYPE_METHOD_RETURN,
+ * #DBUS_MESSAGE_TYPE_ERROR, #DBUS_MESSAGE_TYPE_SIGNAL, but other
+ * types are allowed and all code must silently ignore messages of
+ * unknown type. #DBUS_MESSAGE_TYPE_INVALID will never be returned.
+ *
+ * @param message the message
+ * @returns the type of the message
+ */
+int
+dbus_message_get_type (DBusMessage *message)
+{
+  _dbus_return_val_if_fail (message != NULL, DBUS_MESSAGE_TYPE_INVALID);
+
+  return _dbus_header_get_message_type (&message->header);
+}
+
+/**
+ * Appends fields to a message given a variable argument list. The
+ * variable argument list should contain the type of each argument
+ * followed by the value to append. Appendable types are basic types,
+ * and arrays of fixed-length basic types. To append variable-length
+ * basic types, or any more complex value, you have to use an iterator
+ * rather than this function.
+ *
+ * To append a basic type, specify its type code followed by the
+ * address of the value. For example:
+ *
+ * @code
+ *
+ * dbus_int32_t v_INT32 = 42;
+ * const char *v_STRING = "Hello World";
+ * dbus_message_append_args (message,
+ *                           DBUS_TYPE_INT32, &v_INT32,
+ *                           DBUS_TYPE_STRING, &v_STRING,
+ *                           DBUS_TYPE_INVALID);
+ * @endcode
+ *
+ * To append an array of fixed-length basic types, pass in the
+ * DBUS_TYPE_ARRAY typecode, the element typecode, the address of
+ * the array pointer, and a 32-bit integer giving the number of
+ * elements in the array. So for example:
+ * @code
+ * const dbus_int32_t array[] = { 1, 2, 3 };
+ * const dbus_int32_t *v_ARRAY = array;
+ * dbus_message_append_args (message,
+ *                           DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &v_ARRAY, 3,
+ *                           DBUS_TYPE_INVALID);
+ * @endcode
+ *
+ * @warning in C, given "int array[]", "&array == array" (the
+ * comp.lang.c FAQ says otherwise, but gcc and the FAQ don't agree).
+ * So if you're using an array instead of a pointer you have to create
+ * a pointer variable, assign the array to it, then take the address
+ * of the pointer variable. For strings it works to write
+ * const char *array = "Hello" and then use &array though.
+ *
+ * The last argument to this function must be #DBUS_TYPE_INVALID,
+ * marking the end of the argument list. If you don't do this
+ * then libdbus won't know to stop and will read invalid memory.
+ *
+ * String/signature/path arrays should be passed in as "const char***
+ * address_of_array" and "int n_elements"
+ *
+ * @todo support DBUS_TYPE_STRUCT and DBUS_TYPE_VARIANT and complex arrays
+ *
+ * @todo If this fails due to lack of memory, the message is hosed and
+ * you have to start over building the whole message.
+ *
+ * @param message the message
+ * @param first_arg_type type of the first argument
+ * @param ... value of first argument, list of additional type-value pairs
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+dbus_message_append_args (DBusMessage *message,
+                         int          first_arg_type,
+                         ...)
+{
+  dbus_bool_t retval;
+  va_list var_args;
+
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+
+  va_start (var_args, first_arg_type);
+  retval = dbus_message_append_args_valist (message,
+                                           first_arg_type,
+                                           var_args);
+  va_end (var_args);
+
+  return retval;
+}
+
+/**
+ * Like dbus_message_append_args() but takes a va_list for use by language bindings.
+ *
+ * @todo for now, if this function fails due to OOM it will leave
+ * the message half-written and you have to discard the message
+ * and start over.
+ *
+ * @see dbus_message_append_args.
+ * @param message the message
+ * @param first_arg_type type of first argument
+ * @param var_args value of first argument, then list of type/value pairs
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+dbus_message_append_args_valist (DBusMessage *message,
+                                int          first_arg_type,
+                                va_list      var_args)
+{
+  int type;
+  DBusMessageIter iter;
+
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+
+  type = first_arg_type;
+
+  dbus_message_iter_init_append (message, &iter);
+
+  while (type != DBUS_TYPE_INVALID)
+    {
+      if (dbus_type_is_basic (type))
+        {
+          const DBusBasicValue *value;
+          value = va_arg (var_args, const DBusBasicValue*);
+
+          if (!dbus_message_iter_append_basic (&iter,
+                                               type,
+                                               value))
+            goto failed;
+        }
+      else if (type == DBUS_TYPE_ARRAY)
+        {
+          int element_type;
+          DBusMessageIter array;
+          char buf[2];
+
+          element_type = va_arg (var_args, int);
+              
+          buf[0] = element_type;
+          buf[1] = '\0';
+          if (!dbus_message_iter_open_container (&iter,
+                                                 DBUS_TYPE_ARRAY,
+                                                 buf,
+                                                 &array))
+            goto failed;
+          
+          if (dbus_type_is_fixed (element_type))
+            {
+              const DBusBasicValue **value;
+              int n_elements;
+
+              value = va_arg (var_args, const DBusBasicValue**);
+              n_elements = va_arg (var_args, int);
+              
+              if (!dbus_message_iter_append_fixed_array (&array,
+                                                         element_type,
+                                                         value,
+                                                         n_elements))
+                goto failed;
+            }
+          else if (element_type == DBUS_TYPE_STRING ||
+                   element_type == DBUS_TYPE_SIGNATURE ||
+                   element_type == DBUS_TYPE_OBJECT_PATH)
+            {
+              const char ***value_p;
+              const char **value;
+              int n_elements;
+              int i;
+              
+              value_p = va_arg (var_args, const char***);
+              n_elements = va_arg (var_args, int);
+
+              value = *value_p;
+              
+              i = 0;
+              while (i < n_elements)
+                {
+                  if (!dbus_message_iter_append_basic (&array,
+                                                       element_type,
+                                                       &value[i]))
+                    goto failed;
+                  ++i;
+                }
+            }
+          else
+            {
+              _dbus_warn ("arrays of %s can't be appended with %s for now\n",
+                          _dbus_type_to_string (element_type),
+                          _DBUS_FUNCTION_NAME);
+              goto failed;
+            }
+
+          if (!dbus_message_iter_close_container (&iter, &array))
+            goto failed;
+        }
+#ifndef DBUS_DISABLE_CHECKS
+      else
+        {
+          _dbus_warn ("type %s isn't supported yet in %s\n",
+                      _dbus_type_to_string (type), _DBUS_FUNCTION_NAME);
+          goto failed;
+        }
+#endif
+
+      type = va_arg (var_args, int);
+    }
+
+  return TRUE;
+
+ failed:
+  return FALSE;
+}
+
+/**
+ * Gets arguments from a message given a variable argument list.  The
+ * supported types include those supported by
+ * dbus_message_append_args(); that is, basic types and arrays of
+ * fixed-length basic types.  The arguments are the same as they would
+ * be for dbus_message_iter_get_basic() or
+ * dbus_message_iter_get_fixed_array().
+ *
+ * In addition to those types, arrays of string, object path, and
+ * signature are supported; but these are returned as allocated memory
+ * and must be freed with dbus_free_string_array(), while the other
+ * types are returned as const references. To get a string array
+ * pass in "char ***array_location" and "int *n_elements"
+ *
+ * The variable argument list should contain the type of the argument
+ * followed by a pointer to where the value should be stored. The list
+ * is terminated with #DBUS_TYPE_INVALID.
+ *
+ * Except for string arrays, the returned values are constant; do not
+ * free them. They point into the #DBusMessage.
+ *
+ * If the requested arguments are not present, or do not have the
+ * requested types, then an error will be set.
+ *
+ * If more arguments than requested are present, the requested
+ * arguments are returned and the extra arguments are ignored.
+ * 
+ * @todo support DBUS_TYPE_STRUCT and DBUS_TYPE_VARIANT and complex arrays
+ *
+ * @param message the message
+ * @param error error to be filled in on failure
+ * @param first_arg_type the first argument type
+ * @param ... location for first argument value, then list of type-location pairs
+ * @returns #FALSE if the error was set
+ */
+dbus_bool_t
+dbus_message_get_args (DBusMessage     *message,
+                       DBusError       *error,
+                      int              first_arg_type,
+                      ...)
+{
+  dbus_bool_t retval;
+  va_list var_args;
+
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_error_is_set (error, FALSE);
+
+  va_start (var_args, first_arg_type);
+  retval = dbus_message_get_args_valist (message, error, first_arg_type, var_args);
+  va_end (var_args);
+
+  return retval;
+}
+
+/**
+ * Like dbus_message_get_args but takes a va_list for use by language bindings.
+ *
+ * @see dbus_message_get_args
+ * @param message the message
+ * @param error error to be filled in
+ * @param first_arg_type type of the first argument
+ * @param var_args return location for first argument, followed by list of type/location pairs
+ * @returns #FALSE if error was set
+ */
+dbus_bool_t
+dbus_message_get_args_valist (DBusMessage     *message,
+                              DBusError       *error,
+                             int              first_arg_type,
+                             va_list          var_args)
+{
+  DBusMessageIter iter;
+
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_error_is_set (error, FALSE);
+
+  dbus_message_iter_init (message, &iter);
+  return _dbus_message_iter_get_args_valist (&iter, error, first_arg_type, var_args);
+}
+
+static void
+_dbus_message_iter_init_common (DBusMessage         *message,
+                                DBusMessageRealIter *real,
+                                int                  iter_type)
+{
+  _dbus_assert (sizeof (DBusMessageRealIter) <= sizeof (DBusMessageIter));
+
+  /* Since the iterator will read or write who-knows-what from the
+   * message, we need to get in the right byte order
+   */
+  ensure_byte_order (message);
+  
+  real->message = message;
+  real->changed_stamp = message->changed_stamp;
+  real->iter_type = iter_type;
+  real->sig_refcount = 0;
+}
+
+/**
+ * Initializes a #DBusMessageIter for reading the arguments of the
+ * message passed in.
+ *
+ * When possible, dbus_message_get_args() is much more convenient.
+ * Some types of argument can only be read with #DBusMessageIter
+ * however.
+ *
+ * The easiest way to iterate is like this: 
+ * @code
+ * dbus_message_iter_init (message, &iter);
+ * while ((current_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID)
+ *   dbus_message_iter_next (&iter);
+ * @endcode
+ *
+ * #DBusMessageIter contains no allocated memory; it need not be
+ * freed, and can be copied by assignment or memcpy().
+ * 
+ * @param message the message
+ * @param iter pointer to an iterator to initialize
+ * @returns #FALSE if the message has no arguments
+ */
+dbus_bool_t
+dbus_message_iter_init (DBusMessage     *message,
+                       DBusMessageIter *iter)
+{
+  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+  const DBusString *type_str;
+  int type_pos;
+
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_fail (iter != NULL, FALSE);
+
+  get_const_signature (&message->header, &type_str, &type_pos);
+
+  _dbus_message_iter_init_common (message, real,
+                                  DBUS_MESSAGE_ITER_TYPE_READER);
+
+  _dbus_type_reader_init (&real->u.reader,
+                          message->byte_order,
+                          type_str, type_pos,
+                          &message->body,
+                          0);
+
+  return _dbus_type_reader_get_current_type (&real->u.reader) != DBUS_TYPE_INVALID;
+}
+
+/**
+ * Checks if an iterator has any more fields.
+ *
+ * @param iter the message iter
+ * @returns #TRUE if there are more fields following
+ */
+dbus_bool_t
+dbus_message_iter_has_next (DBusMessageIter *iter)
+{
+  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+
+  _dbus_return_val_if_fail (_dbus_message_iter_check (real), FALSE);
+  _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_READER, FALSE);
+
+  return _dbus_type_reader_has_next (&real->u.reader);
+}
+
+/**
+ * Moves the iterator to the next field, if any. If there's no next
+ * field, returns #FALSE. If the iterator moves forward, returns
+ * #TRUE.
+ *
+ * @param iter the message iter
+ * @returns #TRUE if the iterator was moved to the next field
+ */
+dbus_bool_t
+dbus_message_iter_next (DBusMessageIter *iter)
+{
+  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+
+  _dbus_return_val_if_fail (_dbus_message_iter_check (real), FALSE);
+  _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_READER, FALSE);
+
+  return _dbus_type_reader_next (&real->u.reader);
+}
+
+/**
+ * Returns the argument type of the argument that the message iterator
+ * points to. If the iterator is at the end of the message, returns
+ * #DBUS_TYPE_INVALID. You can thus write a loop as follows:
+ *
+ * @code
+ * dbus_message_iter_init (&iter);
+ * while ((current_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID)
+ *   dbus_message_iter_next (&iter);
+ * @endcode
+ *
+ * @param iter the message iter
+ * @returns the argument type
+ */
+int
+dbus_message_iter_get_arg_type (DBusMessageIter *iter)
+{
+  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+
+  _dbus_return_val_if_fail (_dbus_message_iter_check (real), DBUS_TYPE_INVALID);
+  _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_READER, FALSE);
+
+  return _dbus_type_reader_get_current_type (&real->u.reader);
+}
+
+/**
+ * Returns the element type of the array that the message iterator
+ * points to. Note that you need to check that the iterator points to
+ * an array prior to using this function.
+ *
+ * @param iter the message iter
+ * @returns the array element type
+ */
+int
+dbus_message_iter_get_element_type (DBusMessageIter *iter)
+{
+  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+
+  _dbus_return_val_if_fail (_dbus_message_iter_check (real), DBUS_TYPE_INVALID);
+  _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_READER, DBUS_TYPE_INVALID);
+  _dbus_return_val_if_fail (dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_ARRAY, DBUS_TYPE_INVALID);
+
+  return _dbus_type_reader_get_element_type (&real->u.reader);
+}
+
+/**
+ * Recurses into a container value when reading values from a message,
+ * initializing a sub-iterator to use for traversing the child values
+ * of the container.
+ *
+ * Note that this recurses into a value, not a type, so you can only
+ * recurse if the value exists. The main implication of this is that
+ * if you have for example an empty array of array of int32, you can
+ * recurse into the outermost array, but it will have no values, so
+ * you won't be able to recurse further. There's no array of int32 to
+ * recurse into.
+ *
+ * If a container is an array of fixed-length types, it is much more
+ * efficient to use dbus_message_iter_get_fixed_array() to get the
+ * whole array in one shot, rather than individually walking over the
+ * array elements.
+ *
+ * Be sure you have somehow checked that
+ * dbus_message_iter_get_arg_type() matches the type you are expecting
+ * to recurse into. Results of this function are undefined if there is
+ * no container to recurse into at the current iterator position.
+ *
+ * @param iter the message iterator
+ * @param sub the sub-iterator to initialize
+ */
+void
+dbus_message_iter_recurse (DBusMessageIter  *iter,
+                           DBusMessageIter  *sub)
+{
+  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+  DBusMessageRealIter *real_sub = (DBusMessageRealIter *)sub;
+
+  _dbus_return_if_fail (_dbus_message_iter_check (real));
+  _dbus_return_if_fail (sub != NULL);
+
+  *real_sub = *real;
+  _dbus_type_reader_recurse (&real->u.reader, &real_sub->u.reader);
+}
+
+/**
+ * Returns the current signature of a message iterator.  This
+ * is useful primarily for dealing with variants; one can
+ * recurse into a variant and determine the signature of
+ * the variant's value.
+ *
+ * The returned string must be freed with dbus_free().
+ * 
+ * @param iter the message iterator
+ * @returns the contained signature, or NULL if out of memory
+ */
+char *
+dbus_message_iter_get_signature (DBusMessageIter *iter)
+{
+  const DBusString *sig;
+  DBusString retstr;
+  char *ret;
+  int start, len;
+  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+
+  _dbus_return_val_if_fail (_dbus_message_iter_check (real), NULL);
+
+  if (!_dbus_string_init (&retstr))
+    return NULL;
+
+  _dbus_type_reader_get_signature (&real->u.reader, &sig,
+                                  &start, &len);
+  if (!_dbus_string_append_len (&retstr,
+                               _dbus_string_get_const_data (sig) + start,
+                               len))
+    return NULL;
+  if (!_dbus_string_steal_data (&retstr, &ret))
+    return NULL;
+  _dbus_string_free (&retstr);
+  return ret;
+}
+
+/**
+ * Reads a basic-typed value from the message iterator.
+ * Basic types are the non-containers such as integer and string.
+ *
+ * The value argument should be the address of a location to store
+ * the returned value. So for int32 it should be a "dbus_int32_t*"
+ * and for string a "const char**". The returned value is
+ * by reference and should not be freed.
+ *
+ * Be sure you have somehow checked that
+ * dbus_message_iter_get_arg_type() matches the type you are
+ * expecting, or you'll crash when you try to use an integer as a
+ * string or something.
+ *
+ * To read any container type (array, struct, dict) you will need
+ * to recurse into the container with dbus_message_iter_recurse().
+ * If the container is an array of fixed-length values, you can
+ * get all the array elements at once with
+ * dbus_message_iter_get_fixed_array(). Otherwise, you have to
+ * iterate over the container's contents one value at a time.
+ * 
+ * All basic-typed values are guaranteed to fit in 8 bytes. So you can
+ * write code like this:
+ *
+ * @code
+ * dbus_uint64_t value;
+ * int type;
+ * dbus_message_iter_get_basic (&read_iter, &value);
+ * type = dbus_message_iter_get_arg_type (&read_iter);
+ * dbus_message_iter_append_basic (&write_iter, type, &value);
+ * @endcode
+ *
+ * On some really obscure platforms dbus_uint64_t might not exist, if
+ * you need to worry about this you will know.  dbus_uint64_t is just
+ * one example of a type that's large enough to hold any possible
+ * value, you could use a struct or char[8] instead if you like.
+ *
+ * @param iter the iterator
+ * @param value location to store the value
+ */
+void
+dbus_message_iter_get_basic (DBusMessageIter  *iter,
+                             void             *value)
+{
+  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+
+  _dbus_return_if_fail (_dbus_message_iter_check (real));
+  _dbus_return_if_fail (value != NULL);
+
+  _dbus_type_reader_read_basic (&real->u.reader,
+                                value);
+}
+
+/**
+ * Returns the number of bytes in the array as marshaled in the wire
+ * protocol. The iterator must currently be inside an array-typed
+ * value.
+ *
+ * This function is deprecated on the grounds that it is stupid.  Why
+ * would you want to know how many bytes are in the array as marshaled
+ * in the wire protocol?  For now, use the n_elements returned from
+ * dbus_message_iter_get_fixed_array() instead, or iterate over the
+ * array values and count them.
+ *
+ * @todo introduce a variant of this get_n_elements that returns
+ * the number of elements, though with a non-fixed array it will not
+ * be very efficient, so maybe it's not good.
+ * 
+ * @param iter the iterator
+ * @returns the number of bytes in the array
+ */
+int
+dbus_message_iter_get_array_len (DBusMessageIter *iter)
+{
+  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+
+  _dbus_return_val_if_fail (_dbus_message_iter_check (real), 0);
+
+  return _dbus_type_reader_get_array_length (&real->u.reader);
+}
+
+/**
+ * Reads a block of fixed-length values from the message iterator.
+ * Fixed-length values are those basic types that are not string-like,
+ * such as integers, bool, double. The returned block will be from the
+ * current position in the array until the end of the array.
+ *
+ * The message iter should be "in" the array (that is, you recurse into the
+ * array, and then you call dbus_message_iter_get_fixed_array() on the
+ * "sub-iterator" created by dbus_message_iter_recurse()).
+ *
+ * The value argument should be the address of a location to store the
+ * returned array. So for int32 it should be a "const dbus_int32_t**"
+ * The returned value is by reference and should not be freed.
+ * 
+ * This function should only be used if dbus_type_is_fixed() returns
+ * #TRUE for the element type.
+ *
+ * If an array's elements are not fixed in size, you have to recurse
+ * into the array with dbus_message_iter_recurse() and read the
+ * elements one by one.
+ * 
+ * Because the array is not copied, this function runs in constant
+ * time and is fast; it's much preferred over walking the entire array
+ * with an iterator. (However, you can always use
+ * dbus_message_iter_recurse(), even for fixed-length types;
+ * dbus_message_iter_get_fixed_array() is just an optimization.)
+ * 
+ * @param iter the iterator
+ * @param value location to store the block
+ * @param n_elements number of elements in the block
+ */
+void
+dbus_message_iter_get_fixed_array (DBusMessageIter  *iter,
+                                   void             *value,
+                                   int              *n_elements)
+{
+  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+  int subtype = _dbus_type_reader_get_current_type(&real->u.reader);
+
+  _dbus_return_if_fail (_dbus_message_iter_check (real));
+  _dbus_return_if_fail (value != NULL);
+  _dbus_return_if_fail ((subtype == DBUS_TYPE_INVALID) ||
+                         dbus_type_is_fixed (subtype));
+
+  _dbus_type_reader_read_fixed_multi (&real->u.reader,
+                                      value, n_elements);
+}
+
+/**
+ * Initializes a #DBusMessageIter for appending arguments to the end
+ * of a message.
+ *
+ * @todo If appending any of the arguments fails due to lack of
+ * memory, the message is hosed and you have to start over building
+ * the whole message.
+ *
+ * @param message the message
+ * @param iter pointer to an iterator to initialize
+ */
+void
+dbus_message_iter_init_append (DBusMessage     *message,
+                              DBusMessageIter *iter)
+{
+  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+
+  _dbus_return_if_fail (message != NULL);
+  _dbus_return_if_fail (iter != NULL);
+
+  _dbus_message_iter_init_common (message, real,
+                                  DBUS_MESSAGE_ITER_TYPE_WRITER);
+
+  /* We create the signature string and point iterators at it "on demand"
+   * when a value is actually appended. That means that init() never fails
+   * due to OOM.
+   */
+  _dbus_type_writer_init_types_delayed (&real->u.writer,
+                                        message->byte_order,
+                                        &message->body,
+                                        _dbus_string_get_length (&message->body));
+}
+
+/**
+ * Creates a temporary signature string containing the current
+ * signature, stores it in the iterator, and points the iterator to
+ * the end of it. Used any time we write to the message.
+ *
+ * @param real an iterator without a type_str
+ * @returns #FALSE if no memory
+ */
+static dbus_bool_t
+_dbus_message_iter_open_signature (DBusMessageRealIter *real)
+{
+  DBusString *str;
+  const DBusString *current_sig;
+  int current_sig_pos;
+
+  _dbus_assert (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER);
+
+  if (real->u.writer.type_str != NULL)
+    {
+      _dbus_assert (real->sig_refcount > 0);
+      real->sig_refcount += 1;
+      return TRUE;
+    }
+
+  str = dbus_new (DBusString, 1);
+  if (str == NULL)
+    return FALSE;
+
+  if (!_dbus_header_get_field_raw (&real->message->header,
+                                   DBUS_HEADER_FIELD_SIGNATURE,
+                                   &current_sig, &current_sig_pos))
+    current_sig = NULL;
+
+  if (current_sig)
+    {
+      int current_len;
+
+      current_len = _dbus_string_get_byte (current_sig, current_sig_pos);
+      current_sig_pos += 1; /* move on to sig data */
+
+      if (!_dbus_string_init_preallocated (str, current_len + 4))
+        {
+          dbus_free (str);
+          return FALSE;
+        }
+
+      if (!_dbus_string_copy_len (current_sig, current_sig_pos, current_len,
+                                  str, 0))
+        {
+          _dbus_string_free (str);
+          dbus_free (str);
+          return FALSE;
+        }
+    }
+  else
+    {
+      if (!_dbus_string_init_preallocated (str, 4))
+        {
+          dbus_free (str);
+          return FALSE;
+        }
+    }
+
+  real->sig_refcount = 1;
+
+  _dbus_type_writer_add_types (&real->u.writer,
+                               str, _dbus_string_get_length (str));
+  return TRUE;
+}
+
+/**
+ * Sets the new signature as the message signature, frees the
+ * signature string, and marks the iterator as not having a type_str
+ * anymore. Frees the signature even if it fails, so you can't
+ * really recover from failure. Kinda busted.
+ *
+ * @param real an iterator without a type_str
+ * @returns #FALSE if no memory
+ */
+static dbus_bool_t
+_dbus_message_iter_close_signature (DBusMessageRealIter *real)
+{
+  DBusString *str;
+  const char *v_STRING;
+  dbus_bool_t retval;
+
+  _dbus_assert (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER);
+  _dbus_assert (real->u.writer.type_str != NULL);
+  _dbus_assert (real->sig_refcount > 0);
+
+  real->sig_refcount -= 1;
+
+  if (real->sig_refcount > 0)
+    return TRUE;
+  _dbus_assert (real->sig_refcount == 0);
+
+  retval = TRUE;
+
+  str = real->u.writer.type_str;
+
+  v_STRING = _dbus_string_get_const_data (str);
+  if (!_dbus_header_set_field_basic (&real->message->header,
+                                     DBUS_HEADER_FIELD_SIGNATURE,
+                                     DBUS_TYPE_SIGNATURE,
+                                     &v_STRING))
+    retval = FALSE;
+
+  _dbus_type_writer_remove_types (&real->u.writer);
+  _dbus_string_free (str);
+  dbus_free (str);
+
+  return retval;
+}
+
+#ifndef DBUS_DISABLE_CHECKS
+static dbus_bool_t
+_dbus_message_iter_append_check (DBusMessageRealIter *iter)
+{
+  if (!_dbus_message_iter_check (iter))
+    return FALSE;
+
+  if (iter->message->locked)
+    {
+      _dbus_warn_check_failed ("dbus append iterator can't be used: message is locked (has already been sent)\n");
+      return FALSE;
+    }
+
+  return TRUE;
+}
+#endif /* DBUS_DISABLE_CHECKS */
+
+/**
+ * Appends a basic-typed value to the message. The basic types are the
+ * non-container types such as integer and string.
+ *
+ * The "value" argument should be the address of a basic-typed value.
+ * So for string, const char**. For integer, dbus_int32_t*.
+ *
+ * @todo If this fails due to lack of memory, the message is hosed and
+ * you have to start over building the whole message.
+ *
+ * @param iter the append iterator
+ * @param type the type of the value
+ * @param value the address of the value
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_message_iter_append_basic (DBusMessageIter *iter,
+                                int              type,
+                                const void      *value)
+{
+  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+  dbus_bool_t ret;
+
+  _dbus_return_val_if_fail (_dbus_message_iter_append_check (real), FALSE);
+  _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE);
+  _dbus_return_val_if_fail (dbus_type_is_basic (type), FALSE);
+  _dbus_return_val_if_fail (value != NULL, FALSE);
+
+  if (!_dbus_message_iter_open_signature (real))
+    return FALSE;
+
+  ret = _dbus_type_writer_write_basic (&real->u.writer, type, value);
+
+  if (!_dbus_message_iter_close_signature (real))
+    ret = FALSE;
+
+  return ret;
+}
+
+/**
+ * Appends a block of fixed-length values to an array. The
+ * fixed-length types are all basic types that are not string-like. So
+ * int32, double, bool, etc. You must call
+ * dbus_message_iter_open_container() to open an array of values
+ * before calling this function. You may call this function multiple
+ * times (and intermixed with calls to
+ * dbus_message_iter_append_basic()) for the same array.
+ *
+ * The "value" argument should be the address of the array.  So for
+ * integer, "dbus_int32_t**" is expected for example.
+ *
+ * @warning in C, given "int array[]", "&array == array" (the
+ * comp.lang.c FAQ says otherwise, but gcc and the FAQ don't agree).
+ * So if you're using an array instead of a pointer you have to create
+ * a pointer variable, assign the array to it, then take the address
+ * of the pointer variable.
+ * @code
+ * const dbus_int32_t array[] = { 1, 2, 3 };
+ * const dbus_int32_t *v_ARRAY = array;
+ * if (!dbus_message_iter_append_fixed_array (&iter, DBUS_TYPE_INT32, &v_ARRAY, 3))
+ *   fprintf (stderr, "No memory!\n");
+ * @endcode
+ * For strings it works to write const char *array = "Hello" and then
+ * use &array though.
+ *
+ * @todo If this fails due to lack of memory, the message is hosed and
+ * you have to start over building the whole message.
+ *
+ * @param iter the append iterator
+ * @param element_type the type of the array elements
+ * @param value the address of the array
+ * @param n_elements the number of elements to append
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_message_iter_append_fixed_array (DBusMessageIter *iter,
+                                      int              element_type,
+                                      const void      *value,
+                                      int              n_elements)
+{
+  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+  dbus_bool_t ret;
+
+  _dbus_return_val_if_fail (_dbus_message_iter_append_check (real), FALSE);
+  _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE);
+  _dbus_return_val_if_fail (dbus_type_is_fixed (element_type), FALSE);
+  _dbus_return_val_if_fail (real->u.writer.container_type == DBUS_TYPE_ARRAY, FALSE);
+  _dbus_return_val_if_fail (value != NULL, FALSE);
+  _dbus_return_val_if_fail (n_elements >= 0, FALSE);
+  _dbus_return_val_if_fail (n_elements <=
+                            DBUS_MAXIMUM_ARRAY_LENGTH / _dbus_type_get_alignment (element_type),
+                            FALSE);
+
+  ret = _dbus_type_writer_write_fixed_multi (&real->u.writer, element_type, value, n_elements);
+
+  return ret;
+}
+
+/**
+ * Appends a container-typed value to the message; you are required to
+ * append the contents of the container using the returned
+ * sub-iterator, and then call
+ * dbus_message_iter_close_container(). Container types are for
+ * example struct, variant, and array. For variants, the
+ * contained_signature should be the type of the single value inside
+ * the variant. For structs and dict entries, contained_signature
+ * should be #NULL; it will be set to whatever types you write into
+ * the struct.  For arrays, contained_signature should be the type of
+ * the array elements.
+ *
+ * @todo If this fails due to lack of memory, the message is hosed and
+ * you have to start over building the whole message.
+ *
+ * @param iter the append iterator
+ * @param type the type of the value
+ * @param contained_signature the type of container contents
+ * @param sub sub-iterator to initialize
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_message_iter_open_container (DBusMessageIter *iter,
+                                  int              type,
+                                  const char      *contained_signature,
+                                  DBusMessageIter *sub)
+{
+  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+  DBusMessageRealIter *real_sub = (DBusMessageRealIter *)sub;
+  DBusString contained_str;
+
+  _dbus_return_val_if_fail (_dbus_message_iter_append_check (real), FALSE);
+  _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE);
+  _dbus_return_val_if_fail (dbus_type_is_container (type), FALSE);
+  _dbus_return_val_if_fail (sub != NULL, FALSE);
+  _dbus_return_val_if_fail ((type == DBUS_TYPE_STRUCT &&
+                             contained_signature == NULL) ||
+                            (type == DBUS_TYPE_DICT_ENTRY &&
+                             contained_signature == NULL) ||
+                            (type == DBUS_TYPE_VARIANT &&
+                             contained_signature != NULL) ||
+                            (type == DBUS_TYPE_ARRAY &&
+                             contained_signature != NULL), FALSE);
+  
+  /* this would fail if the contained_signature is a dict entry, since
+   * dict entries are invalid signatures standalone (they must be in
+   * an array)
+   */
+  _dbus_return_val_if_fail ((type == DBUS_TYPE_ARRAY && contained_signature && *contained_signature == DBUS_DICT_ENTRY_BEGIN_CHAR) ||
+                            (contained_signature == NULL ||
+                             _dbus_check_is_valid_signature (contained_signature)),
+                            FALSE);
+
+  if (!_dbus_message_iter_open_signature (real))
+    return FALSE;
+
+  *real_sub = *real;
+
+  if (contained_signature != NULL)
+    {
+      _dbus_string_init_const (&contained_str, contained_signature);
+
+      return _dbus_type_writer_recurse (&real->u.writer,
+                                        type,
+                                        &contained_str, 0,
+                                        &real_sub->u.writer);
+    }
+  else
+    {
+      return _dbus_type_writer_recurse (&real->u.writer,
+                                        type,
+                                        NULL, 0,
+                                        &real_sub->u.writer);
+    } 
+}
+
+
+/**
+ * Closes a container-typed value appended to the message; may write
+ * out more information to the message known only after the entire
+ * container is written, and may free resources created by
+ * dbus_message_iter_open_container().
+ *
+ * @todo If this fails due to lack of memory, the message is hosed and
+ * you have to start over building the whole message.
+ *
+ * @param iter the append iterator
+ * @param sub sub-iterator to close
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_message_iter_close_container (DBusMessageIter *iter,
+                                   DBusMessageIter *sub)
+{
+  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+  DBusMessageRealIter *real_sub = (DBusMessageRealIter *)sub;
+  dbus_bool_t ret;
+
+  _dbus_return_val_if_fail (_dbus_message_iter_append_check (real), FALSE);
+  _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE);
+  _dbus_return_val_if_fail (_dbus_message_iter_append_check (real_sub), FALSE);
+  _dbus_return_val_if_fail (real_sub->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE);
+
+  ret = _dbus_type_writer_unrecurse (&real->u.writer,
+                                     &real_sub->u.writer);
+
+  if (!_dbus_message_iter_close_signature (real))
+    ret = FALSE;
+
+  return ret;
+}
+
+/**
+ * Sets a flag indicating that the message does not want a reply; if
+ * this flag is set, the other end of the connection may (but is not
+ * required to) optimize by not sending method return or error
+ * replies. If this flag is set, there is no way to know whether the
+ * message successfully arrived at the remote end. Normally you know a
+ * message was received when you receive the reply to it.
+ *
+ * The flag is #FALSE by default, that is by default the other end is
+ * required to reply.
+ *
+ * On the protocol level this toggles #DBUS_HEADER_FLAG_NO_REPLY_EXPECTED
+ * 
+ * @param message the message
+ * @param no_reply #TRUE if no reply is desired
+ */
+void
+dbus_message_set_no_reply (DBusMessage *message,
+                           dbus_bool_t  no_reply)
+{
+  _dbus_return_if_fail (message != NULL);
+  _dbus_return_if_fail (!message->locked);
+
+  _dbus_header_toggle_flag (&message->header,
+                            DBUS_HEADER_FLAG_NO_REPLY_EXPECTED,
+                            no_reply);
+}
+
+/**
+ * Returns #TRUE if the message does not expect
+ * a reply.
+ *
+ * @param message the message
+ * @returns #TRUE if the message sender isn't waiting for a reply
+ */
+dbus_bool_t
+dbus_message_get_no_reply (DBusMessage *message)
+{
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+
+  return _dbus_header_get_flag (&message->header,
+                                DBUS_HEADER_FLAG_NO_REPLY_EXPECTED);
+}
+
+/**
+ * Sets a flag indicating that an owner for the destination name will
+ * be automatically started before the message is delivered. When this
+ * flag is set, the message is held until a name owner finishes
+ * starting up, or fails to start up. In case of failure, the reply
+ * will be an error.
+ *
+ * The flag is set to #TRUE by default, i.e. auto starting is the default.
+ *
+ * On the protocol level this toggles #DBUS_HEADER_FLAG_NO_AUTO_START
+ * 
+ * @param message the message
+ * @param auto_start #TRUE if auto-starting is desired
+ */
+void
+dbus_message_set_auto_start (DBusMessage *message,
+                             dbus_bool_t  auto_start)
+{
+  _dbus_return_if_fail (message != NULL);
+  _dbus_return_if_fail (!message->locked);
+
+  _dbus_header_toggle_flag (&message->header,
+                            DBUS_HEADER_FLAG_NO_AUTO_START,
+                            !auto_start);
+}
+
+/**
+ * Returns #TRUE if the message will cause an owner for
+ * destination name to be auto-started.
+ *
+ * @param message the message
+ * @returns #TRUE if the message will use auto-start
+ */
+dbus_bool_t
+dbus_message_get_auto_start (DBusMessage *message)
+{
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+
+  return !_dbus_header_get_flag (&message->header,
+                                 DBUS_HEADER_FLAG_NO_AUTO_START);
+}
+
+
+/**
+ * Sets the object path this message is being sent to (for
+ * DBUS_MESSAGE_TYPE_METHOD_CALL) or the one a signal is being
+ * emitted from (for DBUS_MESSAGE_TYPE_SIGNAL).
+ *
+ * The path must contain only valid characters as defined
+ * in the D-Bus specification.
+ *
+ * @param message the message
+ * @param object_path the path or #NULL to unset
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_message_set_path (DBusMessage   *message,
+                       const char    *object_path)
+{
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_fail (!message->locked, FALSE);
+  _dbus_return_val_if_fail (object_path == NULL ||
+                            _dbus_check_is_valid_path (object_path),
+                            FALSE);
+
+  return set_or_delete_string_field (message,
+                                     DBUS_HEADER_FIELD_PATH,
+                                     DBUS_TYPE_OBJECT_PATH,
+                                     object_path);
+}
+
+/**
+ * Gets the object path this message is being sent to (for
+ * DBUS_MESSAGE_TYPE_METHOD_CALL) or being emitted from (for
+ * DBUS_MESSAGE_TYPE_SIGNAL). Returns #NULL if none.
+ *
+ * See also dbus_message_get_path_decomposed().
+ *
+ * The returned string becomes invalid if the message is
+ * modified, since it points into the wire-marshaled message data.
+ * 
+ * @param message the message
+ * @returns the path (should not be freed) or #NULL
+ */
+const char*
+dbus_message_get_path (DBusMessage   *message)
+{
+  const char *v;
+
+  _dbus_return_val_if_fail (message != NULL, NULL);
+
+  v = NULL; /* in case field doesn't exist */
+  _dbus_header_get_field_basic (&message->header,
+                                DBUS_HEADER_FIELD_PATH,
+                                DBUS_TYPE_OBJECT_PATH,
+                                &v);
+  return v;
+}
+
+/**
+ * Checks if the message has a particular object path.  The object
+ * path is the destination object for a method call or the emitting
+ * object for a signal.
+ *
+ * @param message the message
+ * @param path the path name
+ * @returns #TRUE if there is a path field in the header
+ */
+dbus_bool_t
+dbus_message_has_path (DBusMessage   *message,
+                       const char    *path)
+{
+  const char *msg_path;
+  msg_path = dbus_message_get_path (message);
+  
+  if (msg_path == NULL)
+    {
+      if (path == NULL)
+        return TRUE;
+      else
+        return FALSE;
+    }
+
+  if (path == NULL)
+    return FALSE;
+   
+  if (strcmp (msg_path, path) == 0)
+    return TRUE;
+
+  return FALSE;
+}
+
+/**
+ * Gets the object path this message is being sent to
+ * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or being emitted
+ * from (for DBUS_MESSAGE_TYPE_SIGNAL) in a decomposed
+ * format (one array element per path component).
+ * Free the returned array with dbus_free_string_array().
+ *
+ * An empty but non-NULL path array means the path "/".
+ * So the path "/foo/bar" becomes { "foo", "bar", NULL }
+ * and the path "/" becomes { NULL }.
+ *
+ * See also dbus_message_get_path().
+ * 
+ * @todo this could be optimized by using the len from the message
+ * instead of calling strlen() again
+ *
+ * @param message the message
+ * @param path place to store allocated array of path components; #NULL set here if no path field exists
+ * @returns #FALSE if no memory to allocate the array
+ */
+dbus_bool_t
+dbus_message_get_path_decomposed (DBusMessage   *message,
+                                  char        ***path)
+{
+  const char *v;
+
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_fail (path != NULL, FALSE);
+
+  *path = NULL;
+
+  v = dbus_message_get_path (message);
+  if (v != NULL)
+    {
+      if (!_dbus_decompose_path (v, strlen (v),
+                                 path, NULL))
+        return FALSE;
+    }
+  return TRUE;
+}
+
+/**
+ * Sets the interface this message is being sent to
+ * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or
+ * the interface a signal is being emitted from
+ * (for DBUS_MESSAGE_TYPE_SIGNAL).
+ *
+ * The interface name must contain only valid characters as defined
+ * in the D-Bus specification.
+ * 
+ * @param message the message
+ * @param interface the interface or #NULL to unset
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_message_set_interface (DBusMessage  *message,
+                            const char   *interface)
+{
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_fail (!message->locked, FALSE);
+  _dbus_return_val_if_fail (interface == NULL ||
+                            _dbus_check_is_valid_interface (interface),
+                            FALSE);
+
+  return set_or_delete_string_field (message,
+                                     DBUS_HEADER_FIELD_INTERFACE,
+                                     DBUS_TYPE_STRING,
+                                     interface);
+}
+
+/**
+ * Gets the interface this message is being sent to
+ * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or being emitted
+ * from (for DBUS_MESSAGE_TYPE_SIGNAL).
+ * The interface name is fully-qualified (namespaced).
+ * Returns #NULL if none.
+ *
+ * The returned string becomes invalid if the message is
+ * modified, since it points into the wire-marshaled message data.
+ *
+ * @param message the message
+ * @returns the message interface (should not be freed) or #NULL
+ */
+const char*
+dbus_message_get_interface (DBusMessage *message)
+{
+  const char *v;
+
+  _dbus_return_val_if_fail (message != NULL, NULL);
+
+  v = NULL; /* in case field doesn't exist */
+  _dbus_header_get_field_basic (&message->header,
+                                DBUS_HEADER_FIELD_INTERFACE,
+                                DBUS_TYPE_STRING,
+                                &v);
+  return v;
+}
+
+/**
+ * Checks if the message has an interface
+ *
+ * @param message the message
+ * @param interface the interface name
+ * @returns #TRUE if the interface field in the header matches
+ */
+dbus_bool_t
+dbus_message_has_interface (DBusMessage   *message,
+                            const char    *interface)
+{
+  const char *msg_interface;
+  msg_interface = dbus_message_get_interface (message);
+   
+  if (msg_interface == NULL)
+    {
+      if (interface == NULL)
+        return TRUE;
+      else
+        return FALSE;
+    }
+
+  if (interface == NULL)
+    return FALSE;
+     
+  if (strcmp (msg_interface, interface) == 0)
+    return TRUE;
+
+  return FALSE;
+
+}
+
+/**
+ * Sets the interface member being invoked
+ * (DBUS_MESSAGE_TYPE_METHOD_CALL) or emitted
+ * (DBUS_MESSAGE_TYPE_SIGNAL).
+ *
+ * The member name must contain only valid characters as defined
+ * in the D-Bus specification.
+ *
+ * @param message the message
+ * @param member the member or #NULL to unset
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_message_set_member (DBusMessage  *message,
+                         const char   *member)
+{
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_fail (!message->locked, FALSE);
+  _dbus_return_val_if_fail (member == NULL ||
+                            _dbus_check_is_valid_member (member),
+                            FALSE);
+
+  return set_or_delete_string_field (message,
+                                     DBUS_HEADER_FIELD_MEMBER,
+                                     DBUS_TYPE_STRING,
+                                     member);
+}
+
+/**
+ * Gets the interface member being invoked
+ * (DBUS_MESSAGE_TYPE_METHOD_CALL) or emitted
+ * (DBUS_MESSAGE_TYPE_SIGNAL). Returns #NULL if none.
+ *
+ * The returned string becomes invalid if the message is
+ * modified, since it points into the wire-marshaled message data.
+ * 
+ * @param message the message
+ * @returns the member name (should not be freed) or #NULL
+ */
+const char*
+dbus_message_get_member (DBusMessage *message)
+{
+  const char *v;
+
+  _dbus_return_val_if_fail (message != NULL, NULL);
+
+  v = NULL; /* in case field doesn't exist */
+  _dbus_header_get_field_basic (&message->header,
+                                DBUS_HEADER_FIELD_MEMBER,
+                                DBUS_TYPE_STRING,
+                                &v);
+  return v;
+}
+
+/**
+ * Checks if the message has an interface member
+ *
+ * @param message the message
+ * @param member the member name
+ * @returns #TRUE if there is a member field in the header
+ */
+dbus_bool_t
+dbus_message_has_member (DBusMessage   *message,
+                         const char    *member)
+{
+  const char *msg_member;
+  msg_member = dbus_message_get_member (message);
+  if (msg_member == NULL)
+    {
+      if (member == NULL)
+        return TRUE;
+      else
+        return FALSE;
+    }
+
+  if (member == NULL)
+    return FALSE;
+    
+  if (strcmp (msg_member, member) == 0)
+    return TRUE;
+
+  return FALSE;
+
+}
+
+/**
+ * Sets the name of the error (DBUS_MESSAGE_TYPE_ERROR).
+ * The name is fully-qualified (namespaced).
+ *
+ * The error name must contain only valid characters as defined
+ * in the D-Bus specification.
+ *
+ * @param message the message
+ * @param error_name the name or #NULL to unset
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_message_set_error_name (DBusMessage  *message,
+                             const char   *error_name)
+{
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_fail (!message->locked, FALSE);
+  _dbus_return_val_if_fail (error_name == NULL ||
+                            _dbus_check_is_valid_error_name (error_name),
+                            FALSE);
+
+  return set_or_delete_string_field (message,
+                                     DBUS_HEADER_FIELD_ERROR_NAME,
+                                     DBUS_TYPE_STRING,
+                                     error_name);
+}
+
+/**
+ * Gets the error name (DBUS_MESSAGE_TYPE_ERROR only)
+ * or #NULL if none.
+ *
+ * The returned string becomes invalid if the message is
+ * modified, since it points into the wire-marshaled message data.
+ * 
+ * @param message the message
+ * @returns the error name (should not be freed) or #NULL
+ */
+const char*
+dbus_message_get_error_name (DBusMessage *message)
+{
+  const char *v;
+
+  _dbus_return_val_if_fail (message != NULL, NULL);
+
+  v = NULL; /* in case field doesn't exist */
+  _dbus_header_get_field_basic (&message->header,
+                                DBUS_HEADER_FIELD_ERROR_NAME,
+                                DBUS_TYPE_STRING,
+                                &v);
+  return v;
+}
+
+/**
+ * Sets the message's destination. The destination is the name of
+ * another connection on the bus and may be either the unique name
+ * assigned by the bus to each connection, or a well-known name
+ * specified in advance.
+ *
+ * The destination name must contain only valid characters as defined
+ * in the D-Bus specification.
+ * 
+ * @param message the message
+ * @param destination the destination name or #NULL to unset
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_message_set_destination (DBusMessage  *message,
+                              const char   *destination)
+{
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_fail (!message->locked, FALSE);
+  _dbus_return_val_if_fail (destination == NULL ||
+                            _dbus_check_is_valid_bus_name (destination),
+                            FALSE);
+
+  return set_or_delete_string_field (message,
+                                     DBUS_HEADER_FIELD_DESTINATION,
+                                     DBUS_TYPE_STRING,
+                                     destination);
+}
+
+/**
+ * Gets the destination of a message or #NULL if there is none set.
+ *
+ * The returned string becomes invalid if the message is
+ * modified, since it points into the wire-marshaled message data.
+ *
+ * @param message the message
+ * @returns the message destination (should not be freed) or #NULL
+ */
+const char*
+dbus_message_get_destination (DBusMessage *message)
+{
+  const char *v;
+
+  _dbus_return_val_if_fail (message != NULL, NULL);
+
+  v = NULL; /* in case field doesn't exist */
+  _dbus_header_get_field_basic (&message->header,
+                                DBUS_HEADER_FIELD_DESTINATION,
+                                DBUS_TYPE_STRING,
+                                &v);
+  return v;
+}
+
+/**
+ * Sets the message sender.
+ *
+ * The sender must be a valid bus name as defined in the D-Bus
+ * specification.
+ *
+ * Usually you don't want to call this. The message bus daemon will
+ * call it to set the origin of each message. If you aren't implementing
+ * a message bus daemon you shouldn't need to set the sender.
+ *
+ * @param message the message
+ * @param sender the sender or #NULL to unset
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_message_set_sender (DBusMessage  *message,
+                         const char   *sender)
+{
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_fail (!message->locked, FALSE);
+  _dbus_return_val_if_fail (sender == NULL ||
+                            _dbus_check_is_valid_bus_name (sender),
+                            FALSE);
+
+  return set_or_delete_string_field (message,
+                                     DBUS_HEADER_FIELD_SENDER,
+                                     DBUS_TYPE_STRING,
+                                     sender);
+}
+
+/**
+ * Gets the unique name of the connection which originated this
+ * message, or #NULL if unknown or inapplicable. The sender is filled
+ * in by the message bus.
+ *
+ * Note, the returned sender is always the unique bus name.
+ * Connections may own multiple other bus names, but those
+ * are not found in the sender field.
+ * 
+ * The returned string becomes invalid if the message is
+ * modified, since it points into the wire-marshaled message data.
+ *
+ * @param message the message
+ * @returns the unique name of the sender or #NULL
+ */
+const char*
+dbus_message_get_sender (DBusMessage *message)
+{
+  const char *v;
+
+  _dbus_return_val_if_fail (message != NULL, NULL);
+
+  v = NULL; /* in case field doesn't exist */
+  _dbus_header_get_field_basic (&message->header,
+                                DBUS_HEADER_FIELD_SENDER,
+                                DBUS_TYPE_STRING,
+                                &v);
+  return v;
+}
+
+/**
+ * Gets the type signature of the message, i.e. the arguments in the
+ * message payload. The signature includes only "in" arguments for
+ * #DBUS_MESSAGE_TYPE_METHOD_CALL and only "out" arguments for
+ * #DBUS_MESSAGE_TYPE_METHOD_RETURN, so is slightly different from
+ * what you might expect (that is, it does not include the signature of the
+ * entire C++-style method).
+ *
+ * The signature is a string made up of type codes such as
+ * #DBUS_TYPE_INT32. The string is terminated with nul (nul is also
+ * the value of #DBUS_TYPE_INVALID).
+ *
+ * The returned string becomes invalid if the message is
+ * modified, since it points into the wire-marshaled message data.
+ *
+ * @param message the message
+ * @returns the type signature
+ */
+const char*
+dbus_message_get_signature (DBusMessage *message)
+{
+  const DBusString *type_str;
+  int type_pos;
+
+  _dbus_return_val_if_fail (message != NULL, NULL);
+
+  get_const_signature (&message->header, &type_str, &type_pos);
+
+  return _dbus_string_get_const_data_len (type_str, type_pos, 0);
+}
+
+static dbus_bool_t
+_dbus_message_has_type_interface_member (DBusMessage *message,
+                                         int          type,
+                                         const char  *interface,
+                                         const char  *member)
+{
+  const char *n;
+
+  _dbus_assert (message != NULL);
+  _dbus_assert (interface != NULL);
+  _dbus_assert (member != NULL);
+
+  if (dbus_message_get_type (message) != type)
+    return FALSE;
+
+  /* Optimize by checking the short member name first
+   * instead of the longer interface name
+   */
+
+  n = dbus_message_get_member (message);
+
+  if (n && strcmp (n, member) == 0)
+    {
+      n = dbus_message_get_interface (message);
+
+      if (n == NULL || strcmp (n, interface) == 0)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+/**
+ * Checks whether the message is a method call with the given
+ * interface and member fields.  If the message is not
+ * #DBUS_MESSAGE_TYPE_METHOD_CALL, or has a different interface or
+ * member field, returns #FALSE. If the interface field is missing,
+ * then it will be assumed equal to the provided interface.  The D-Bus
+ * protocol allows method callers to leave out the interface name.
+ *
+ * @param message the message
+ * @param interface the name to check (must not be #NULL)
+ * @param method the name to check (must not be #NULL)
+ *
+ * @returns #TRUE if the message is the specified method call
+ */
+dbus_bool_t
+dbus_message_is_method_call (DBusMessage *message,
+                             const char  *interface,
+                             const char  *method)
+{
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_fail (interface != NULL, FALSE);
+  _dbus_return_val_if_fail (method != NULL, FALSE);
+  /* don't check that interface/method are valid since it would be
+   * expensive, and not catch many common errors
+   */
+
+  return _dbus_message_has_type_interface_member (message,
+                                                  DBUS_MESSAGE_TYPE_METHOD_CALL,
+                                                  interface, method);
+}
+
+/**
+ * Checks whether the message is a signal with the given interface and
+ * member fields.  If the message is not #DBUS_MESSAGE_TYPE_SIGNAL, or
+ * has a different interface or member field, returns #FALSE.
+ *
+ * @param message the message
+ * @param interface the name to check (must not be #NULL)
+ * @param signal_name the name to check (must not be #NULL)
+ *
+ * @returns #TRUE if the message is the specified signal
+ */
+dbus_bool_t
+dbus_message_is_signal (DBusMessage *message,
+                        const char  *interface,
+                        const char  *signal_name)
+{
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_fail (interface != NULL, FALSE);
+  _dbus_return_val_if_fail (signal_name != NULL, FALSE);
+  /* don't check that interface/name are valid since it would be
+   * expensive, and not catch many common errors
+   */
+
+  return _dbus_message_has_type_interface_member (message,
+                                                  DBUS_MESSAGE_TYPE_SIGNAL,
+                                                  interface, signal_name);
+}
+
+/**
+ * Checks whether the message is an error reply with the given error
+ * name.  If the message is not #DBUS_MESSAGE_TYPE_ERROR, or has a
+ * different name, returns #FALSE.
+ *
+ * @param message the message
+ * @param error_name the name to check (must not be #NULL)
+ *
+ * @returns #TRUE if the message is the specified error
+ */
+dbus_bool_t
+dbus_message_is_error (DBusMessage *message,
+                       const char  *error_name)
+{
+  const char *n;
+
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_fail (error_name != NULL, FALSE);
+  /* don't check that error_name is valid since it would be expensive,
+   * and not catch many common errors
+   */
+
+  if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_ERROR)
+    return FALSE;
+
+  n = dbus_message_get_error_name (message);
+
+  if (n && strcmp (n, error_name) == 0)
+    return TRUE;
+  else
+    return FALSE;
+}
+
+/**
+ * Checks whether the message was sent to the given name.  If the
+ * message has no destination specified or has a different
+ * destination, returns #FALSE.
+ *
+ * @param message the message
+ * @param name the name to check (must not be #NULL)
+ *
+ * @returns #TRUE if the message has the given destination name
+ */
+dbus_bool_t
+dbus_message_has_destination (DBusMessage  *message,
+                              const char   *name)
+{
+  const char *s;
+
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_fail (name != NULL, FALSE);
+  /* don't check that name is valid since it would be expensive, and
+   * not catch many common errors
+   */
+
+  s = dbus_message_get_destination (message);
+
+  if (s && strcmp (s, name) == 0)
+    return TRUE;
+  else
+    return FALSE;
+}
+
+/**
+ * Checks whether the message has the given unique name as its sender.
+ * If the message has no sender specified or has a different sender,
+ * returns #FALSE. Note that a peer application will always have the
+ * unique name of the connection as the sender. So you can't use this
+ * function to see whether a sender owned a well-known name.
+ *
+ * Messages from the bus itself will have #DBUS_SERVICE_DBUS
+ * as the sender.
+ *
+ * @param message the message
+ * @param name the name to check (must not be #NULL)
+ *
+ * @returns #TRUE if the message has the given sender
+ */
+dbus_bool_t
+dbus_message_has_sender (DBusMessage  *message,
+                         const char   *name)
+{
+  const char *s;
+
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_fail (name != NULL, FALSE);
+  /* don't check that name is valid since it would be expensive, and
+   * not catch many common errors
+   */
+
+  s = dbus_message_get_sender (message);
+
+  if (s && strcmp (s, name) == 0)
+    return TRUE;
+  else
+    return FALSE;
+}
+
+/**
+ * Checks whether the message has the given signature; see
+ * dbus_message_get_signature() for more details on what the signature
+ * looks like.
+ *
+ * @param message the message
+ * @param signature typecode array
+ * @returns #TRUE if message has the given signature
+*/
+dbus_bool_t
+dbus_message_has_signature (DBusMessage   *message,
+                            const char    *signature)
+{
+  const char *s;
+
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_fail (signature != NULL, FALSE);
+  /* don't check that signature is valid since it would be expensive,
+   * and not catch many common errors
+   */
+
+  s = dbus_message_get_signature (message);
+
+  if (s && strcmp (s, signature) == 0)
+    return TRUE;
+  else
+    return FALSE;
+}
+
+/**
+ * Sets a #DBusError based on the contents of the given
+ * message. The error is only set if the message
+ * is an error message, as in #DBUS_MESSAGE_TYPE_ERROR.
+ * The name of the error is set to the name of the message,
+ * and the error message is set to the first argument
+ * if the argument exists and is a string.
+ *
+ * The return value indicates whether the error was set (the error is
+ * set if and only if the message is an error message).  So you can
+ * check for an error reply and convert it to DBusError in one go:
+ * @code
+ *  if (dbus_set_error_from_message (error, reply))
+ *    return error;
+ *  else
+ *    process reply;
+ * @endcode
+ *
+ * @param error the error to set
+ * @param message the message to set it from
+ * @returns #TRUE if the message had type #DBUS_MESSAGE_TYPE_ERROR
+ */
+dbus_bool_t
+dbus_set_error_from_message (DBusError   *error,
+                             DBusMessage *message)
+{
+  const char *str;
+
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_error_is_set (error, FALSE);
+
+  if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_ERROR)
+    return FALSE;
+
+  str = NULL;
+  dbus_message_get_args (message, NULL,
+                         DBUS_TYPE_STRING, &str,
+                         DBUS_TYPE_INVALID);
+
+  dbus_set_error (error, dbus_message_get_error_name (message),
+                  str ? "%s" : NULL, str);
+
+  return TRUE;
+}
+
+/** @} */
+
+/**
+ * @addtogroup DBusMessageInternals
+ *
+ * @{
+ */
+
+/**
+ * The initial buffer size of the message loader.
+ *
+ * @todo this should be based on min header size plus some average
+ * body size, or something. Or rather, the min header size only, if we
+ * want to try to read only the header, store that in a DBusMessage,
+ * then read only the body and store that, etc., depends on
+ * how we optimize _dbus_message_loader_get_buffer() and what
+ * the exact message format is.
+ */
+#define INITIAL_LOADER_DATA_LEN 32
+
+/**
+ * Creates a new message loader. Returns #NULL if memory can't
+ * be allocated.
+ *
+ * @returns new loader, or #NULL.
+ */
+DBusMessageLoader*
+_dbus_message_loader_new (void)
+{
+  DBusMessageLoader *loader;
+
+  loader = dbus_new0 (DBusMessageLoader, 1);
+  if (loader == NULL)
+    return NULL;
+  
+  loader->refcount = 1;
+
+  loader->corrupted = FALSE;
+  loader->corruption_reason = DBUS_VALID;
+
+  /* this can be configured by the app, but defaults to the protocol max */
+  loader->max_message_size = DBUS_MAXIMUM_MESSAGE_LENGTH;
+
+  if (!_dbus_string_init (&loader->data))
+    {
+      dbus_free (loader);
+      return NULL;
+    }
+
+  /* preallocate the buffer for speed, ignore failure */
+  _dbus_string_set_length (&loader->data, INITIAL_LOADER_DATA_LEN);
+  _dbus_string_set_length (&loader->data, 0);
+
+  return loader;
+}
+
+/**
+ * Increments the reference count of the loader.
+ *
+ * @param loader the loader.
+ * @returns the loader
+ */
+DBusMessageLoader *
+_dbus_message_loader_ref (DBusMessageLoader *loader)
+{
+  loader->refcount += 1;
+
+  return loader;
+}
+
+/**
+ * Decrements the reference count of the loader and finalizes the
+ * loader when the count reaches zero.
+ *
+ * @param loader the loader.
+ */
+void
+_dbus_message_loader_unref (DBusMessageLoader *loader)
+{
+  loader->refcount -= 1;
+  if (loader->refcount == 0)
+    {
+      _dbus_list_foreach (&loader->messages,
+                          (DBusForeachFunction) dbus_message_unref,
+                          NULL);
+      _dbus_list_clear (&loader->messages);
+      _dbus_string_free (&loader->data);
+      dbus_free (loader);
+    }
+}
+
+/**
+ * Gets the buffer to use for reading data from the network.  Network
+ * data is read directly into an allocated buffer, which is then used
+ * in the DBusMessage, to avoid as many extra memcpy's as possible.
+ * The buffer must always be returned immediately using
+ * _dbus_message_loader_return_buffer(), even if no bytes are
+ * successfully read.
+ *
+ * @todo this function can be a lot more clever. For example
+ * it can probably always return a buffer size to read exactly
+ * the body of the next message, thus avoiding any memory wastage
+ * or reallocs.
+ *
+ * @todo we need to enforce a max length on strings in header fields.
+ *
+ * @param loader the message loader.
+ * @param buffer the buffer
+ */
+void
+_dbus_message_loader_get_buffer (DBusMessageLoader  *loader,
+                                 DBusString        **buffer)
+{
+  _dbus_assert (!loader->buffer_outstanding);
+
+  *buffer = &loader->data;
+
+  loader->buffer_outstanding = TRUE;
+}
+
+/**
+ * Returns a buffer obtained from _dbus_message_loader_get_buffer(),
+ * indicating to the loader how many bytes of the buffer were filled
+ * in. This function must always be called, even if no bytes were
+ * successfully read.
+ *
+ * @param loader the loader.
+ * @param buffer the buffer.
+ * @param bytes_read number of bytes that were read into the buffer.
+ */
+void
+_dbus_message_loader_return_buffer (DBusMessageLoader  *loader,
+                                    DBusString         *buffer,
+                                    int                 bytes_read)
+{
+  _dbus_assert (loader->buffer_outstanding);
+  _dbus_assert (buffer == &loader->data);
+
+  loader->buffer_outstanding = FALSE;
+}
+
+/*
+ * FIXME when we move the header out of the buffer, that memmoves all
+ * buffered messages. Kind of crappy.
+ *
+ * Also we copy the header and body, which is kind of crappy.  To
+ * avoid this, we have to allow header and body to be in a single
+ * memory block, which is good for messages we read and bad for
+ * messages we are creating. But we could move_len() the buffer into
+ * this single memory block, and move_len() will just swap the buffers
+ * if you're moving the entire buffer replacing the dest string.
+ *
+ * We could also have the message loader tell the transport how many
+ * bytes to read; so it would first ask for some arbitrary number like
+ * 256, then if the message was incomplete it would use the
+ * header/body len to ask for exactly the size of the message (or
+ * blocks the size of a typical kernel buffer for the socket). That
+ * way we don't get trailing bytes in the buffer that have to be
+ * memmoved. Though I suppose we also don't have a chance of reading a
+ * bunch of small messages at once, so the optimization may be stupid.
+ *
+ * Another approach would be to keep a "start" index into
+ * loader->data and only delete it occasionally, instead of after
+ * each message is loaded.
+ *
+ * load_message() returns FALSE if not enough memory OR the loader was corrupted
+ */
+static dbus_bool_t
+load_message (DBusMessageLoader *loader,
+              DBusMessage       *message,
+              int                byte_order,
+              int                fields_array_len,
+              int                header_len,
+              int                body_len)
+{
+  dbus_bool_t oom;
+  DBusValidity validity;
+  const DBusString *type_str;
+  int type_pos;
+  DBusValidationMode mode;
+
+  mode = DBUS_VALIDATION_MODE_DATA_IS_UNTRUSTED;
+  
+  oom = FALSE;
+
+#if 0
+  _dbus_verbose_bytes_of_string (&loader->data, 0, header_len /* + body_len */);
+#endif
+
+  /* 1. VALIDATE AND COPY OVER HEADER */
+  _dbus_assert (_dbus_string_get_length (&message->header.data) == 0);
+  _dbus_assert ((header_len + body_len) <= _dbus_string_get_length (&loader->data));
+
+  if (!_dbus_header_load (&message->header,
+                          mode,
+                          &validity,
+                          byte_order,
+                          fields_array_len,
+                          header_len,
+                          body_len,
+                          &loader->data, 0,
+                          _dbus_string_get_length (&loader->data)))
+    {
+      _dbus_verbose ("Failed to load header for new message code %d\n", validity);
+
+      /* assert here so we can catch any code that still uses DBUS_VALID to indicate
+         oom errors.  They should use DBUS_VALIDITY_UNKNOWN_OOM_ERROR instead */
+      _dbus_assert (validity != DBUS_VALID);
+
+      if (validity == DBUS_VALIDITY_UNKNOWN_OOM_ERROR)
+        oom = TRUE;
+      else
+        {
+          loader->corrupted = TRUE;
+          loader->corruption_reason = validity;
+        }
+      goto failed;
+    }
+
+  _dbus_assert (validity == DBUS_VALID);
+
+  message->byte_order = byte_order;
+
+  /* 2. VALIDATE BODY */
+  if (mode != DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY)
+    {
+      get_const_signature (&message->header, &type_str, &type_pos);
+      
+      /* Because the bytes_remaining arg is NULL, this validates that the
+       * body is the right length
+       */
+      validity = _dbus_validate_body_with_reason (type_str,
+                                                  type_pos,
+                                                  byte_order,
+                                                  NULL,
+                                                  &loader->data,
+                                                  header_len,
+                                                  body_len);
+      if (validity != DBUS_VALID)
+        {
+          _dbus_verbose ("Failed to validate message body code %d\n", validity);
+
+          loader->corrupted = TRUE;
+          loader->corruption_reason = validity;
+          
+          goto failed;
+        }
+    }
+
+  /* 3. COPY OVER BODY AND QUEUE MESSAGE */
+
+  if (!_dbus_list_append (&loader->messages, message))
+    {
+      _dbus_verbose ("Failed to append new message to loader queue\n");
+      oom = TRUE;
+      goto failed;
+    }
+
+  _dbus_assert (_dbus_string_get_length (&message->body) == 0);
+  _dbus_assert (_dbus_string_get_length (&loader->data) >=
+                (header_len + body_len));
+
+  if (!_dbus_string_copy_len (&loader->data, header_len, body_len, &message->body, 0))
+    {
+      _dbus_verbose ("Failed to move body into new message\n");
+      oom = TRUE;
+      goto failed;
+    }
+
+  _dbus_string_delete (&loader->data, 0, header_len + body_len);
+
+  /* don't waste more than 2k of memory */
+  _dbus_string_compact (&loader->data, 2048);
+
+  _dbus_assert (_dbus_string_get_length (&message->header.data) == header_len);
+  _dbus_assert (_dbus_string_get_length (&message->body) == body_len);
+
+  _dbus_verbose ("Loaded message %p\n", message);
+
+  _dbus_assert (!oom);
+  _dbus_assert (!loader->corrupted);
+  _dbus_assert (loader->messages != NULL);
+  _dbus_assert (_dbus_list_find_last (&loader->messages, message) != NULL);
+
+  return TRUE;
+
+ failed:
+
+  /* Clean up */
+
+  /* does nothing if the message isn't in the list */
+  _dbus_list_remove_last (&loader->messages, message);
+  
+  if (oom)
+    _dbus_assert (!loader->corrupted);
+  else
+    _dbus_assert (loader->corrupted);
+
+  _dbus_verbose_bytes_of_string (&loader->data, 0, _dbus_string_get_length (&loader->data));
+
+  return FALSE;
+}
+
+/**
+ * Converts buffered data into messages, if we have enough data.  If
+ * we don't have enough data, does nothing.
+ *
+ * @todo we need to check that the proper named header fields exist
+ * for each message type.
+ *
+ * @todo If a message has unknown type, we should probably eat it
+ * right here rather than passing it out to applications.  However
+ * it's not an error to see messages of unknown type.
+ *
+ * @param loader the loader.
+ * @returns #TRUE if we had enough memory to finish.
+ */
+dbus_bool_t
+_dbus_message_loader_queue_messages (DBusMessageLoader *loader)
+{
+  while (!loader->corrupted &&
+         _dbus_string_get_length (&loader->data) >= DBUS_MINIMUM_HEADER_SIZE)
+    {
+      DBusValidity validity;
+      int byte_order, fields_array_len, header_len, body_len;
+
+      if (_dbus_header_have_message_untrusted (loader->max_message_size,
+                                               &validity,
+                                               &byte_order,
+                                               &fields_array_len,
+                                               &header_len,
+                                               &body_len,
+                                               &loader->data, 0,
+                                               _dbus_string_get_length (&loader->data)))
+        {
+          DBusMessage *message;
+
+          _dbus_assert (validity == DBUS_VALID);
+
+          message = dbus_message_new_empty_header ();
+          if (message == NULL)
+            return FALSE;
+
+          if (!load_message (loader, message,
+                             byte_order, fields_array_len,
+                             header_len, body_len))
+            {
+              dbus_message_unref (message);
+              /* load_message() returns false if corrupted or OOM; if
+               * corrupted then return TRUE for not OOM
+               */
+              return loader->corrupted;
+            }
+
+          _dbus_assert (loader->messages != NULL);
+          _dbus_assert (_dbus_list_find_last (&loader->messages, message) != NULL);
+       }
+      else
+        {
+          _dbus_verbose ("Initial peek at header says we don't have a whole message yet, or data broken with invalid code %d\n",
+                         validity);
+          if (validity != DBUS_VALID)
+            {
+              loader->corrupted = TRUE;
+              loader->corruption_reason = validity;
+            }
+          return TRUE;
+        }
+    }
+
+  return TRUE;
+}
+
+/**
+ * Peeks at first loaded message, returns #NULL if no messages have
+ * been queued.
+ *
+ * @param loader the loader.
+ * @returns the next message, or #NULL if none.
+ */
+DBusMessage*
+_dbus_message_loader_peek_message (DBusMessageLoader *loader)
+{
+  if (loader->messages)
+    return loader->messages->data;
+  else
+    return NULL;
+}
+
+/**
+ * Pops a loaded message (passing ownership of the message
+ * to the caller). Returns #NULL if no messages have been
+ * queued.
+ *
+ * @param loader the loader.
+ * @returns the next message, or #NULL if none.
+ */
+DBusMessage*
+_dbus_message_loader_pop_message (DBusMessageLoader *loader)
+{
+  return _dbus_list_pop_first (&loader->messages);
+}
+
+/**
+ * Pops a loaded message inside a list link (passing ownership of the
+ * message and link to the caller). Returns #NULL if no messages have
+ * been loaded.
+ *
+ * @param loader the loader.
+ * @returns the next message link, or #NULL if none.
+ */
+DBusList*
+_dbus_message_loader_pop_message_link (DBusMessageLoader *loader)
+{
+  return _dbus_list_pop_first_link (&loader->messages);
+}
+
+/**
+ * Returns a popped message link, used to undo a pop.
+ *
+ * @param loader the loader
+ * @param link the link with a message in it
+ */
+void
+_dbus_message_loader_putback_message_link (DBusMessageLoader  *loader,
+                                           DBusList           *link)
+{
+  _dbus_list_prepend_link (&loader->messages, link);
+}
+
+/**
+ * Checks whether the loader is confused due to bad data.
+ * If messages are received that are invalid, the
+ * loader gets confused and gives up permanently.
+ * This state is called "corrupted."
+ *
+ * @param loader the loader
+ * @returns #TRUE if the loader is hosed.
+ */
+dbus_bool_t
+_dbus_message_loader_get_is_corrupted (DBusMessageLoader *loader)
+{
+  _dbus_assert ((loader->corrupted && loader->corruption_reason != DBUS_VALID) ||
+                (!loader->corrupted && loader->corruption_reason == DBUS_VALID));
+  return loader->corrupted;
+}
+
+/**
+ * Sets the maximum size message we allow.
+ *
+ * @param loader the loader
+ * @param size the max message size in bytes
+ */
+void
+_dbus_message_loader_set_max_message_size (DBusMessageLoader  *loader,
+                                           long                size)
+{
+  if (size > DBUS_MAXIMUM_MESSAGE_LENGTH)
+    {
+      _dbus_verbose ("clamping requested max message size %ld to %d\n",
+                     size, DBUS_MAXIMUM_MESSAGE_LENGTH);
+      size = DBUS_MAXIMUM_MESSAGE_LENGTH;
+    }
+  loader->max_message_size = size;
+}
+
+/**
+ * Gets the maximum allowed message size in bytes.
+ *
+ * @param loader the loader
+ * @returns max size in bytes
+ */
+long
+_dbus_message_loader_get_max_message_size (DBusMessageLoader  *loader)
+{
+  return loader->max_message_size;
+}
+
+static DBusDataSlotAllocator slot_allocator;
+_DBUS_DEFINE_GLOBAL_LOCK (message_slots);
+
+/**
+ * Allocates an integer ID to be used for storing application-specific
+ * data on any DBusMessage. The allocated ID may then be used
+ * with dbus_message_set_data() and dbus_message_get_data().
+ * The passed-in slot must be initialized to -1, and is filled in
+ * with the slot ID. If the passed-in slot is not -1, it's assumed
+ * to be already allocated, and its refcount is incremented.
+ *
+ * The allocated slot is global, i.e. all DBusMessage objects will
+ * have a slot with the given integer ID reserved.
+ *
+ * @param slot_p address of a global variable storing the slot
+ * @returns #FALSE on failure (no memory)
+ */
+dbus_bool_t
+dbus_message_allocate_data_slot (dbus_int32_t *slot_p)
+{
+  return _dbus_data_slot_allocator_alloc (&slot_allocator,
+                                          &_DBUS_LOCK_NAME (message_slots),
+                                          slot_p);
+}
+
+/**
+ * Deallocates a global ID for message data slots.
+ * dbus_message_get_data() and dbus_message_set_data() may no
+ * longer be used with this slot.  Existing data stored on existing
+ * DBusMessage objects will be freed when the message is
+ * finalized, but may not be retrieved (and may only be replaced if
+ * someone else reallocates the slot).  When the refcount on the
+ * passed-in slot reaches 0, it is set to -1.
+ *
+ * @param slot_p address storing the slot to deallocate
+ */
+void
+dbus_message_free_data_slot (dbus_int32_t *slot_p)
+{
+  _dbus_return_if_fail (*slot_p >= 0);
+
+  _dbus_data_slot_allocator_free (&slot_allocator, slot_p);
+}
+
+/**
+ * Stores a pointer on a DBusMessage, along
+ * with an optional function to be used for freeing
+ * the data when the data is set again, or when
+ * the message is finalized. The slot number
+ * must have been allocated with dbus_message_allocate_data_slot().
+ *
+ * @param message the message
+ * @param slot the slot number
+ * @param data the data to store
+ * @param free_data_func finalizer function for the data
+ * @returns #TRUE if there was enough memory to store the data
+ */
+dbus_bool_t
+dbus_message_set_data (DBusMessage     *message,
+                       dbus_int32_t     slot,
+                       void            *data,
+                       DBusFreeFunction free_data_func)
+{
+  DBusFreeFunction old_free_func;
+  void *old_data;
+  dbus_bool_t retval;
+
+  _dbus_return_val_if_fail (message != NULL, FALSE);
+  _dbus_return_val_if_fail (slot >= 0, FALSE);
+
+  retval = _dbus_data_slot_list_set (&slot_allocator,
+                                     &message->slot_list,
+                                     slot, data, free_data_func,
+                                     &old_free_func, &old_data);
+
+  if (retval)
+    {
+      /* Do the actual free outside the message lock */
+      if (old_free_func)
+        (* old_free_func) (old_data);
+    }
+
+  return retval;
+}
+
+/**
+ * Retrieves data previously set with dbus_message_set_data().
+ * The slot must still be allocated (must not have been freed).
+ *
+ * @param message the message
+ * @param slot the slot to get data from
+ * @returns the data, or #NULL if not found
+ */
+void*
+dbus_message_get_data (DBusMessage   *message,
+                       dbus_int32_t   slot)
+{
+  void *res;
+
+  _dbus_return_val_if_fail (message != NULL, NULL);
+
+  res = _dbus_data_slot_list_get (&slot_allocator,
+                                  &message->slot_list,
+                                  slot);
+
+  return res;
+}
+
+/**
+ * Utility function to convert a machine-readable (not translated)
+ * string into a D-Bus message type.
+ *
+ * @code
+ *   "method_call"    -> DBUS_MESSAGE_TYPE_METHOD_CALL
+ *   "method_return"  -> DBUS_MESSAGE_TYPE_METHOD_RETURN
+ *   "signal"         -> DBUS_MESSAGE_TYPE_SIGNAL
+ *   "error"          -> DBUS_MESSAGE_TYPE_ERROR
+ *   anything else    -> DBUS_MESSAGE_TYPE_INVALID
+ * @endcode
+ *
+ */
+int
+dbus_message_type_from_string (const char *type_str)
+{
+  if (strcmp (type_str, "method_call") == 0)
+    return DBUS_MESSAGE_TYPE_METHOD_CALL;
+  if (strcmp (type_str, "method_return") == 0)
+    return DBUS_MESSAGE_TYPE_METHOD_RETURN;
+  else if (strcmp (type_str, "signal") == 0)
+    return DBUS_MESSAGE_TYPE_SIGNAL;
+  else if (strcmp (type_str, "error") == 0)
+    return DBUS_MESSAGE_TYPE_ERROR;
+  else
+    return DBUS_MESSAGE_TYPE_INVALID;
+}
+
+/**
+ * Utility function to convert a D-Bus message type into a
+ * machine-readable string (not translated).
+ *
+ * @code
+ *   DBUS_MESSAGE_TYPE_METHOD_CALL    -> "method_call"
+ *   DBUS_MESSAGE_TYPE_METHOD_RETURN  -> "method_return"
+ *   DBUS_MESSAGE_TYPE_SIGNAL         -> "signal"
+ *   DBUS_MESSAGE_TYPE_ERROR          -> "error"
+ *   DBUS_MESSAGE_TYPE_INVALID        -> "invalid"
+ * @endcode
+ *
+ */
+const char *
+dbus_message_type_to_string (int type)
+{
+  switch (type)
+    {
+    case DBUS_MESSAGE_TYPE_METHOD_CALL:
+      return "method_call";
+    case DBUS_MESSAGE_TYPE_METHOD_RETURN:
+      return "method_return";
+    case DBUS_MESSAGE_TYPE_SIGNAL:
+      return "signal";
+    case DBUS_MESSAGE_TYPE_ERROR:
+      return "error";
+    default:
+      return "invalid";
+    }
+}
+
+/**
+ * Turn a DBusMessage into the marshalled form as described in the D-Bus
+ * specification.
+ *
+ * Generally, this function is only useful for encapsulating D-Bus messages in
+ * a different protocol.
+ *
+ * @param msg the DBusMessage
+ * @param marshalled_data_p the location to save the marshalled form to
+ * @param len_p the location to save the length of the marshalled form to
+ * @returns #FALSE if there was not enough memory
+ */
+dbus_bool_t
+dbus_message_marshal (DBusMessage  *msg,
+                      char        **marshalled_data_p,
+                      int          *len_p)
+{
+  DBusString tmp;
+
+  _dbus_return_val_if_fail (msg != NULL, FALSE);
+  _dbus_return_val_if_fail (marshalled_data_p != NULL, FALSE);
+  _dbus_return_val_if_fail (len_p != NULL, FALSE);
+  
+  if (!_dbus_string_init (&tmp))
+    return FALSE;
+
+  if (!_dbus_string_copy (&(msg->header.data), 0, &tmp, 0))
+    goto fail;
+
+  *len_p = _dbus_string_get_length (&tmp);
+
+  if (!_dbus_string_copy (&(msg->body), 0, &tmp, *len_p))
+    goto fail;
+
+  *len_p = _dbus_string_get_length (&tmp);
+
+  if (!_dbus_string_steal_data (&tmp, marshalled_data_p))
+    goto fail;
+
+  _dbus_string_free (&tmp);
+  return TRUE;
+
+ fail:
+  _dbus_string_free (&tmp);
+  return FALSE;
+}
+
+/**
+ * Demarshal a D-Bus message from the format described in the D-Bus
+ * specification.
+ *
+ * Generally, this function is only useful for encapsulating D-Bus messages in
+ * a different protocol.
+ *
+ * @param str the marshalled DBusMessage
+ * @param len the length of str
+ * @param error the location to save errors to
+ * @returns #NULL if there was an error
+ */
+DBusMessage *
+dbus_message_demarshal (const char *str,
+                        int         len,
+                        DBusError  *error)
+{
+  DBusMessageLoader *loader;
+  DBusString *buffer;
+  DBusMessage *msg;
+
+  _dbus_return_val_if_fail (str != NULL, NULL);
+
+  loader = _dbus_message_loader_new ();
+
+  if (loader == NULL)
+    return NULL;
+
+  _dbus_message_loader_get_buffer (loader, &buffer);
+  _dbus_string_append_len (buffer, str, len);
+  _dbus_message_loader_return_buffer (loader, buffer, len);
+
+  if (!_dbus_message_loader_queue_messages (loader))
+    goto fail_oom;
+
+  if (_dbus_message_loader_get_is_corrupted (loader))
+    goto fail_corrupt;
+
+  msg = _dbus_message_loader_pop_message (loader);
+
+  if (!msg)
+    goto fail_oom;
+
+  _dbus_message_loader_unref (loader);
+  return msg;
+
+ fail_corrupt:
+  dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, "Message is corrupted");
+  _dbus_message_loader_unref (loader);
+  return NULL;
+
+ fail_oom:
+  _DBUS_SET_OOM (error);
+  _dbus_message_loader_unref (loader);
+  return NULL;
+}
+
+/**
+ * Returns the number of bytes required to be in the buffer to demarshal a
+ * D-Bus message.
+ *
+ * Generally, this function is only useful for encapsulating D-Bus messages in
+ * a different protocol.
+ *
+ * @param str data to be marshalled
+ * @param len the length of str
+ * @param error the location to save errors to
+ * @returns -1 if there was no valid data to be demarshalled, 0 if there wasn't enough data to determine how much should be demarshalled. Otherwise returns the number of bytes to be demarshalled
+ * 
+ */
+int 
+dbus_message_demarshal_bytes_needed(const char *buf, 
+                                    int         len)
+{
+  DBusString str;
+  int byte_order, fields_array_len, header_len, body_len;
+  DBusValidity validity = DBUS_VALID;
+  int have_message;
+
+  if (!buf || len < DBUS_MINIMUM_HEADER_SIZE)
+    return 0;
+
+  if (len > DBUS_MAXIMUM_MESSAGE_LENGTH)
+    len = DBUS_MAXIMUM_MESSAGE_LENGTH;
+  _dbus_string_init_const_len (&str, buf, len);
+  
+  validity = DBUS_VALID;
+  have_message
+    = _dbus_header_have_message_untrusted(DBUS_MAXIMUM_MESSAGE_LENGTH,
+                                          &validity, &byte_order,
+                                          &fields_array_len,
+                                          &header_len,
+                                          &body_len,
+                                          &str, 0,
+                                          len);
+  _dbus_string_free (&str);
+
+  if (validity == DBUS_VALID)
+    {
+      _dbus_assert(have_message);
+      return header_len + body_len;
+    }
+  else
+    {
+      return -1; /* broken! */
+    }
+}
+
+/** @} */
+
+/* tests in dbus-message-util.c */
diff --git a/src/dbus/dbus-message.h b/src/dbus/dbus-message.h
new file mode 100644 (file)
index 0000000..2e29fef
--- /dev/null
@@ -0,0 +1,233 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-message.h DBusMessage object
+ *
+ * Copyright (C) 2002, 2003, 2005 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_MESSAGE_H
+#define DBUS_MESSAGE_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-arch-deps.h>
+#include <dbus/dbus-memory.h>
+#include <dbus/dbus-errors.h>
+#include <stdarg.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusMessage
+ * @{
+ */
+
+typedef struct DBusMessage DBusMessage;
+/** Opaque type representing a message iterator. Can be copied by value, and contains no allocated memory so never needs to be freed and can be allocated on the stack. */
+typedef struct DBusMessageIter DBusMessageIter;
+
+/**
+ * DBusMessageIter struct; contains no public fields. 
+ */
+struct DBusMessageIter
+{ 
+  void *dummy1;         /**< Don't use this */
+  void *dummy2;         /**< Don't use this */
+  dbus_uint32_t dummy3; /**< Don't use this */
+  int dummy4;           /**< Don't use this */
+  int dummy5;           /**< Don't use this */
+  int dummy6;           /**< Don't use this */
+  int dummy7;           /**< Don't use this */
+  int dummy8;           /**< Don't use this */
+  int dummy9;           /**< Don't use this */
+  int dummy10;          /**< Don't use this */
+  int dummy11;          /**< Don't use this */
+  int pad1;             /**< Don't use this */
+  int pad2;             /**< Don't use this */
+  void *pad3;           /**< Don't use this */
+};
+
+DBusMessage* dbus_message_new               (int          message_type);
+DBusMessage* dbus_message_new_method_call   (const char  *bus_name,
+                                             const char  *path,
+                                             const char  *interface,
+                                             const char  *method);
+DBusMessage* dbus_message_new_method_return (DBusMessage *method_call);
+DBusMessage* dbus_message_new_signal        (const char  *path,
+                                             const char  *interface,
+                                             const char  *name);
+DBusMessage* dbus_message_new_error         (DBusMessage *reply_to,
+                                             const char  *error_name,
+                                             const char  *error_message);
+DBusMessage* dbus_message_new_error_printf  (DBusMessage *reply_to,
+                                             const char  *error_name,
+                                             const char  *error_format,
+                                            ...);
+
+DBusMessage* dbus_message_copy              (const DBusMessage *message);
+
+DBusMessage*  dbus_message_ref              (DBusMessage   *message);
+void          dbus_message_unref            (DBusMessage   *message);
+int           dbus_message_get_type         (DBusMessage   *message);
+dbus_bool_t   dbus_message_set_path         (DBusMessage   *message,
+                                             const char    *object_path);
+const char*   dbus_message_get_path         (DBusMessage   *message);
+dbus_bool_t   dbus_message_has_path         (DBusMessage   *message, 
+                                             const char    *object_path);  
+dbus_bool_t   dbus_message_set_interface    (DBusMessage   *message,
+                                             const char    *interface);       
+const char*   dbus_message_get_interface    (DBusMessage   *message);
+dbus_bool_t   dbus_message_has_interface    (DBusMessage   *message, 
+                                             const char    *interface);
+dbus_bool_t   dbus_message_set_member       (DBusMessage   *message,
+                                             const char    *member);
+const char*   dbus_message_get_member       (DBusMessage   *message);
+dbus_bool_t   dbus_message_has_member       (DBusMessage   *message, 
+                                             const char    *member);
+dbus_bool_t   dbus_message_set_error_name   (DBusMessage   *message,
+                                             const char    *name);
+const char*   dbus_message_get_error_name   (DBusMessage   *message);
+dbus_bool_t   dbus_message_set_destination  (DBusMessage   *message,
+                                             const char    *destination);
+const char*   dbus_message_get_destination  (DBusMessage   *message);
+dbus_bool_t   dbus_message_set_sender       (DBusMessage   *message,
+                                             const char    *sender);
+const char*   dbus_message_get_sender       (DBusMessage   *message);
+const char*   dbus_message_get_signature    (DBusMessage   *message);
+void          dbus_message_set_no_reply     (DBusMessage   *message,
+                                             dbus_bool_t    no_reply);
+dbus_bool_t   dbus_message_get_no_reply     (DBusMessage   *message);
+dbus_bool_t   dbus_message_is_method_call   (DBusMessage   *message,
+                                             const char    *interface,
+                                             const char    *method);
+dbus_bool_t   dbus_message_is_signal        (DBusMessage   *message,
+                                             const char    *interface,
+                                             const char    *signal_name);
+dbus_bool_t   dbus_message_is_error         (DBusMessage   *message,
+                                             const char    *error_name);
+dbus_bool_t   dbus_message_has_destination  (DBusMessage   *message,
+                                             const char    *bus_name);
+dbus_bool_t   dbus_message_has_sender       (DBusMessage   *message,
+                                             const char    *unique_bus_name);
+dbus_bool_t   dbus_message_has_signature    (DBusMessage   *message,
+                                             const char    *signature);
+dbus_uint32_t dbus_message_get_serial       (DBusMessage   *message);
+void          dbus_message_set_serial       (DBusMessage   *message, 
+                                             dbus_uint32_t  serial);
+dbus_bool_t   dbus_message_set_reply_serial (DBusMessage   *message,
+                                             dbus_uint32_t  reply_serial);
+dbus_uint32_t dbus_message_get_reply_serial (DBusMessage   *message);
+
+void          dbus_message_set_auto_start   (DBusMessage   *message,
+                                             dbus_bool_t    auto_start);
+dbus_bool_t   dbus_message_get_auto_start   (DBusMessage   *message);
+
+dbus_bool_t   dbus_message_get_path_decomposed (DBusMessage   *message,
+                                                char        ***path);
+
+dbus_bool_t dbus_message_append_args          (DBusMessage     *message,
+                                              int              first_arg_type,
+                                              ...);
+dbus_bool_t dbus_message_append_args_valist   (DBusMessage     *message,
+                                              int              first_arg_type,
+                                              va_list          var_args);
+dbus_bool_t dbus_message_get_args             (DBusMessage     *message,
+                                              DBusError       *error,
+                                              int              first_arg_type,
+                                              ...);
+dbus_bool_t dbus_message_get_args_valist      (DBusMessage     *message,
+                                              DBusError       *error,
+                                              int              first_arg_type,
+                                              va_list          var_args);
+
+
+dbus_bool_t dbus_message_iter_init             (DBusMessage     *message,
+                                                DBusMessageIter *iter);
+dbus_bool_t dbus_message_iter_has_next         (DBusMessageIter *iter);
+dbus_bool_t dbus_message_iter_next             (DBusMessageIter *iter);
+char*       dbus_message_iter_get_signature    (DBusMessageIter *iter);
+int         dbus_message_iter_get_arg_type     (DBusMessageIter *iter);
+int         dbus_message_iter_get_element_type (DBusMessageIter *iter);
+void        dbus_message_iter_recurse          (DBusMessageIter *iter,
+                                                DBusMessageIter *sub);
+void        dbus_message_iter_get_basic        (DBusMessageIter *iter,
+                                                void            *value);
+#ifndef DBUS_DISABLE_DEPRECATED
+/* This function returns the wire protocol size of the array in bytes,
+ * you do not want to know that probably
+ */
+DBUS_DEPRECATED int         dbus_message_iter_get_array_len    (DBusMessageIter *iter);
+#endif
+void        dbus_message_iter_get_fixed_array  (DBusMessageIter *iter,
+                                                void            *value,
+                                                int             *n_elements);
+
+
+void        dbus_message_iter_init_append        (DBusMessage     *message,
+                                                  DBusMessageIter *iter);
+dbus_bool_t dbus_message_iter_append_basic       (DBusMessageIter *iter,
+                                                  int              type,
+                                                  const void      *value);
+dbus_bool_t dbus_message_iter_append_fixed_array (DBusMessageIter *iter,
+                                                  int              element_type,
+                                                  const void      *value,
+                                                  int              n_elements);
+dbus_bool_t dbus_message_iter_open_container     (DBusMessageIter *iter,
+                                                  int              type,
+                                                  const char      *contained_signature,
+                                                  DBusMessageIter *sub);
+dbus_bool_t dbus_message_iter_close_container    (DBusMessageIter *iter,
+                                                  DBusMessageIter *sub);
+
+void dbus_message_lock    (DBusMessage  *message);
+
+dbus_bool_t  dbus_set_error_from_message  (DBusError    *error,
+                                           DBusMessage  *message);
+
+
+dbus_bool_t dbus_message_allocate_data_slot (dbus_int32_t     *slot_p);
+void        dbus_message_free_data_slot     (dbus_int32_t     *slot_p);
+dbus_bool_t dbus_message_set_data           (DBusMessage      *message,
+                                             dbus_int32_t      slot,
+                                             void             *data,
+                                             DBusFreeFunction  free_data_func);
+void*       dbus_message_get_data           (DBusMessage      *message,
+                                             dbus_int32_t      slot);
+
+int         dbus_message_type_from_string (const char *type_str);
+const char* dbus_message_type_to_string   (int type);
+
+dbus_bool_t  dbus_message_marshal   (DBusMessage  *msg,
+                                     char        **marshalled_data_p,
+                                     int          *len_p);
+DBusMessage* dbus_message_demarshal (const char *str,
+                                     int         len,
+                                     DBusError  *error);
+
+int          dbus_message_demarshal_bytes_needed (const char *str, 
+                                                  int len);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_MESSAGE_H */
diff --git a/src/dbus/dbus-misc.c b/src/dbus/dbus-misc.c
new file mode 100644 (file)
index 0000000..758e1a0
--- /dev/null
@@ -0,0 +1,248 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-misc.c  A few assorted public functions that don't fit elsewhere
+ *
+ * Copyright (C) 2006 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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 <config.h>
+#include "dbus-misc.h"
+#include "dbus-internals.h"
+#include "dbus-string.h"
+
+/**
+ * @defgroup DBusMisc Miscellaneous
+ * @ingroup  DBus
+ * @brief Miscellaneous API that doesn't cleanly fit anywhere else
+ *
+ * @{
+ */
+
+/**
+ * Obtains the machine UUID of the machine this process is running on.
+ *
+ * The returned string must be freed with dbus_free().
+ * 
+ * This UUID is guaranteed to remain the same until the next reboot
+ * (unless the sysadmin foolishly changes it and screws themselves).
+ * It will usually remain the same across reboots also, but hardware
+ * configuration changes or rebuilding the machine could break that.
+ *
+ * The idea is that two processes with the same machine ID should be
+ * able to use shared memory, UNIX domain sockets, process IDs, and other
+ * features of the OS that require both processes to be running
+ * on the same OS kernel instance.
+ *
+ * The machine ID can also be used to create unique per-machine
+ * instances. For example, you could use it in bus names or
+ * X selection names.
+ *
+ * The machine ID is preferred over the machine hostname, because
+ * the hostname is frequently set to "localhost.localdomain" and
+ * may also change at runtime.
+ *
+ * You can get the machine ID of a remote application by invoking the
+ * method GetMachineId from interface org.freedesktop.DBus.Peer.
+ *
+ * If the remote application has the same machine ID as the one
+ * returned by this function, then the remote application is on the
+ * same machine as your application.
+ *
+ * The UUID is not a UUID in the sense of RFC4122; the details
+ * are explained in the D-Bus specification.
+ *
+ * @returns a 32-byte-long hex-encoded UUID string, or #NULL if insufficient memory
+ */
+char*
+dbus_get_local_machine_id (void)
+{
+  DBusString uuid;
+  char *s;
+
+  s = NULL;
+
+  if (!_dbus_string_init (&uuid))
+    return NULL;
+
+  if (!_dbus_get_local_machine_uuid_encoded (&uuid) ||
+      !_dbus_string_steal_data (&uuid, &s))
+    {
+      _dbus_string_free (&uuid);
+      return NULL;
+    }
+  else
+    {
+      _dbus_string_free (&uuid);
+      return s;
+    }
+
+}
+
+/**
+ * @def DBUS_MAJOR_VERSION
+ *
+ * The COMPILE TIME major version of libdbus, that is, the "X" in "X.Y.Z",
+ * as an integer literal. Consider carefully whether to use this or the
+ * runtime version from dbus_get_version().
+ */
+
+/**
+ * @def DBUS_MINOR_VERSION
+ *
+ * The COMPILE TIME minor version of libdbus, that is, the "Y" in "X.Y.Z",
+ * as an integer literal. Consider carefully whether to use this or the
+ * runtime version from dbus_get_version().
+ */
+
+/**
+ * @def DBUS_MICRO_VERSION
+ *
+ * The COMPILE TIME micro version of libdbus, that is, the "Z" in "X.Y.Z",
+ * as an integer literal. Consider carefully whether to use this or the
+ * runtime version from dbus_get_version().
+ */
+
+/**
+ * @def DBUS_VERSION
+ *
+ * The COMPILE TIME version of libdbus, as a single integer that has 0 in the most
+ * significant byte, the major version in the next most significant byte,
+ * the minor version in the third most significant, and the micro version in the
+ * least significant byte. This means two DBUS_VERSION can be compared to see
+ * which is higher.
+ *
+ * Consider carefully whether to use this or the runtime version from
+ * dbus_get_version().
+ */
+
+/**
+ * @def DBUS_VERSION_STRING
+ *
+ * The COMPILE TIME version of libdbus, as a string "X.Y.Z".
+ *
+ * Consider carefully whether to use this or the runtime version from
+ * dbus_get_version().
+ */
+
+/**
+ * Gets the DYNAMICALLY LINKED version of libdbus. Alternatively, there
+ * are macros #DBUS_MAJOR_VERSION, #DBUS_MINOR_VERSION, #DBUS_MICRO_VERSION,
+ * and #DBUS_VERSION which allow you to test the VERSION YOU ARE COMPILED AGAINST.
+ * In other words, you can get either the runtime or the compile-time version.
+ * Think carefully about which of these you want in a given case.
+ *
+ * The libdbus full version number is "MAJOR.MINOR.MICRO" where the
+ * MINOR changes if API is added, and the MICRO changes with each
+ * release of a MAJOR.MINOR series.  The MINOR is an odd number for
+ * development releases and an even number for stable releases.
+ * 
+ * @param major_version_p pointer to return the major version, or #NULL
+ * @param minor_version_p pointer to return the minor version, or #NULL
+ * @param micro_version_p pointer to return the micro version, or #NULL 
+ * 
+ */
+void
+dbus_get_version (int *major_version_p,
+                  int *minor_version_p,
+                  int *micro_version_p)
+{
+  if (major_version_p)
+    *major_version_p = DBUS_MAJOR_VERSION;
+  if (minor_version_p)
+    *minor_version_p = DBUS_MINOR_VERSION;
+  if (micro_version_p)
+    *micro_version_p = DBUS_MICRO_VERSION;
+}
+
+
+/** @} */ /* End of public API */
+
+#ifdef DBUS_BUILD_TESTS
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+#include "dbus-test.h"
+#include <stdlib.h>
+
+
+dbus_bool_t
+_dbus_misc_test (void)
+{
+  int major, minor, micro;
+  DBusString str;
+
+  /* make sure we don't crash on NULL */
+  dbus_get_version (NULL, NULL, NULL);
+
+  /* Now verify that all the compile-time version stuff
+   * is right and matches the runtime. These tests
+   * are mostly intended to catch various kinds of
+   * typo (mixing up major and minor, that sort of thing).
+   */
+  dbus_get_version (&major, &minor, &micro);
+
+  _dbus_assert (major == DBUS_MAJOR_VERSION);
+  _dbus_assert (minor == DBUS_MINOR_VERSION);
+  _dbus_assert (micro == DBUS_MICRO_VERSION);
+
+#define MAKE_VERSION(x, y, z) (((x) << 16) | ((y) << 8) | (z))
+
+  /* check that MAKE_VERSION works and produces the intended ordering */
+  _dbus_assert (MAKE_VERSION (1, 0, 0) > MAKE_VERSION (0, 0, 0));
+  _dbus_assert (MAKE_VERSION (1, 1, 0) > MAKE_VERSION (1, 0, 0));
+  _dbus_assert (MAKE_VERSION (1, 1, 1) > MAKE_VERSION (1, 1, 0));
+
+  _dbus_assert (MAKE_VERSION (2, 0, 0) > MAKE_VERSION (1, 1, 1));
+  _dbus_assert (MAKE_VERSION (2, 1, 0) > MAKE_VERSION (1, 1, 1));
+  _dbus_assert (MAKE_VERSION (2, 1, 1) > MAKE_VERSION (1, 1, 1));
+
+  /* check DBUS_VERSION */
+  _dbus_assert (MAKE_VERSION (major, minor, micro) == DBUS_VERSION);
+
+  /* check that ordering works with DBUS_VERSION */
+  _dbus_assert (MAKE_VERSION (major - 1, minor, micro) < DBUS_VERSION);
+  _dbus_assert (MAKE_VERSION (major, minor - 1, micro) < DBUS_VERSION);
+  _dbus_assert (MAKE_VERSION (major, minor, micro - 1) < DBUS_VERSION);
+  
+  _dbus_assert (MAKE_VERSION (major + 1, minor, micro) > DBUS_VERSION);
+  _dbus_assert (MAKE_VERSION (major, minor + 1, micro) > DBUS_VERSION);
+  _dbus_assert (MAKE_VERSION (major, minor, micro + 1) > DBUS_VERSION);
+
+  /* Check DBUS_VERSION_STRING */
+
+  if (!_dbus_string_init (&str))
+    _dbus_assert_not_reached ("no memory");
+
+  if (!(_dbus_string_append_int (&str, major) &&
+        _dbus_string_append_byte (&str, '.') &&
+        _dbus_string_append_int (&str, minor) &&
+        _dbus_string_append_byte (&str, '.') &&
+        _dbus_string_append_int (&str, micro)))
+    _dbus_assert_not_reached ("no memory");
+
+  _dbus_assert (_dbus_string_equal_c_str (&str, DBUS_VERSION_STRING));
+
+  _dbus_string_free (&str);
+   
+  return TRUE;
+}
+
+#endif /* !DOXYGEN_SHOULD_SKIP_THIS */
+
+#endif
diff --git a/src/dbus/dbus-misc.h b/src/dbus/dbus-misc.h
new file mode 100644 (file)
index 0000000..c59ce70
--- /dev/null
@@ -0,0 +1,51 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-misc.h  A few assorted public functions that don't fit elsewhere
+ *
+ * Copyright (C) 2006 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_MISC_H
+#define DBUS_MISC_H
+
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-errors.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusMisc
+ * @{
+ */
+
+char*       dbus_get_local_machine_id  (void);
+
+void        dbus_get_version           (int *major_version_p,
+                                        int *minor_version_p,
+                                        int *micro_version_p);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_MISC_H */
+
diff --git a/src/dbus/dbus-object-tree.c b/src/dbus/dbus-object-tree.c
new file mode 100644 (file)
index 0000000..953aa3b
--- /dev/null
@@ -0,0 +1,1949 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-object-tree.c  DBusObjectTree (internals of DBusConnection)
+ *
+ * Copyright (C) 2003, 2005  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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 "dbus-string.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 library-internal.
+ *
+ * @{
+ */
+
+/** Subnode of the object hierarchy */
+typedef struct DBusObjectSubtree DBusObjectSubtree;
+
+static DBusObjectSubtree* _dbus_object_subtree_new   (const char                  *name,
+                                                      const DBusObjectPathVTable  *vtable,
+                                                      void                        *user_data);
+static DBusObjectSubtree* _dbus_object_subtree_ref   (DBusObjectSubtree           *subtree);
+static void               _dbus_object_subtree_unref (DBusObjectSubtree           *subtree);
+
+/**
+ * Internals of DBusObjectTree
+ */
+struct DBusObjectTree
+{
+  int                 refcount;   /**< Reference count */
+  DBusConnection     *connection; /**< Connection this tree belongs to */
+
+  DBusObjectSubtree  *root;       /**< Root of the tree ("/" node) */
+};
+
+/**
+ * Struct representing a single registered subtree handler, or node
+ * that's a parent of a registered subtree handler. If
+ * message_function != NULL there's actually a handler at this node.
+ */
+struct DBusObjectSubtree
+{
+  DBusAtomic                         refcount;            /**< Reference count */
+  DBusObjectSubtree                 *parent;              /**< Parent node */
+  DBusObjectPathUnregisterFunction   unregister_function; /**< Function to call on unregister */
+  DBusObjectPathMessageFunction      message_function;    /**< Function to handle messages */
+  void                              *user_data;           /**< Data for functions */
+  DBusObjectSubtree                **subtrees;            /**< Child nodes */
+  int                                n_subtrees;          /**< Number of child nodes */
+  int                                max_subtrees;        /**< Number of allocated entries in subtrees */
+  unsigned int                       invoke_as_fallback : 1; /**< Whether to invoke message_function when child nodes don't handle the message */
+  char                               name[1]; /**< Allocated as large as necessary */
+};
+
+/**
+ * Creates a new object tree, representing a mapping from paths
+ * to handler vtables.
+ *
+ * @param connection the connection this tree belongs to
+ * @returns the new tree or #NULL if no memory
+ */
+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;
+  tree->root = _dbus_object_subtree_new ("/", NULL, NULL);
+  if (tree->root == NULL)
+    goto oom;
+  tree->root->invoke_as_fallback = TRUE;
+  
+  return tree;
+
+ oom:
+  if (tree)
+    {
+      dbus_free (tree);
+    }
+
+  return NULL;
+}
+
+/**
+ * Increment the reference count
+ * @param tree the object tree
+ * @returns the object tree
+ */
+DBusObjectTree *
+_dbus_object_tree_ref (DBusObjectTree *tree)
+{
+  _dbus_assert (tree->refcount > 0);
+
+  tree->refcount += 1;
+
+  return tree;
+}
+
+/**
+ * Decrement the reference count
+ * @param tree the object tree
+ */
+void
+_dbus_object_tree_unref (DBusObjectTree *tree)
+{
+  _dbus_assert (tree->refcount > 0);
+
+  tree->refcount -= 1;
+
+  if (tree->refcount == 0)
+    {
+      _dbus_object_tree_free_all_unlocked (tree);
+
+      dbus_free (tree);
+    }
+}
+
+/** Set to 1 to get a bunch of debug spew about finding the
+ * subtree nodes
+ */
+#define VERBOSE_FIND 0
+
+static DBusObjectSubtree*
+find_subtree_recurse (DBusObjectSubtree  *subtree,
+                      const char        **path,
+                      dbus_bool_t         create_if_not_found,
+                      int                *index_in_parent,
+                      dbus_bool_t        *exact_match)
+{
+  int i, j;
+  dbus_bool_t return_deepest_match;
+
+  return_deepest_match = exact_match != NULL;
+
+  _dbus_assert (!(return_deepest_match && create_if_not_found));
+
+  if (path[0] == NULL)
+    {
+#if VERBOSE_FIND
+      _dbus_verbose ("  path exhausted, returning %s\n",
+                     subtree->name);
+#endif
+      if (exact_match != NULL)
+       *exact_match = TRUE;
+      return subtree;
+    }
+
+#if VERBOSE_FIND
+  _dbus_verbose ("  searching children of %s for %s\n",
+                 subtree->name, path[0]);
+#endif
+  
+  i = 0;
+  j = subtree->n_subtrees;
+  while (i < j)
+    {
+      int k, v;
+
+      k = (i + j) / 2;
+      v = strcmp (path[0], subtree->subtrees[k]->name);
+
+#if VERBOSE_FIND
+      _dbus_verbose ("  %s cmp %s = %d\n",
+                     path[0], subtree->subtrees[k]->name,
+                     v);
+#endif
+      
+      if (v == 0)
+        {
+          if (index_in_parent)
+            {
+#if VERBOSE_FIND
+              _dbus_verbose ("  storing parent index %d\n", k);
+#endif
+              *index_in_parent = k;
+            }
+
+          if (return_deepest_match)
+            {
+              DBusObjectSubtree *next;
+
+              next = find_subtree_recurse (subtree->subtrees[k],
+                                           &path[1], create_if_not_found, 
+                                           index_in_parent, exact_match);
+              if (next == NULL &&
+                  subtree->invoke_as_fallback)
+                {
+#if VERBOSE_FIND
+                  _dbus_verbose ("  no deeper match found, returning %s\n",
+                                 subtree->name);
+#endif
+                 if (exact_match != NULL)
+                   *exact_match = FALSE;
+                  return subtree;
+                }
+              else
+                return next;
+            }
+          else
+            return find_subtree_recurse (subtree->subtrees[k],
+                                         &path[1], create_if_not_found, 
+                                         index_in_parent, exact_match);
+        }
+      else if (v < 0)
+        {
+          j = k;
+        }
+      else
+        {
+          i = k + 1;
+        }
+    }
+
+#if VERBOSE_FIND
+  _dbus_verbose ("  no match found, current tree %s, create_if_not_found = %d\n",
+                 subtree->name, create_if_not_found);
+#endif
+  
+  if (create_if_not_found)
+    {
+      DBusObjectSubtree* child;
+      int child_pos, new_n_subtrees;
+
+#if VERBOSE_FIND
+      _dbus_verbose ("  creating subtree %s\n",
+                     path[0]);
+#endif
+      
+      child = _dbus_object_subtree_new (path[0],
+                                        NULL, NULL);
+      if (child == NULL)
+        return NULL;
+
+      new_n_subtrees = subtree->n_subtrees + 1;
+      if (new_n_subtrees > subtree->max_subtrees)
+        {
+          int new_max_subtrees;
+          DBusObjectSubtree **new_subtrees;
+
+          new_max_subtrees = subtree->max_subtrees == 0 ? 1 : 2 * subtree->max_subtrees;
+          new_subtrees = dbus_realloc (subtree->subtrees,
+                                       new_max_subtrees * sizeof (DBusObjectSubtree*));
+          if (new_subtrees == NULL)
+            {
+              _dbus_object_subtree_unref (child);
+              return NULL;
+            }
+          subtree->subtrees = new_subtrees;
+          subtree->max_subtrees = new_max_subtrees;
+        }
+
+      /* The binary search failed, so i == j points to the 
+         place the child should be inserted. */
+      child_pos = i;
+      _dbus_assert (child_pos < new_n_subtrees &&
+                    new_n_subtrees <= subtree->max_subtrees);
+      if (child_pos + 1 < new_n_subtrees)
+       {
+         memmove (&subtree->subtrees[child_pos+1], 
+                  &subtree->subtrees[child_pos], 
+                  (new_n_subtrees - child_pos - 1) * 
+                  sizeof subtree->subtrees[0]);
+       }
+      subtree->subtrees[child_pos] = child;
+
+      if (index_in_parent)
+        *index_in_parent = child_pos;
+      subtree->n_subtrees = new_n_subtrees;
+      child->parent = subtree;
+
+      return find_subtree_recurse (child,
+                                   &path[1], create_if_not_found, 
+                                   index_in_parent, exact_match);
+    }
+  else
+    {
+      if (exact_match != NULL)
+       *exact_match = FALSE;
+      return (return_deepest_match && subtree->invoke_as_fallback) ? subtree : NULL;
+    }
+}
+
+static DBusObjectSubtree*
+find_subtree (DBusObjectTree *tree,
+              const char    **path,
+              int            *index_in_parent)
+{
+  DBusObjectSubtree *subtree;
+
+#if VERBOSE_FIND
+  _dbus_verbose ("Looking for exact registered subtree\n");
+#endif
+  
+  subtree = find_subtree_recurse (tree->root, path, FALSE, index_in_parent, NULL);
+
+  if (subtree && subtree->message_function == NULL)
+    return NULL;
+  else
+    return subtree;
+}
+
+static DBusObjectSubtree*
+lookup_subtree (DBusObjectTree *tree,
+                const char    **path)
+{
+#if VERBOSE_FIND
+  _dbus_verbose ("Looking for subtree\n");
+#endif
+  return find_subtree_recurse (tree->root, path, FALSE, NULL, NULL);
+}
+
+static DBusObjectSubtree*
+find_handler (DBusObjectTree *tree,
+              const char    **path,
+              dbus_bool_t    *exact_match)
+{
+#if VERBOSE_FIND
+  _dbus_verbose ("Looking for deepest handler\n");
+#endif
+  _dbus_assert (exact_match != NULL);
+
+  *exact_match = FALSE; /* ensure always initialized */
+  
+  return find_subtree_recurse (tree->root, path, FALSE, NULL, exact_match);
+}
+
+static DBusObjectSubtree*
+ensure_subtree (DBusObjectTree *tree,
+                const char    **path)
+{
+#if VERBOSE_FIND
+  _dbus_verbose ("Ensuring subtree\n");
+#endif
+  return find_subtree_recurse (tree->root, path, TRUE, NULL, NULL);
+}
+
+static char *flatten_path (const char **path);
+
+/**
+ * Registers a new subtree in the global object tree.
+ *
+ * @param tree the global object tree
+ * @param fallback #TRUE to handle messages to children of this path
+ * @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
+ * @param error address where an error can be returned
+ * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
+ *    #DBUS_ERROR_OBJECT_PATH_IN_USE) is reported
+ */
+dbus_bool_t
+_dbus_object_tree_register (DBusObjectTree              *tree,
+                            dbus_bool_t                  fallback,
+                            const char                 **path,
+                            const DBusObjectPathVTable  *vtable,
+                            void                        *user_data,
+                            DBusError                   *error)
+{
+  DBusObjectSubtree  *subtree;
+
+  _dbus_assert (tree != NULL);
+  _dbus_assert (vtable->message_function != NULL);
+  _dbus_assert (path != NULL);
+
+  subtree = ensure_subtree (tree, path);
+  if (subtree == NULL)
+    {
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+
+  if (subtree->message_function != NULL)
+    {
+      if (error != NULL)
+        {
+          char *complete_path = flatten_path (path);
+
+          dbus_set_error (error, DBUS_ERROR_OBJECT_PATH_IN_USE,
+                          "A handler is already registered for %s",
+                          complete_path ? complete_path
+                                        : "(cannot represent path: out of memory!)");
+
+          dbus_free (complete_path);
+        }
+
+      return FALSE;
+    }
+
+  subtree->message_function = vtable->message_function;
+  subtree->unregister_function = vtable->unregister_function;
+  subtree->user_data = user_data;
+  subtree->invoke_as_fallback = fallback != FALSE;
+
+  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;
+  DBusObjectPathUnregisterFunction unregister_function;
+  void *user_data;
+  DBusConnection *connection;
+
+  _dbus_assert (path != NULL);
+
+  unregister_function = NULL;
+  user_data = NULL;
+
+  subtree = find_subtree (tree, path, &i);
+
+#ifndef DBUS_DISABLE_CHECKS
+  if (subtree == NULL)
+    {
+      _dbus_warn ("Attempted to unregister path (path[0] = %s path[1] = %s) which isn't registered\n",
+                  path[0] ? path[0] : "null",
+                  path[1] ? path[1] : "null");
+      goto unlock;    
+    }
+#else
+  _dbus_assert (subtree != NULL);
+#endif
+
+  _dbus_assert (subtree->parent == NULL ||
+                (i >= 0 && subtree->parent->subtrees[i] == subtree));
+
+  subtree->message_function = NULL;
+
+  unregister_function = subtree->unregister_function;
+  user_data = subtree->user_data;
+
+  subtree->unregister_function = NULL;
+  subtree->user_data = NULL;
+
+  /* If we have no subtrees of our own, remove from
+   * our parent (FIXME could also be more aggressive
+   * and remove our parent if it becomes empty)
+   */
+  if (subtree->parent && subtree->n_subtrees == 0)
+    {
+      /* assumes a 0-byte memmove is OK */
+      memmove (&subtree->parent->subtrees[i],
+               &subtree->parent->subtrees[i+1],
+               (subtree->parent->n_subtrees - i - 1) *
+               sizeof (subtree->parent->subtrees[0]));
+      subtree->parent->n_subtrees -= 1;
+
+      subtree->parent = NULL;
+
+      _dbus_object_subtree_unref (subtree);
+    }
+  subtree = NULL;
+
+unlock:
+  connection = tree->connection;
+
+  /* Unlock and call application code */
+#ifdef DBUS_BUILD_TESTS
+  if (connection)
+#endif
+    {
+      _dbus_connection_ref_unlocked (connection);
+      _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME);
+      _dbus_connection_unlock (connection);
+    }
+
+  if (unregister_function)
+    (* unregister_function) (connection, user_data);
+
+#ifdef DBUS_BUILD_TESTS
+  if (connection)
+#endif
+    dbus_connection_unref (connection);
+}
+
+static void
+free_subtree_recurse (DBusConnection    *connection,
+                      DBusObjectSubtree *subtree)
+{
+  /* Delete them from the end, for slightly
+   * more robustness against odd reentrancy.
+   */
+  while (subtree->n_subtrees > 0)
+    {
+      DBusObjectSubtree *child;
+
+      child = subtree->subtrees[subtree->n_subtrees - 1];
+      subtree->subtrees[subtree->n_subtrees - 1] = NULL;
+      subtree->n_subtrees -= 1;
+      child->parent = NULL;
+
+      free_subtree_recurse (connection, child);
+    }
+
+  /* Call application code */
+  if (subtree->unregister_function)
+    (* subtree->unregister_function) (connection,
+                                     subtree->user_data);
+
+  subtree->message_function = NULL;
+  subtree->unregister_function = NULL;
+  subtree->user_data = NULL;
+
+  /* Now free ourselves */
+  _dbus_object_subtree_unref (subtree);
+}
+
+/**
+ * Free all the handlers in the tree. Lock on tree's connection
+ * must not be held.
+ *
+ * @param tree the object tree
+ */
+void
+_dbus_object_tree_free_all_unlocked (DBusObjectTree *tree)
+{
+  if (tree->root)
+    free_subtree_recurse (tree->connection,
+                          tree->root);
+  tree->root = NULL;
+}
+
+static dbus_bool_t
+_dbus_object_tree_list_registered_unlocked (DBusObjectTree *tree,
+                                            const char    **parent_path,
+                                            char         ***child_entries)
+{
+  DBusObjectSubtree *subtree;
+  char **retval;
+  
+  _dbus_assert (parent_path != NULL);
+  _dbus_assert (child_entries != NULL);
+
+  *child_entries = NULL;
+  
+  subtree = lookup_subtree (tree, parent_path);
+  if (subtree == NULL)
+    {
+      retval = dbus_new0 (char *, 1);
+    }
+  else
+    {
+      int i;
+      retval = dbus_new0 (char*, subtree->n_subtrees + 1);
+      if (retval == NULL)
+        goto out;
+      i = 0;
+      while (i < subtree->n_subtrees)
+        {
+          retval[i] = _dbus_strdup (subtree->subtrees[i]->name);
+          if (retval[i] == NULL)
+            {
+              dbus_free_string_array (retval);
+              retval = NULL;
+              goto out;
+            }
+          ++i;
+        }
+    }
+
+ out:
+    
+  *child_entries = retval;
+  return retval != NULL;
+}
+
+static DBusHandlerResult
+handle_default_introspect_and_unlock (DBusObjectTree          *tree,
+                                      DBusMessage             *message,
+                                      const char             **path)
+{
+  DBusString xml;
+  DBusHandlerResult result;
+  char **children;
+  int i;
+  DBusMessage *reply;
+  DBusMessageIter iter;
+  const char *v_STRING;
+  dbus_bool_t already_unlocked;
+
+  /* We have the connection lock here */
+
+  already_unlocked = FALSE;
+  
+  _dbus_verbose (" considering default Introspect() handler...\n");
+
+  reply = NULL;
+  
+  if (!dbus_message_is_method_call (message,
+                                    DBUS_INTERFACE_INTROSPECTABLE,
+                                    "Introspect"))
+    {
+#ifdef DBUS_BUILD_TESTS
+      if (tree->connection)
+#endif
+        {
+          _dbus_verbose ("unlock %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+          _dbus_connection_unlock (tree->connection);
+        }
+      
+      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+
+  _dbus_verbose (" using default Introspect() handler!\n");
+  
+  if (!_dbus_string_init (&xml))
+    {
+#ifdef DBUS_BUILD_TESTS
+      if (tree->connection)
+#endif
+        {
+          _dbus_verbose ("unlock %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+          _dbus_connection_unlock (tree->connection);
+        }
+
+      return DBUS_HANDLER_RESULT_NEED_MEMORY;
+    }
+
+  result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+  children = NULL;
+  if (!_dbus_object_tree_list_registered_unlocked (tree, path, &children))
+    goto out;
+
+  if (!_dbus_string_append (&xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE))
+    goto out;
+  
+  if (!_dbus_string_append (&xml, "<node>\n"))
+    goto out;
+
+  i = 0;
+  while (children[i] != NULL)
+    {
+      if (!_dbus_string_append_printf (&xml, "  <node name=\"%s\"/>\n",
+                                       children[i]))
+        goto out;
+
+      ++i;
+    }
+
+  if (!_dbus_string_append (&xml, "</node>\n"))
+    goto out;
+
+  reply = dbus_message_new_method_return (message);
+  if (reply == NULL)
+    goto out;
+
+  dbus_message_iter_init_append (reply, &iter);
+  v_STRING = _dbus_string_get_const_data (&xml);
+  if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &v_STRING))
+    goto out;
+  
+#ifdef DBUS_BUILD_TESTS
+  if (tree->connection)
+#endif
+    {
+      already_unlocked = TRUE;
+      
+      if (!_dbus_connection_send_and_unlock (tree->connection, reply, NULL))
+        goto out;
+    }
+  
+  result = DBUS_HANDLER_RESULT_HANDLED;
+  
+ out:
+#ifdef DBUS_BUILD_TESTS
+  if (tree->connection)
+#endif
+    {
+      if (!already_unlocked)
+        {
+          _dbus_verbose ("unlock %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+          _dbus_connection_unlock (tree->connection);
+        }
+    }
+  
+  _dbus_string_free (&xml);
+  dbus_free_string_array (children);
+  if (reply)
+    dbus_message_unref (reply);
+  
+  return result;
+}
+
+/**
+ * Tries to dispatch a message by directing it to handler for the
+ * object path listed in the message header, if any. Messages are
+ * dispatched first to the registered handler that matches the largest
+ * number of path elements; that is, message to /foo/bar/baz would go
+ * to the handler for /foo/bar before the one for /foo.
+ *
+ * @todo thread problems
+ *
+ * @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)
+{
+  char **path;
+  dbus_bool_t exact_match;
+  DBusList *list;
+  DBusList *link;
+  DBusHandlerResult result;
+  DBusObjectSubtree *subtree;
+  
+#if 0
+  _dbus_verbose ("Dispatch of message by object path\n");
+#endif
+  
+  path = NULL;
+  if (!dbus_message_get_path_decomposed (message, &path))
+    {
+#ifdef DBUS_BUILD_TESTS
+      if (tree->connection)
+#endif
+        {
+          _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME);
+          _dbus_connection_unlock (tree->connection);
+        }
+      
+      _dbus_verbose ("No memory to get decomposed path\n");
+
+      return DBUS_HANDLER_RESULT_NEED_MEMORY;
+    }
+
+  if (path == NULL)
+    {
+#ifdef DBUS_BUILD_TESTS
+      if (tree->connection)
+#endif
+        {
+          _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME);
+          _dbus_connection_unlock (tree->connection);
+        }
+      
+      _dbus_verbose ("No path field in message\n");
+      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+  
+  /* Find the deepest path that covers the path in the message */
+  subtree = find_handler (tree, (const char**) path, &exact_match);
+  
+  /* Build a list of all paths that cover the path in the message */
+
+  list = NULL;
+
+  while (subtree != NULL)
+    {
+      if (subtree->message_function != NULL && (exact_match || subtree->invoke_as_fallback))
+        {
+          _dbus_object_subtree_ref (subtree);
+
+          /* run deepest paths first */
+          if (!_dbus_list_append (&list, subtree))
+            {
+              result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+              _dbus_object_subtree_unref (subtree);
+              goto free_and_return;
+            }
+        }
+
+      exact_match = FALSE;
+      subtree = subtree->parent;
+    }
+
+  _dbus_verbose ("%d handlers in the path tree for this message\n",
+                 _dbus_list_get_length (&list));
+
+  /* Invoke each handler in the list */
+
+  result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+  link = _dbus_list_get_first_link (&list);
+  while (link != NULL)
+    {
+      DBusList *next = _dbus_list_get_next_link (&list, link);
+      subtree = link->data;
+
+      /* message_function is NULL if we're unregistered
+       * due to reentrancy
+       */
+      if (subtree->message_function)
+        {
+          DBusObjectPathMessageFunction message_function;
+          void *user_data;
+
+          message_function = subtree->message_function;
+          user_data = subtree->user_data;
+
+#if 0
+          _dbus_verbose ("  (invoking a handler)\n");
+#endif
+          
+#ifdef DBUS_BUILD_TESTS
+          if (tree->connection)
+#endif
+            {
+              _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME);
+              _dbus_connection_unlock (tree->connection);
+            }
+
+          /* FIXME you could unregister the subtree in another thread
+           * before we invoke the callback, and I can't figure out a
+           * good way to solve this.
+           */
+
+          result = (* message_function) (tree->connection,
+                                         message,
+                                         user_data);
+
+#ifdef DBUS_BUILD_TESTS
+          if (tree->connection)
+#endif
+            _dbus_connection_lock (tree->connection);
+
+          if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
+            goto free_and_return;
+        }
+
+      link = next;
+    }
+
+ free_and_return:
+
+  if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
+    {
+      /* This hardcoded default handler does a minimal Introspect()
+       */
+      result = handle_default_introspect_and_unlock (tree, message,
+                                                     (const char**) path);
+    }
+  else
+    {
+#ifdef DBUS_BUILD_TESTS
+      if (tree->connection)
+#endif
+        {
+          _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME);
+          _dbus_connection_unlock (tree->connection);
+        }
+    }
+  
+  while (list != NULL)
+    {
+      link = _dbus_list_get_first_link (&list);
+      _dbus_object_subtree_unref (link->data);
+      _dbus_list_remove_link (&list, link);
+    }
+  
+  dbus_free_string_array (path);
+
+  return result;
+}
+
+/**
+ * Looks up the data passed to _dbus_object_tree_register() for a
+ * handler at the given path.
+ *
+ * @param tree the global object tree
+ * @param path NULL-terminated array of path elements giving path to subtree
+ * @returns the object's user_data or #NULL if none found
+ */
+void*
+_dbus_object_tree_get_user_data_unlocked (DBusObjectTree *tree,
+                                          const char    **path)
+{
+  dbus_bool_t exact_match;
+  DBusObjectSubtree *subtree;
+
+  _dbus_assert (tree != NULL);
+  _dbus_assert (path != NULL);
+  
+  /* Find the deepest path that covers the path in the message */
+  subtree = find_handler (tree, (const char**) path, &exact_match);
+
+  if ((subtree == NULL) || !exact_match)
+    {
+      _dbus_verbose ("%s: No object at specified path found\n",
+                     _DBUS_FUNCTION_NAME);
+      return NULL;
+    }
+
+  return subtree->user_data;
+}
+
+/**
+ * Allocates a subtree object.
+ *
+ * @param name name to duplicate.
+ * @returns newly-allocated subtree
+ */
+static DBusObjectSubtree*
+allocate_subtree_object (const char *name)
+{
+  int len;
+  DBusObjectSubtree *subtree;
+  const size_t front_padding = _DBUS_STRUCT_OFFSET (DBusObjectSubtree, name);
+
+  _dbus_assert (name != NULL);
+
+  len = strlen (name);
+
+  subtree = dbus_malloc (MAX (front_padding + (len + 1), sizeof (DBusObjectSubtree)));
+
+  if (subtree == NULL)
+    return NULL;
+
+  memcpy (subtree->name, name, len + 1);
+
+  return subtree;
+}
+
+static DBusObjectSubtree*
+_dbus_object_subtree_new (const char                  *name,
+                          const DBusObjectPathVTable  *vtable,
+                          void                        *user_data)
+{
+  DBusObjectSubtree *subtree;
+
+  subtree = allocate_subtree_object (name);
+  if (subtree == NULL)
+    goto oom;
+
+  _dbus_assert (name != NULL);
+
+  subtree->parent = NULL;
+
+  if (vtable)
+    {
+      subtree->message_function = vtable->message_function;
+      subtree->unregister_function = vtable->unregister_function;
+    }
+  else
+    {
+      subtree->message_function = NULL;
+      subtree->unregister_function = NULL;
+    }
+
+  subtree->user_data = user_data;
+  subtree->refcount.value = 1;
+  subtree->subtrees = NULL;
+  subtree->n_subtrees = 0;
+  subtree->max_subtrees = 0;
+  subtree->invoke_as_fallback = FALSE;
+
+  return subtree;
+
+ oom:
+  return NULL;
+}
+
+static DBusObjectSubtree *
+_dbus_object_subtree_ref (DBusObjectSubtree *subtree)
+{
+  _dbus_assert (subtree->refcount.value > 0);
+  _dbus_atomic_inc (&subtree->refcount);
+
+  return subtree;
+}
+
+static void
+_dbus_object_subtree_unref (DBusObjectSubtree *subtree)
+{
+  _dbus_assert (subtree->refcount.value > 0);
+
+  if (_dbus_atomic_dec (&subtree->refcount) == 1)
+    {
+      _dbus_assert (subtree->unregister_function == NULL);
+      _dbus_assert (subtree->message_function == NULL);
+
+      dbus_free (subtree->subtrees);
+      dbus_free (subtree);
+    }
+}
+
+/**
+ * Lists the registered fallback handlers and object path handlers at
+ * the given parent_path. The returned array should be freed with
+ * dbus_free_string_array().
+ *
+ * @param tree the object tree
+ * @param parent_path the path to list the child handlers of
+ * @param child_entries returns #NULL-terminated array of children
+ * @returns #FALSE if no memory to allocate the child entries
+ */
+dbus_bool_t
+_dbus_object_tree_list_registered_and_unlock (DBusObjectTree *tree,
+                                              const char    **parent_path,
+                                              char         ***child_entries)
+{
+  dbus_bool_t result;
+
+  result = _dbus_object_tree_list_registered_unlocked (tree,
+                                                       parent_path,
+                                                       child_entries);
+  
+#ifdef DBUS_BUILD_TESTS
+  if (tree->connection)
+#endif
+    {
+      _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME);
+      _dbus_connection_unlock (tree->connection);
+    }
+
+  return result;
+}
+
+
+/** Set to 1 to get a bunch of spew about disassembling the path string */
+#define VERBOSE_DECOMPOSE 0
+
+/**
+ * Decompose an object path.  A path of just "/" is
+ * represented as an empty vector of strings.
+ * The path need not be nul terminated.
+ * 
+ * @param data the path data
+ * @param len  the length of the path string
+ * @param path address to store new object path
+ * @param path_len length of stored path
+ */
+dbus_bool_t
+_dbus_decompose_path (const char*     data,
+                      int             len,
+                      char         ***path,
+                      int            *path_len)
+{
+  char **retval;
+  int n_components;
+  int i, j, comp;
+
+  _dbus_assert (data != NULL);
+  
+#if VERBOSE_DECOMPOSE
+  _dbus_verbose ("Decomposing path \"%s\"\n",
+                 data);
+#endif
+  
+  n_components = 0;
+  if (len > 1) /* if path is not just "/" */
+    {
+      i = 0;
+      while (i < len)
+        {
+          if (data[i] == '/')
+            n_components += 1;
+          ++i;
+        }
+    }
+  
+  retval = dbus_new0 (char*, n_components + 1);
+
+  if (retval == NULL)
+    return FALSE;
+
+  comp = 0;
+  if (n_components == 0)
+    i = 1;
+  else
+    i = 0;
+  while (comp < n_components)
+    {
+      _dbus_assert (i < len);
+      
+      if (data[i] == '/')
+        ++i;
+      j = i;
+
+      while (j < len && data[j] != '/')
+        ++j;
+
+      /* Now [i, j) is the path component */
+      _dbus_assert (i < j);
+      _dbus_assert (data[i] != '/');
+      _dbus_assert (j == len || data[j] == '/');
+
+#if VERBOSE_DECOMPOSE
+      _dbus_verbose ("  (component in [%d,%d))\n",
+                     i, j);
+#endif
+      
+      retval[comp] = _dbus_memdup (&data[i], j - i + 1);
+      if (retval[comp] == NULL)
+        {
+          dbus_free_string_array (retval);
+          return FALSE;
+        }
+      retval[comp][j-i] = '\0';
+#if VERBOSE_DECOMPOSE
+      _dbus_verbose ("  (component %d = \"%s\")\n",
+                     comp, retval[comp]);
+#endif
+
+      ++comp;
+      i = j;
+    }
+  _dbus_assert (i == len);
+  
+  *path = retval;
+  if (path_len)
+    *path_len = n_components;
+  
+  return TRUE;
+}
+
+/** @} */
+
+static char*
+flatten_path (const char **path)
+{
+  DBusString str;
+  char *s;
+
+  if (!_dbus_string_init (&str))
+    return NULL;
+
+  if (path[0] == NULL)
+    {
+      if (!_dbus_string_append_byte (&str, '/'))
+        goto nomem;
+    }
+  else
+    {
+      int i;
+      
+      i = 0;
+      while (path[i])
+        {
+          if (!_dbus_string_append_byte (&str, '/'))
+            goto nomem;
+          
+          if (!_dbus_string_append (&str, path[i]))
+            goto nomem;
+          
+          ++i;
+        }
+    }
+
+  if (!_dbus_string_steal_data (&str, &s))
+    goto nomem;
+
+  _dbus_string_free (&str);
+
+  return s;
+
+ nomem:
+  _dbus_string_free (&str);
+  return NULL;
+}
+
+
+#ifdef DBUS_BUILD_TESTS
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+#include "dbus-test.h"
+#include <stdio.h>
+
+typedef enum 
+{
+  STR_EQUAL,
+  STR_PREFIX,
+  STR_DIFFERENT
+} StrComparison;
+
+/* Returns TRUE if container is a parent of child
+ */
+static StrComparison
+path_contains (const char **container,
+               const char **child)
+{
+  int i;
+
+  i = 0;
+  while (child[i] != NULL)
+    {
+      int v;
+
+      if (container[i] == NULL)
+        return STR_PREFIX; /* container ran out, child continues;
+                            * thus the container is a parent of the
+                            * child.
+                            */
+
+      _dbus_assert (container[i] != NULL);
+      _dbus_assert (child[i] != NULL);
+
+      v = strcmp (container[i], child[i]);
+
+      if (v != 0)
+        return STR_DIFFERENT; /* they overlap until here and then are different,
+                               * not overlapping
+                               */
+
+      ++i;
+    }
+
+  /* Child ran out; if container also did, they are equal;
+   * otherwise, the child is a parent of the container.
+   */
+  if (container[i] == NULL)
+    return STR_EQUAL;
+  else
+    return STR_DIFFERENT;
+}
+
+#if 0
+static void
+spew_subtree_recurse (DBusObjectSubtree *subtree,
+                      int                indent)
+{
+  int i;
+
+  i = 0;
+  while (i < indent)
+    {
+      _dbus_verbose (" ");
+      ++i;
+    }
+
+  _dbus_verbose ("%s (%d children)\n",
+                 subtree->name, subtree->n_subtrees);
+
+  i = 0;
+  while (i < subtree->n_subtrees)
+    {
+      spew_subtree_recurse (subtree->subtrees[i], indent + 2);
+
+      ++i;
+    }
+}
+
+static void
+spew_tree (DBusObjectTree *tree)
+{
+  spew_subtree_recurse (tree->root, 0);
+}
+#endif
+
+/**
+ * Callback data used in tests
+ */
+typedef struct
+{
+  const char **path; /**< Path */
+  dbus_bool_t handler_fallback; /**< true if the handler may be called as fallback */
+  dbus_bool_t message_handled; /**< Gets set to true if message handler called */
+  dbus_bool_t handler_unregistered; /**< gets set to true if handler is unregistered */
+} TreeTestData;
+
+
+static void
+test_unregister_function (DBusConnection  *connection,
+                          void            *user_data)
+{
+  TreeTestData *ttd = user_data;
+
+  ttd->handler_unregistered = TRUE;
+}
+
+static DBusHandlerResult
+test_message_function (DBusConnection  *connection,
+                       DBusMessage     *message,
+                       void            *user_data)
+{
+  TreeTestData *ttd = user_data;
+
+  ttd->message_handled = TRUE;
+
+  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static dbus_bool_t
+do_register (DBusObjectTree *tree,
+             const char    **path,
+             dbus_bool_t     fallback,
+             int             i,
+             TreeTestData   *tree_test_data)
+{
+  DBusObjectPathVTable vtable = { test_unregister_function,
+                                  test_message_function, NULL };
+
+  tree_test_data[i].message_handled = FALSE;
+  tree_test_data[i].handler_unregistered = FALSE;
+  tree_test_data[i].handler_fallback = fallback;
+  tree_test_data[i].path = path;
+
+  if (!_dbus_object_tree_register (tree, fallback, path,
+                                   &vtable,
+                                   &tree_test_data[i],
+                                   NULL))
+    return FALSE;
+
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path) ==
+                &tree_test_data[i]);
+  
+  return TRUE;
+}
+
+static dbus_bool_t
+do_test_dispatch (DBusObjectTree *tree,
+                  const char    **path,
+                  int             i,
+                  TreeTestData   *tree_test_data,
+                  int             n_test_data)
+{
+  DBusMessage *message;
+  int j;
+  DBusHandlerResult result;
+  char *flat;
+
+  message = NULL;
+  
+  flat = flatten_path (path);
+  if (flat == NULL)
+    goto oom;
+
+  message = dbus_message_new_method_call (NULL,
+                                          flat,
+                                          "org.freedesktop.TestInterface",
+                                          "Foo");
+  dbus_free (flat);
+  if (message == NULL)
+    goto oom;
+
+  j = 0;
+  while (j < n_test_data)
+    {
+      tree_test_data[j].message_handled = FALSE;
+      ++j;
+    }
+
+  result = _dbus_object_tree_dispatch_and_unlock (tree, message);
+  if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
+    goto oom;
+
+  _dbus_assert (tree_test_data[i].message_handled);
+
+  j = 0;
+  while (j < n_test_data)
+    {
+      if (tree_test_data[j].message_handled)
+       {
+         if (tree_test_data[j].handler_fallback)
+           _dbus_assert (path_contains (tree_test_data[j].path,
+                                        path) != STR_DIFFERENT);
+         else
+           _dbus_assert (path_contains (tree_test_data[j].path, path) == STR_EQUAL);
+       }
+      else
+       {
+         if (tree_test_data[j].handler_fallback)
+           _dbus_assert (path_contains (tree_test_data[j].path,
+                                        path) == STR_DIFFERENT);
+         else
+           _dbus_assert (path_contains (tree_test_data[j].path, path) != STR_EQUAL);
+       }
+
+      ++j;
+    }
+
+  dbus_message_unref (message);
+
+  return TRUE;
+
+ oom:
+  if (message)
+    dbus_message_unref (message);
+  return FALSE;
+}
+
+static size_t
+string_array_length (const char **array)
+{
+  size_t i;
+  for (i = 0; array[i]; i++) ;
+  return i;
+}
+
+typedef struct
+{
+  const char *path;
+  const char *result[20];
+} DecomposePathTest;
+
+static DecomposePathTest decompose_tests[] = {
+  { "/foo", { "foo", NULL } },
+  { "/foo/bar", { "foo", "bar", NULL } },
+  { "/", { NULL } },
+  { "/a/b", { "a", "b", NULL } },
+  { "/a/b/c", { "a", "b", "c", NULL } },
+  { "/a/b/c/d", { "a", "b", "c", "d", NULL } },
+  { "/foo/bar/q", { "foo", "bar", "q", NULL } },
+  { "/foo/bar/this/is/longer", { "foo", "bar", "this", "is", "longer", NULL } }
+};
+
+static dbus_bool_t
+run_decompose_tests (void)
+{
+  int i;
+
+  i = 0;
+  while (i < _DBUS_N_ELEMENTS (decompose_tests))
+    {
+      char **result;
+      int    result_len;
+      int    expected_len;
+
+      if (!_dbus_decompose_path (decompose_tests[i].path,
+                                 strlen (decompose_tests[i].path),
+                                 &result, &result_len))
+        return FALSE;
+
+      expected_len = string_array_length (decompose_tests[i].result);
+      
+      if (result_len != (int) string_array_length ((const char**)result) ||
+          expected_len != result_len ||
+          path_contains (decompose_tests[i].result,
+                         (const char**) result) != STR_EQUAL)
+        {
+          int real_len = string_array_length ((const char**)result);
+          _dbus_warn ("Expected decompose of %s to have len %d, returned %d, appears to have %d\n",
+                      decompose_tests[i].path, expected_len, result_len,
+                      real_len);
+          _dbus_warn ("Decompose resulted in elements: { ");
+          i = 0;
+          while (i < real_len)
+            {
+              _dbus_warn ("\"%s\"%s", result[i],
+                          (i + 1) == real_len ? "" : ", ");
+              ++i;
+            }
+          _dbus_warn ("}\n");
+          _dbus_assert_not_reached ("path decompose failed\n");
+        }
+
+      dbus_free_string_array (result);
+
+      ++i;
+    }
+  
+  return TRUE;
+}
+
+static dbus_bool_t
+object_tree_test_iteration (void *data)
+{
+  const char *path0[] = { NULL };
+  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 };
+  const char *path6[] = { "blah", "boof", NULL };
+  const char *path7[] = { "blah", "boof", "this", "is", "really", "long", NULL };
+  const char *path8[] = { "childless", NULL };
+  DBusObjectTree *tree;
+  TreeTestData tree_test_data[9];
+  int i;
+  dbus_bool_t exact_match;
+
+  if (!run_decompose_tests ())
+    return FALSE;
+  
+  tree = NULL;
+
+  tree = _dbus_object_tree_new (NULL);
+  if (tree == NULL)
+    goto out;
+
+  if (!do_register (tree, path0, TRUE, 0, tree_test_data))
+    goto out;
+
+  _dbus_assert (find_subtree (tree, path0, NULL));
+  _dbus_assert (!find_subtree (tree, path1, NULL));
+  _dbus_assert (!find_subtree (tree, path2, NULL));
+  _dbus_assert (!find_subtree (tree, path3, NULL));
+  _dbus_assert (!find_subtree (tree, path4, NULL));
+  _dbus_assert (!find_subtree (tree, path5, NULL));
+  _dbus_assert (!find_subtree (tree, path6, NULL));
+  _dbus_assert (!find_subtree (tree, path7, NULL));
+  _dbus_assert (!find_subtree (tree, path8, NULL));
+
+  _dbus_assert (find_handler (tree, path0, &exact_match) && exact_match);
+  _dbus_assert (find_handler (tree, path1, &exact_match) == tree->root && !exact_match);
+  _dbus_assert (find_handler (tree, path2, &exact_match) == tree->root && !exact_match);
+  _dbus_assert (find_handler (tree, path3, &exact_match) == tree->root && !exact_match);
+  _dbus_assert (find_handler (tree, path4, &exact_match) == tree->root && !exact_match);
+  _dbus_assert (find_handler (tree, path5, &exact_match) == tree->root && !exact_match);
+  _dbus_assert (find_handler (tree, path6, &exact_match) == tree->root && !exact_match);
+  _dbus_assert (find_handler (tree, path7, &exact_match) == tree->root && !exact_match);
+  _dbus_assert (find_handler (tree, path8, &exact_match) == tree->root && !exact_match);
+  
+  if (!do_register (tree, path1, TRUE, 1, tree_test_data))
+    goto out;
+
+  _dbus_assert (find_subtree (tree, path0, NULL));
+  _dbus_assert (find_subtree (tree, path1, NULL));
+  _dbus_assert (!find_subtree (tree, path2, NULL));
+  _dbus_assert (!find_subtree (tree, path3, NULL));
+  _dbus_assert (!find_subtree (tree, path4, NULL));
+  _dbus_assert (!find_subtree (tree, path5, NULL));
+  _dbus_assert (!find_subtree (tree, path6, NULL));
+  _dbus_assert (!find_subtree (tree, path7, NULL));
+  _dbus_assert (!find_subtree (tree, path8, NULL));
+
+  _dbus_assert (find_handler (tree, path0, &exact_match) &&  exact_match);
+  _dbus_assert (find_handler (tree, path1, &exact_match) &&  exact_match);
+  _dbus_assert (find_handler (tree, path2, &exact_match) && !exact_match);
+  _dbus_assert (find_handler (tree, path3, &exact_match) && !exact_match);
+  _dbus_assert (find_handler (tree, path4, &exact_match) && !exact_match);
+  _dbus_assert (find_handler (tree, path5, &exact_match) == tree->root && !exact_match);
+  _dbus_assert (find_handler (tree, path6, &exact_match) == tree->root && !exact_match);
+  _dbus_assert (find_handler (tree, path7, &exact_match) == tree->root && !exact_match);
+  _dbus_assert (find_handler (tree, path8, &exact_match) == tree->root && !exact_match);
+
+  if (!do_register (tree, path2, TRUE, 2, tree_test_data))
+    goto out;
+
+  _dbus_assert (find_subtree (tree, path1, NULL));
+  _dbus_assert (find_subtree (tree, path2, NULL));
+  _dbus_assert (!find_subtree (tree, path3, NULL));
+  _dbus_assert (!find_subtree (tree, path4, NULL));
+  _dbus_assert (!find_subtree (tree, path5, NULL));
+  _dbus_assert (!find_subtree (tree, path6, NULL));
+  _dbus_assert (!find_subtree (tree, path7, NULL));
+  _dbus_assert (!find_subtree (tree, path8, NULL));
+
+  if (!do_register (tree, path3, TRUE, 3, tree_test_data))
+    goto out;
+
+  _dbus_assert (find_subtree (tree, path0, NULL));
+  _dbus_assert (find_subtree (tree, path1, NULL));
+  _dbus_assert (find_subtree (tree, path2, NULL));
+  _dbus_assert (find_subtree (tree, path3, NULL));
+  _dbus_assert (!find_subtree (tree, path4, NULL));
+  _dbus_assert (!find_subtree (tree, path5, NULL));
+  _dbus_assert (!find_subtree (tree, path6, NULL));
+  _dbus_assert (!find_subtree (tree, path7, NULL));
+  _dbus_assert (!find_subtree (tree, path8, NULL));
+  
+  if (!do_register (tree, path4, TRUE, 4, tree_test_data))
+    goto out;
+
+  _dbus_assert (find_subtree (tree, path0, NULL));
+  _dbus_assert (find_subtree (tree, path1, NULL));
+  _dbus_assert (find_subtree (tree, path2, NULL));
+  _dbus_assert (find_subtree (tree, path3, NULL));  
+  _dbus_assert (find_subtree (tree, path4, NULL));
+  _dbus_assert (!find_subtree (tree, path5, NULL));
+  _dbus_assert (!find_subtree (tree, path6, NULL));
+  _dbus_assert (!find_subtree (tree, path7, NULL));
+  _dbus_assert (!find_subtree (tree, path8, NULL));
+  
+  if (!do_register (tree, path5, TRUE, 5, tree_test_data))
+    goto out;
+
+  _dbus_assert (find_subtree (tree, path0, NULL));
+  _dbus_assert (find_subtree (tree, path1, NULL));
+  _dbus_assert (find_subtree (tree, path2, NULL));
+  _dbus_assert (find_subtree (tree, path3, NULL));
+  _dbus_assert (find_subtree (tree, path4, NULL));
+  _dbus_assert (find_subtree (tree, path5, NULL));
+  _dbus_assert (!find_subtree (tree, path6, NULL));
+  _dbus_assert (!find_subtree (tree, path7, NULL));
+  _dbus_assert (!find_subtree (tree, path8, NULL));
+
+  _dbus_assert (find_handler (tree, path0, &exact_match) == tree->root &&  exact_match);
+  _dbus_assert (find_handler (tree, path1, &exact_match) != tree->root &&  exact_match);
+  _dbus_assert (find_handler (tree, path2, &exact_match) != tree->root &&  exact_match);
+  _dbus_assert (find_handler (tree, path3, &exact_match) != tree->root &&  exact_match);
+  _dbus_assert (find_handler (tree, path4, &exact_match) != tree->root &&  exact_match);
+  _dbus_assert (find_handler (tree, path5, &exact_match) != tree->root &&  exact_match);
+  _dbus_assert (find_handler (tree, path6, &exact_match) != tree->root && !exact_match);
+  _dbus_assert (find_handler (tree, path7, &exact_match) != tree->root && !exact_match);
+  _dbus_assert (find_handler (tree, path8, &exact_match) == tree->root && !exact_match);
+
+  if (!do_register (tree, path6, TRUE, 6, tree_test_data))
+    goto out;
+
+  _dbus_assert (find_subtree (tree, path0, NULL));
+  _dbus_assert (find_subtree (tree, path1, NULL));
+  _dbus_assert (find_subtree (tree, path2, NULL));
+  _dbus_assert (find_subtree (tree, path3, NULL));
+  _dbus_assert (find_subtree (tree, path4, NULL));
+  _dbus_assert (find_subtree (tree, path5, NULL));
+  _dbus_assert (find_subtree (tree, path6, NULL));
+  _dbus_assert (!find_subtree (tree, path7, NULL));
+  _dbus_assert (!find_subtree (tree, path8, NULL));
+
+  if (!do_register (tree, path7, TRUE, 7, tree_test_data))
+    goto out;
+
+  _dbus_assert (find_subtree (tree, path0, NULL));
+  _dbus_assert (find_subtree (tree, path1, NULL));
+  _dbus_assert (find_subtree (tree, path2, NULL));
+  _dbus_assert (find_subtree (tree, path3, NULL));
+  _dbus_assert (find_subtree (tree, path4, NULL));
+  _dbus_assert (find_subtree (tree, path5, NULL));
+  _dbus_assert (find_subtree (tree, path6, NULL));
+  _dbus_assert (find_subtree (tree, path7, NULL));
+  _dbus_assert (!find_subtree (tree, path8, NULL));
+
+  if (!do_register (tree, path8, TRUE, 8, tree_test_data))
+    goto out;
+
+  _dbus_assert (find_subtree (tree, path0, NULL));
+  _dbus_assert (find_subtree (tree, path1, NULL));
+  _dbus_assert (find_subtree (tree, path2, NULL));
+  _dbus_assert (find_subtree (tree, path3, NULL));
+  _dbus_assert (find_subtree (tree, path4, NULL));
+  _dbus_assert (find_subtree (tree, path5, NULL));
+  _dbus_assert (find_subtree (tree, path6, NULL));
+  _dbus_assert (find_subtree (tree, path7, NULL));
+  _dbus_assert (find_subtree (tree, path8, NULL));
+
+  _dbus_assert (find_handler (tree, path0, &exact_match) == tree->root &&  exact_match);
+  _dbus_assert (find_handler (tree, path1, &exact_match) != tree->root && exact_match);
+  _dbus_assert (find_handler (tree, path2, &exact_match) != tree->root && exact_match);
+  _dbus_assert (find_handler (tree, path3, &exact_match) != tree->root && exact_match);
+  _dbus_assert (find_handler (tree, path4, &exact_match) != tree->root && exact_match);
+  _dbus_assert (find_handler (tree, path5, &exact_match) != tree->root && exact_match);
+  _dbus_assert (find_handler (tree, path6, &exact_match) != tree->root && exact_match);
+  _dbus_assert (find_handler (tree, path7, &exact_match) != tree->root && exact_match);
+  _dbus_assert (find_handler (tree, path8, &exact_match) != tree->root && exact_match);
+  
+  /* test the list_registered function */
+
+  {
+    const char *root[] = { NULL };
+    char **child_entries;
+    int nb;
+
+    _dbus_object_tree_list_registered_unlocked (tree, path1, &child_entries);
+    if (child_entries != NULL)
+      {
+       nb = string_array_length ((const char**)child_entries);
+       _dbus_assert (nb == 1);
+       dbus_free_string_array (child_entries);
+      }
+
+    _dbus_object_tree_list_registered_unlocked (tree, path2, &child_entries);
+    if (child_entries != NULL)
+      {
+       nb = string_array_length ((const char**)child_entries);
+       _dbus_assert (nb == 2);
+       dbus_free_string_array (child_entries);
+      }
+
+    _dbus_object_tree_list_registered_unlocked (tree, path8, &child_entries);
+    if (child_entries != NULL)
+      {
+       nb = string_array_length ((const char**)child_entries);
+       _dbus_assert (nb == 0);
+       dbus_free_string_array (child_entries);
+      }
+
+    _dbus_object_tree_list_registered_unlocked (tree, root, &child_entries);
+    if (child_entries != NULL)
+      {
+       nb = string_array_length ((const char**)child_entries);
+       _dbus_assert (nb == 3);
+       dbus_free_string_array (child_entries);
+      }
+  }
+
+  /* Check that destroying tree calls unregister funcs */
+  _dbus_object_tree_unref (tree);
+
+  i = 0;
+  while (i < (int) _DBUS_N_ELEMENTS (tree_test_data))
+    {
+      _dbus_assert (tree_test_data[i].handler_unregistered);
+      _dbus_assert (!tree_test_data[i].message_handled);
+      ++i;
+    }
+
+  /* Now start again and try the individual unregister function */
+  tree = _dbus_object_tree_new (NULL);
+  if (tree == NULL)
+    goto out;
+
+  if (!do_register (tree, path0, TRUE, 0, tree_test_data))
+    goto out;
+  if (!do_register (tree, path1, TRUE, 1, tree_test_data))
+    goto out;
+  if (!do_register (tree, path2, TRUE, 2, tree_test_data))
+    goto out;
+  if (!do_register (tree, path3, TRUE, 3, tree_test_data))
+    goto out;
+  if (!do_register (tree, path4, TRUE, 4, tree_test_data))
+    goto out;
+  if (!do_register (tree, path5, TRUE, 5, tree_test_data))
+    goto out;
+  if (!do_register (tree, path6, TRUE, 6, tree_test_data))
+    goto out;
+  if (!do_register (tree, path7, TRUE, 7, tree_test_data))
+    goto out;
+  if (!do_register (tree, path8, TRUE, 8, tree_test_data))
+    goto out;
+
+  _dbus_object_tree_unregister_and_unlock (tree, path0);
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path0) == NULL);
+
+  _dbus_assert (!find_subtree (tree, path0, NULL));
+  _dbus_assert (find_subtree (tree, path1, NULL));
+  _dbus_assert (find_subtree (tree, path2, NULL));
+  _dbus_assert (find_subtree (tree, path3, NULL));
+  _dbus_assert (find_subtree (tree, path4, NULL));
+  _dbus_assert (find_subtree (tree, path5, NULL));
+  _dbus_assert (find_subtree (tree, path6, NULL));
+  _dbus_assert (find_subtree (tree, path7, NULL));
+  _dbus_assert (find_subtree (tree, path8, NULL));
+  
+  _dbus_object_tree_unregister_and_unlock (tree, path1);
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path1) == NULL);
+
+  _dbus_assert (!find_subtree (tree, path0, NULL));
+  _dbus_assert (!find_subtree (tree, path1, NULL));
+  _dbus_assert (find_subtree (tree, path2, NULL));
+  _dbus_assert (find_subtree (tree, path3, NULL));
+  _dbus_assert (find_subtree (tree, path4, NULL));
+  _dbus_assert (find_subtree (tree, path5, NULL));
+  _dbus_assert (find_subtree (tree, path6, NULL));
+  _dbus_assert (find_subtree (tree, path7, NULL));
+  _dbus_assert (find_subtree (tree, path8, NULL));
+
+  _dbus_object_tree_unregister_and_unlock (tree, path2);
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path2) == NULL);
+
+  _dbus_assert (!find_subtree (tree, path0, NULL));
+  _dbus_assert (!find_subtree (tree, path1, NULL));
+  _dbus_assert (!find_subtree (tree, path2, NULL));
+  _dbus_assert (find_subtree (tree, path3, NULL));
+  _dbus_assert (find_subtree (tree, path4, NULL));
+  _dbus_assert (find_subtree (tree, path5, NULL));
+  _dbus_assert (find_subtree (tree, path6, NULL));
+  _dbus_assert (find_subtree (tree, path7, NULL));
+  _dbus_assert (find_subtree (tree, path8, NULL));
+  
+  _dbus_object_tree_unregister_and_unlock (tree, path3);
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path3) == NULL);
+
+  _dbus_assert (!find_subtree (tree, path0, NULL));
+  _dbus_assert (!find_subtree (tree, path1, NULL));
+  _dbus_assert (!find_subtree (tree, path2, NULL));
+  _dbus_assert (!find_subtree (tree, path3, NULL));
+  _dbus_assert (find_subtree (tree, path4, NULL));
+  _dbus_assert (find_subtree (tree, path5, NULL));
+  _dbus_assert (find_subtree (tree, path6, NULL));
+  _dbus_assert (find_subtree (tree, path7, NULL));
+  _dbus_assert (find_subtree (tree, path8, NULL));
+  
+  _dbus_object_tree_unregister_and_unlock (tree, path4);
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path4) == NULL);
+
+  _dbus_assert (!find_subtree (tree, path0, NULL));
+  _dbus_assert (!find_subtree (tree, path1, NULL));
+  _dbus_assert (!find_subtree (tree, path2, NULL));
+  _dbus_assert (!find_subtree (tree, path3, NULL));
+  _dbus_assert (!find_subtree (tree, path4, NULL));
+  _dbus_assert (find_subtree (tree, path5, NULL));
+  _dbus_assert (find_subtree (tree, path6, NULL));
+  _dbus_assert (find_subtree (tree, path7, NULL));
+  _dbus_assert (find_subtree (tree, path8, NULL));
+  
+  _dbus_object_tree_unregister_and_unlock (tree, path5);
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path5) == NULL);
+
+  _dbus_assert (!find_subtree (tree, path0, NULL));
+  _dbus_assert (!find_subtree (tree, path1, NULL));
+  _dbus_assert (!find_subtree (tree, path2, NULL));
+  _dbus_assert (!find_subtree (tree, path3, NULL));
+  _dbus_assert (!find_subtree (tree, path4, NULL));
+  _dbus_assert (!find_subtree (tree, path5, NULL));
+  _dbus_assert (find_subtree (tree, path6, NULL));
+  _dbus_assert (find_subtree (tree, path7, NULL));
+  _dbus_assert (find_subtree (tree, path8, NULL));
+  
+  _dbus_object_tree_unregister_and_unlock (tree, path6);
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path6) == NULL);
+
+  _dbus_assert (!find_subtree (tree, path0, NULL));
+  _dbus_assert (!find_subtree (tree, path1, NULL));
+  _dbus_assert (!find_subtree (tree, path2, NULL));
+  _dbus_assert (!find_subtree (tree, path3, NULL));
+  _dbus_assert (!find_subtree (tree, path4, NULL));
+  _dbus_assert (!find_subtree (tree, path5, NULL));
+  _dbus_assert (!find_subtree (tree, path6, NULL));
+  _dbus_assert (find_subtree (tree, path7, NULL));
+  _dbus_assert (find_subtree (tree, path8, NULL));
+
+  _dbus_object_tree_unregister_and_unlock (tree, path7);
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path7) == NULL);
+
+  _dbus_assert (!find_subtree (tree, path0, NULL));
+  _dbus_assert (!find_subtree (tree, path1, NULL));
+  _dbus_assert (!find_subtree (tree, path2, NULL));
+  _dbus_assert (!find_subtree (tree, path3, NULL));
+  _dbus_assert (!find_subtree (tree, path4, NULL));
+  _dbus_assert (!find_subtree (tree, path5, NULL));
+  _dbus_assert (!find_subtree (tree, path6, NULL));
+  _dbus_assert (!find_subtree (tree, path7, NULL));
+  _dbus_assert (find_subtree (tree, path8, NULL));
+
+  _dbus_object_tree_unregister_and_unlock (tree, path8);
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path8) == NULL);
+
+  _dbus_assert (!find_subtree (tree, path0, NULL));
+  _dbus_assert (!find_subtree (tree, path1, NULL));
+  _dbus_assert (!find_subtree (tree, path2, NULL));
+  _dbus_assert (!find_subtree (tree, path3, NULL));
+  _dbus_assert (!find_subtree (tree, path4, NULL));
+  _dbus_assert (!find_subtree (tree, path5, NULL));
+  _dbus_assert (!find_subtree (tree, path6, NULL));
+  _dbus_assert (!find_subtree (tree, path7, NULL));
+  _dbus_assert (!find_subtree (tree, path8, NULL));
+  
+  i = 0;
+  while (i < (int) _DBUS_N_ELEMENTS (tree_test_data))
+    {
+      _dbus_assert (tree_test_data[i].handler_unregistered);
+      _dbus_assert (!tree_test_data[i].message_handled);
+      ++i;
+    }
+
+  /* Register it all again, and test dispatch */
+  
+  if (!do_register (tree, path0, TRUE, 0, tree_test_data))
+    goto out;
+  if (!do_register (tree, path1, FALSE, 1, tree_test_data))
+    goto out;
+  if (!do_register (tree, path2, TRUE, 2, tree_test_data))
+    goto out;
+  if (!do_register (tree, path3, TRUE, 3, tree_test_data))
+    goto out;
+  if (!do_register (tree, path4, TRUE, 4, tree_test_data))
+    goto out;
+  if (!do_register (tree, path5, TRUE, 5, tree_test_data))
+    goto out;
+  if (!do_register (tree, path6, FALSE, 6, tree_test_data))
+    goto out;
+  if (!do_register (tree, path7, TRUE, 7, tree_test_data))
+    goto out;
+  if (!do_register (tree, path8, TRUE, 8, tree_test_data))
+    goto out;
+
+#if 0
+  spew_tree (tree);
+#endif
+
+  if (!do_test_dispatch (tree, path0, 0, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+    goto out;
+  if (!do_test_dispatch (tree, path1, 1, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+    goto out;
+  if (!do_test_dispatch (tree, path2, 2, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+    goto out;
+  if (!do_test_dispatch (tree, path3, 3, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+    goto out;
+  if (!do_test_dispatch (tree, path4, 4, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+    goto out;
+  if (!do_test_dispatch (tree, path5, 5, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+    goto out;
+  if (!do_test_dispatch (tree, path6, 6, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+    goto out;
+  if (!do_test_dispatch (tree, path7, 7, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+    goto out;
+  if (!do_test_dispatch (tree, path8, 8, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+    goto out;
+  
+ out:
+  if (tree)
+    {
+      /* test ref */
+      _dbus_object_tree_ref (tree);
+      _dbus_object_tree_unref (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 /* !DOXYGEN_SHOULD_SKIP_THIS */
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-object-tree.h b/src/dbus/dbus-object-tree.h
new file mode 100644 (file)
index 0000000..1166752
--- /dev/null
@@ -0,0 +1,62 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-object-tree.h  DBusObjectTree (internals of DBusConnection)
+ *
+ * Copyright (C) 2003  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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);
+DBusObjectTree* _dbus_object_tree_ref   (DBusObjectTree *tree);
+void            _dbus_object_tree_unref (DBusObjectTree *tree);
+
+dbus_bool_t       _dbus_object_tree_register               (DBusObjectTree              *tree,
+                                                            dbus_bool_t                  fallback,
+                                                            const char                 **path,
+                                                            const DBusObjectPathVTable  *vtable,
+                                                            void                        *user_data,
+                                                            DBusError                   *error);
+void              _dbus_object_tree_unregister_and_unlock  (DBusObjectTree              *tree,
+                                                            const char                 **path);
+DBusHandlerResult _dbus_object_tree_dispatch_and_unlock    (DBusObjectTree              *tree,
+                                                            DBusMessage                 *message);
+void*             _dbus_object_tree_get_user_data_unlocked (DBusObjectTree              *tree,
+                                                            const char                 **path);
+void              _dbus_object_tree_free_all_unlocked      (DBusObjectTree              *tree);
+
+
+dbus_bool_t _dbus_object_tree_list_registered_and_unlock (DBusObjectTree *tree,
+                                                          const char    **parent_path,
+                                                          char         ***child_entries);
+
+dbus_bool_t _dbus_decompose_path (const char   *data,
+                                  int           len,
+                                  char       ***path,
+                                  int          *path_len);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_OBJECT_TREE_H */
diff --git a/src/dbus/dbus-pending-call-internal.h b/src/dbus/dbus-pending-call-internal.h
new file mode 100644 (file)
index 0000000..05374a6
--- /dev/null
@@ -0,0 +1,67 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-pending-call-internal.h DBusPendingCall internal interfaces
+ *
+ * Copyright (C) 2002  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_PENDING_CALL_INTERNAL_H
+#define DBUS_PENDING_CALL_INTERNAL_H
+
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-message.h>
+#include <dbus/dbus-connection.h>
+#include <dbus/dbus-list.h>
+
+DBUS_BEGIN_DECLS
+
+dbus_bool_t      _dbus_pending_call_is_timeout_added_unlocked    (DBusPendingCall    *pending);
+void             _dbus_pending_call_set_timeout_added_unlocked   (DBusPendingCall    *pending,
+                                                                  dbus_bool_t         is_added);
+DBusTimeout    * _dbus_pending_call_get_timeout_unlocked         (DBusPendingCall    *pending);
+dbus_uint32_t    _dbus_pending_call_get_reply_serial_unlocked    (DBusPendingCall    *pending);
+void             _dbus_pending_call_set_reply_serial_unlocked    (DBusPendingCall    *pending,
+                                                                  dbus_uint32_t       serial);
+DBusConnection * _dbus_pending_call_get_connection_and_lock      (DBusPendingCall    *pending);
+DBusConnection * _dbus_pending_call_get_connection_unlocked      (DBusPendingCall    *pending);
+dbus_bool_t      _dbus_pending_call_get_completed_unlocked       (DBusPendingCall    *pending);
+void             _dbus_pending_call_complete                     (DBusPendingCall    *pending);
+void             _dbus_pending_call_set_reply_unlocked           (DBusPendingCall    *pending,
+                                                                  DBusMessage        *message);
+void             _dbus_pending_call_queue_timeout_error_unlocked (DBusPendingCall    *pending,
+                                                                  DBusConnection     *connection);
+void             _dbus_pending_call_set_reply_serial_unlocked    (DBusPendingCall    *pending,
+                                                                  dbus_uint32_t       serial);
+dbus_bool_t      _dbus_pending_call_set_timeout_error_unlocked   (DBusPendingCall    *pending,
+                                                                  DBusMessage        *message,
+                                                                  dbus_uint32_t       serial);
+DBusPendingCall* _dbus_pending_call_new_unlocked                 (DBusConnection     *connection,
+                                                                  int                 timeout_milliseconds,
+                                                                  DBusTimeoutHandler  timeout_handler);
+DBusPendingCall* _dbus_pending_call_ref_unlocked                 (DBusPendingCall    *pending);
+void             _dbus_pending_call_unref_and_unlock             (DBusPendingCall    *pending);
+dbus_bool_t      _dbus_pending_call_set_data_unlocked            (DBusPendingCall    *pending,
+                                                                  dbus_int32_t        slot,
+                                                                  void               *data,
+                                                                  DBusFreeFunction    free_data_func);
+
+
+DBUS_END_DECLS
+
+#endif /* DBUS_PENDING_CALL_INTERNAL_H */
diff --git a/src/dbus/dbus-pending-call.c b/src/dbus/dbus-pending-call.c
new file mode 100644 (file)
index 0000000..51b9378
--- /dev/null
@@ -0,0 +1,826 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-pending-call.c Object representing a call in progress.
+ *
+ * Copyright (C) 2002, 2003 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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-internals.h"
+#include "dbus-connection-internal.h"
+#include "dbus-pending-call-internal.h"
+#include "dbus-pending-call.h"
+#include "dbus-list.h"
+#include "dbus-threads.h"
+#include "dbus-test.h"
+
+/**
+ * @defgroup DBusPendingCallInternals DBusPendingCall implementation details
+ * @ingroup DBusInternals
+ * @brief DBusPendingCall private implementation details.
+ *
+ * The guts of DBusPendingCall and its methods.
+ *
+ * @{
+ */
+
+/**
+ * @brief Internals of DBusPendingCall
+ *
+ * Opaque object representing a reply message that we're waiting for.
+ */
+
+/**
+ * shorter and more visible way to write _dbus_connection_lock()
+ */
+#define CONNECTION_LOCK(connection)   _dbus_connection_lock(connection)
+/**
+ * shorter and more visible way to write _dbus_connection_unlock()
+ */
+#define CONNECTION_UNLOCK(connection) _dbus_connection_unlock(connection)
+
+/**
+ * Implementation details of #DBusPendingCall - all fields are private.
+ */
+struct DBusPendingCall
+{
+  DBusAtomic refcount;                            /**< reference count */
+
+  DBusDataSlotList slot_list;                     /**< Data stored by allocated integer ID */
+  
+  DBusPendingCallNotifyFunction function;         /**< Notifier when reply arrives. */
+
+  DBusConnection *connection;                     /**< Connections we're associated with */
+  DBusMessage *reply;                             /**< Reply (after we've received it) */
+  DBusTimeout *timeout;                           /**< Timeout */
+
+  DBusList *timeout_link;                         /**< Preallocated timeout response */
+  
+  dbus_uint32_t reply_serial;                     /**< Expected serial of reply */
+
+  unsigned int completed : 1;                     /**< TRUE if completed */
+  unsigned int timeout_added : 1;                 /**< Have added the timeout */
+};
+
+static dbus_int32_t notify_user_data_slot = -1;
+
+/**
+ * Creates a new pending reply object.
+ *
+ * @param connection connection where reply will arrive
+ * @param timeout_milliseconds length of timeout, -1 for default
+ * @param timeout_handler timeout handler, takes pending call as data
+ * @returns a new #DBusPendingCall or #NULL if no memory.
+ */
+DBusPendingCall*
+_dbus_pending_call_new_unlocked (DBusConnection    *connection,
+                                 int                timeout_milliseconds,
+                                 DBusTimeoutHandler timeout_handler)
+{
+  DBusPendingCall *pending;
+  DBusTimeout *timeout;
+
+  _dbus_assert (timeout_milliseconds >= 0 || timeout_milliseconds == -1);
+  if (timeout_milliseconds == -1)
+    timeout_milliseconds = _DBUS_DEFAULT_TIMEOUT_VALUE;
+
+  /* it would probably seem logical to pass in _DBUS_INT_MAX for
+   * infinite timeout, but then math in
+   * _dbus_connection_block_for_reply would get all overflow-prone, so
+   * smack that down.
+   */
+  if (timeout_milliseconds > _DBUS_ONE_HOUR_IN_MILLISECONDS * 6)
+    timeout_milliseconds = _DBUS_ONE_HOUR_IN_MILLISECONDS * 6;
+  
+  if (!dbus_pending_call_allocate_data_slot (&notify_user_data_slot))
+    return NULL;
+  
+  pending = dbus_new0 (DBusPendingCall, 1);
+  
+  if (pending == NULL)
+    {
+      dbus_pending_call_free_data_slot (&notify_user_data_slot);
+      return NULL;
+    }
+
+  timeout = _dbus_timeout_new (timeout_milliseconds,
+                               timeout_handler,
+                              pending, NULL);  
+
+  if (timeout == NULL)
+    {
+      dbus_pending_call_free_data_slot (&notify_user_data_slot);
+      dbus_free (pending);
+      return NULL;
+    }
+  
+  pending->refcount.value = 1;
+  pending->connection = connection;
+  _dbus_connection_ref_unlocked (pending->connection);
+
+  pending->timeout = timeout;
+
+
+  _dbus_data_slot_list_init (&pending->slot_list);
+  
+  return pending;
+}
+
+/**
+ * Sets the reply of a pending call with the given message,
+ * or if the message is #NULL, by timing out the pending call.
+ * 
+ * @param pending the pending call
+ * @param message the message to complete the call with, or #NULL
+ *  to time out the call
+ */
+void
+_dbus_pending_call_set_reply_unlocked (DBusPendingCall *pending,
+                                       DBusMessage     *message)
+{
+  if (message == NULL)
+    {
+      message = pending->timeout_link->data;
+      _dbus_list_clear (&pending->timeout_link);
+    }
+  else
+    dbus_message_ref (message);
+
+  _dbus_verbose ("  handing message %p (%s) to pending call serial %u\n",
+                 message,
+                 dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN ?
+                 "method return" :
+                 dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR ?
+                 "error" : "other type",
+                 pending->reply_serial);
+  
+  _dbus_assert (pending->reply == NULL);
+  _dbus_assert (pending->reply_serial == dbus_message_get_reply_serial (message));
+  pending->reply = message;
+}
+
+/**
+ * Calls notifier function for the pending call
+ * and sets the call to completed.
+ *
+ * @param pending the pending call
+ * 
+ */
+void
+_dbus_pending_call_complete (DBusPendingCall *pending)
+{
+  _dbus_assert (!pending->completed);
+  
+  pending->completed = TRUE;
+
+  if (pending->function)
+    {
+      void *user_data;
+      user_data = dbus_pending_call_get_data (pending,
+                                              notify_user_data_slot);
+      
+      (* pending->function) (pending, user_data);
+    }
+}
+
+/**
+ * If the pending call hasn't been timed out, add its timeout
+ * error reply to the connection's incoming message queue.
+ *
+ * @param pending the pending call
+ * @param connection the connection the call was sent to
+ */
+void
+_dbus_pending_call_queue_timeout_error_unlocked (DBusPendingCall *pending, 
+                                                 DBusConnection  *connection)
+{
+  _dbus_assert (connection == pending->connection);
+  
+  if (pending->timeout_link)
+    {
+      _dbus_connection_queue_synthesized_message_link (connection,
+                                                      pending->timeout_link);
+      pending->timeout_link = NULL;
+    }
+}
+
+/**
+ * Checks to see if a timeout has been added
+ *
+ * @param pending the pending_call
+ * @returns #TRUE if there is a timeout or #FALSE if not
+ */
+dbus_bool_t 
+_dbus_pending_call_is_timeout_added_unlocked (DBusPendingCall  *pending)
+{
+  _dbus_assert (pending != NULL);
+
+  return pending->timeout_added;
+}
+
+
+/**
+ * Sets wether the timeout has been added
+ *
+ * @param pending the pending_call
+ * @param is_added whether or not a timeout is added
+ */
+void
+_dbus_pending_call_set_timeout_added_unlocked (DBusPendingCall  *pending,
+                                               dbus_bool_t       is_added)
+{
+  _dbus_assert (pending != NULL);
+
+  pending->timeout_added = is_added;
+}
+
+
+/**
+ * Retrives the timeout
+ *
+ * @param pending the pending_call
+ * @returns a timeout object 
+ */
+DBusTimeout *
+_dbus_pending_call_get_timeout_unlocked (DBusPendingCall  *pending)
+{
+  _dbus_assert (pending != NULL);
+
+  return pending->timeout;
+}
+
+/**
+ * Gets the reply's serial number
+ *
+ * @param pending the pending_call
+ * @returns a serial number for the reply or 0 
+ */
+dbus_uint32_t 
+_dbus_pending_call_get_reply_serial_unlocked (DBusPendingCall  *pending)
+{
+  _dbus_assert (pending != NULL);
+
+  return pending->reply_serial;
+}
+
+/**
+ * Sets the reply's serial number
+ *
+ * @param pending the pending_call
+ * @param serial the serial number 
+ */
+void
+_dbus_pending_call_set_reply_serial_unlocked  (DBusPendingCall *pending,
+                                               dbus_uint32_t serial)
+{
+  _dbus_assert (pending != NULL);
+  _dbus_assert (pending->reply_serial == 0);
+
+  pending->reply_serial = serial;
+}
+
+/**
+ * Gets the connection associated with this pending call.
+ *
+ * @param pending the pending_call
+ * @returns the connection associated with the pending call
+ */
+DBusConnection *
+_dbus_pending_call_get_connection_and_lock (DBusPendingCall *pending)
+{
+  _dbus_assert (pending != NULL);
+  CONNECTION_LOCK (pending->connection);
+  return pending->connection;
+}
+
+/**
+ * Gets the connection associated with this pending call.
+ *
+ * @param pending the pending_call
+ * @returns the connection associated with the pending call
+ */
+DBusConnection *
+_dbus_pending_call_get_connection_unlocked (DBusPendingCall *pending)
+{
+  _dbus_assert (pending != NULL);
+  return pending->connection;
+}
+
+/**
+ * Sets the reply message associated with the pending call to a timeout error
+ *
+ * @param pending the pending_call
+ * @param message the message we are sending the error reply to 
+ * @param serial serial number for the reply
+ * @return #FALSE on OOM
+ */
+dbus_bool_t
+_dbus_pending_call_set_timeout_error_unlocked (DBusPendingCall *pending,
+                                               DBusMessage     *message,
+                                               dbus_uint32_t    serial)
+{ 
+  DBusList *reply_link;
+  DBusMessage *reply;
+
+  reply = dbus_message_new_error (message, DBUS_ERROR_NO_REPLY,
+                                  "Did not receive a reply. Possible causes include: "
+                                  "the remote application did not send a reply, "
+                                  "the message bus security policy blocked the reply, "
+                                  "the reply timeout expired, or "
+                                  "the network connection was broken.");
+  if (reply == NULL)
+    return FALSE;
+
+  reply_link = _dbus_list_alloc_link (reply);
+  if (reply_link == NULL)
+    {
+      dbus_message_unref (reply);
+      return FALSE;
+    }
+
+  pending->timeout_link = reply_link;
+
+  _dbus_pending_call_set_reply_serial_unlocked (pending, serial);
+  
+  return TRUE;
+}
+
+/**
+ * Increments the reference count on a pending call,
+ * while the lock on its connection is already held.
+ *
+ * @param pending the pending call object
+ * @returns the pending call object
+ */
+DBusPendingCall *
+_dbus_pending_call_ref_unlocked (DBusPendingCall *pending)
+{
+  pending->refcount.value += 1;
+  
+  return pending;
+}
+
+
+static void
+_dbus_pending_call_last_unref (DBusPendingCall *pending)
+{
+  DBusConnection *connection;
+  
+  /* If we get here, we should be already detached
+   * from the connection, or never attached.
+   */
+  _dbus_assert (!pending->timeout_added);  
+
+  connection = pending->connection;
+
+  /* this assumes we aren't holding connection lock... */
+  _dbus_data_slot_list_free (&pending->slot_list);
+
+  if (pending->timeout != NULL)
+    _dbus_timeout_unref (pending->timeout);
+      
+  if (pending->timeout_link)
+    {
+      dbus_message_unref ((DBusMessage *)pending->timeout_link->data);
+      _dbus_list_free_link (pending->timeout_link);
+      pending->timeout_link = NULL;
+    }
+
+  if (pending->reply)
+    {
+      dbus_message_unref (pending->reply);
+      pending->reply = NULL;
+    }
+      
+  dbus_free (pending);
+
+  dbus_pending_call_free_data_slot (&notify_user_data_slot);
+
+  /* connection lock should not be held. */
+  /* Free the connection last to avoid a weird state while
+   * calling out to application code where the pending exists
+   * but not the connection.
+   */
+  dbus_connection_unref (connection);
+}
+
+/**
+ * Decrements the reference count on a pending call,
+ * freeing it if the count reaches 0. Assumes
+ * connection lock is already held.
+ *
+ * @param pending the pending call object
+ */
+void
+_dbus_pending_call_unref_and_unlock (DBusPendingCall *pending)
+{
+  dbus_bool_t last_unref;
+  
+  _dbus_assert (pending->refcount.value > 0);
+
+  pending->refcount.value -= 1;
+  last_unref = pending->refcount.value == 0;
+
+  CONNECTION_UNLOCK (pending->connection);
+  if (last_unref)
+    _dbus_pending_call_last_unref (pending);
+}
+
+/**
+ * Checks whether the pending call has received a reply
+ * yet, or not. Assumes connection lock is held.
+ *
+ * @param pending the pending call
+ * @returns #TRUE if a reply has been received
+ */
+dbus_bool_t
+_dbus_pending_call_get_completed_unlocked (DBusPendingCall    *pending)
+{
+  return pending->completed;
+}
+
+static DBusDataSlotAllocator slot_allocator;
+_DBUS_DEFINE_GLOBAL_LOCK (pending_call_slots);
+
+/**
+ * Stores a pointer on a #DBusPendingCall, along
+ * with an optional function to be used for freeing
+ * the data when the data is set again, or when
+ * the pending call is finalized. The slot number
+ * must have been allocated with dbus_pending_call_allocate_data_slot().
+ *
+ * @param pending the pending_call
+ * @param slot the slot number
+ * @param data the data to store
+ * @param free_data_func finalizer function for the data
+ * @returns #TRUE if there was enough memory to store the data
+ */
+dbus_bool_t
+_dbus_pending_call_set_data_unlocked (DBusPendingCall  *pending,
+                                     dbus_int32_t      slot,
+                                     void             *data,
+                                     DBusFreeFunction  free_data_func)
+{
+  DBusFreeFunction old_free_func;
+  void *old_data;
+  dbus_bool_t retval;
+
+  retval = _dbus_data_slot_list_set (&slot_allocator,
+                                     &pending->slot_list,
+                                     slot, data, free_data_func,
+                                     &old_free_func, &old_data);
+
+  /* Drop locks to call out to app code */
+  CONNECTION_UNLOCK (pending->connection);
+  
+  if (retval)
+    {
+      if (old_free_func)
+        (* old_free_func) (old_data);
+    }
+
+  CONNECTION_LOCK (pending->connection);
+  
+  return retval;
+}
+
+/** @} */
+
+/**
+ * @defgroup DBusPendingCall DBusPendingCall
+ * @ingroup  DBus
+ * @brief Pending reply to a method call message
+ *
+ * A DBusPendingCall is an object representing an
+ * expected reply. A #DBusPendingCall can be created
+ * when you send a message that should have a reply.
+ *
+ * @{
+ */
+
+/**
+ * @typedef DBusPendingCall
+ *
+ * Opaque data type representing a message pending.
+ */
+
+/**
+ * Increments the reference count on a pending call.
+ *
+ * @param pending the pending call object
+ * @returns the pending call object
+ */
+DBusPendingCall *
+dbus_pending_call_ref (DBusPendingCall *pending)
+{
+  _dbus_return_val_if_fail (pending != NULL, NULL);
+
+  /* The connection lock is better than the global
+   * lock in the atomic increment fallback
+   */
+#ifdef DBUS_HAVE_ATOMIC_INT
+  _dbus_atomic_inc (&pending->refcount);
+#else
+  CONNECTION_LOCK (pending->connection);
+  _dbus_assert (pending->refcount.value > 0);
+
+  pending->refcount.value += 1;
+  CONNECTION_UNLOCK (pending->connection);
+#endif
+  
+  return pending;
+}
+
+/**
+ * Decrements the reference count on a pending call,
+ * freeing it if the count reaches 0.
+ *
+ * @param pending the pending call object
+ */
+void
+dbus_pending_call_unref (DBusPendingCall *pending)
+{
+  dbus_bool_t last_unref;
+
+  _dbus_return_if_fail (pending != NULL);
+
+  /* More efficient to use the connection lock instead of atomic
+   * int fallback if we lack atomic int decrement
+   */
+#ifdef DBUS_HAVE_ATOMIC_INT
+  last_unref = (_dbus_atomic_dec (&pending->refcount) == 1);
+#else
+  CONNECTION_LOCK (pending->connection);
+  _dbus_assert (pending->refcount.value > 0);
+  pending->refcount.value -= 1;
+  last_unref = pending->refcount.value == 0;
+  CONNECTION_UNLOCK (pending->connection);
+#endif
+  
+  if (last_unref)
+    _dbus_pending_call_last_unref(pending);
+}
+
+/**
+ * Sets a notification function to be called when the reply is
+ * received or the pending call times out.
+ *
+ * @param pending the pending call
+ * @param function notifier function
+ * @param user_data data to pass to notifier function
+ * @param free_user_data function to free the user data
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_pending_call_set_notify (DBusPendingCall              *pending,
+                              DBusPendingCallNotifyFunction function,
+                              void                         *user_data,
+                              DBusFreeFunction              free_user_data)
+{
+  _dbus_return_val_if_fail (pending != NULL, FALSE);
+
+  CONNECTION_LOCK (pending->connection);
+  
+  /* could invoke application code! */
+  if (!_dbus_pending_call_set_data_unlocked (pending, notify_user_data_slot,
+                                             user_data, free_user_data))
+    return FALSE;
+  
+  pending->function = function;
+
+  CONNECTION_UNLOCK (pending->connection);
+  
+  return TRUE;
+}
+
+/**
+ * Cancels the pending call, such that any reply or error received
+ * will just be ignored.  Drops the dbus library's internal reference
+ * to the #DBusPendingCall so will free the call if nobody else is
+ * holding a reference. However you usually get a reference from
+ * dbus_connection_send_with_reply() so probably your app owns a ref
+ * also.
+ *
+ * Note that canceling a pending call will <em>not</em> simulate a
+ * timed-out call; if a call times out, then a timeout error reply is
+ * received. If you cancel the call, no reply is received unless the
+ * the reply was already received before you canceled.
+ * 
+ * @param pending the pending call
+ */
+void
+dbus_pending_call_cancel (DBusPendingCall *pending)
+{
+  _dbus_return_if_fail (pending != NULL);
+
+  _dbus_connection_remove_pending_call (pending->connection,
+                                        pending);
+}
+
+/**
+ * Checks whether the pending call has received a reply
+ * yet, or not.
+ *
+ * @param pending the pending call
+ * @returns #TRUE if a reply has been received
+ */
+dbus_bool_t
+dbus_pending_call_get_completed (DBusPendingCall *pending)
+{
+  dbus_bool_t completed;
+  
+  _dbus_return_val_if_fail (pending != NULL, FALSE);
+
+  CONNECTION_LOCK (pending->connection);
+  completed = pending->completed;
+  CONNECTION_UNLOCK (pending->connection);
+
+  return completed;
+}
+
+/**
+ * Gets the reply, or returns #NULL if none has been received
+ * yet. Ownership of the reply message passes to the caller. This
+ * function can only be called once per pending call, since the reply
+ * message is tranferred to the caller.
+ * 
+ * @param pending the pending call
+ * @returns the reply message or #NULL.
+ */
+DBusMessage*
+dbus_pending_call_steal_reply (DBusPendingCall *pending)
+{
+  DBusMessage *message;
+  
+  _dbus_return_val_if_fail (pending != NULL, NULL);
+  _dbus_return_val_if_fail (pending->completed, NULL);
+  _dbus_return_val_if_fail (pending->reply != NULL, NULL);
+
+  CONNECTION_LOCK (pending->connection);
+  
+  message = pending->reply;
+  pending->reply = NULL;
+
+  CONNECTION_UNLOCK (pending->connection);
+  
+  return message;
+}
+
+/**
+ * Block until the pending call is completed.  The blocking is as with
+ * dbus_connection_send_with_reply_and_block(); it does not enter the
+ * main loop or process other messages, it simply waits for the reply
+ * in question.
+ *
+ * If the pending call is already completed, this function returns
+ * immediately.
+ *
+ * @todo when you start blocking, the timeout is reset, but it should
+ * really only use time remaining since the pending call was created.
+ * This requires storing timestamps instead of intervals in the timeout
+ *
+ * @param pending the pending call
+ */
+void
+dbus_pending_call_block (DBusPendingCall *pending)
+{
+  _dbus_return_if_fail (pending != NULL);
+
+  _dbus_connection_block_pending_call (pending);
+}
+
+/**
+ * Allocates an integer ID to be used for storing application-specific
+ * data on any DBusPendingCall. The allocated ID may then be used
+ * with dbus_pending_call_set_data() and dbus_pending_call_get_data().
+ * The passed-in slot must be initialized to -1, and is filled in
+ * with the slot ID. If the passed-in slot is not -1, it's assumed
+ * to be already allocated, and its refcount is incremented.
+ * 
+ * The allocated slot is global, i.e. all DBusPendingCall objects will
+ * have a slot with the given integer ID reserved.
+ *
+ * @param slot_p address of a global variable storing the slot
+ * @returns #FALSE on failure (no memory)
+ */
+dbus_bool_t
+dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p)
+{
+  _dbus_return_val_if_fail (slot_p != NULL, FALSE);
+
+  return _dbus_data_slot_allocator_alloc (&slot_allocator,
+                                          &_DBUS_LOCK_NAME (pending_call_slots),
+                                          slot_p);
+}
+
+/**
+ * Deallocates a global ID for #DBusPendingCall data slots.
+ * dbus_pending_call_get_data() and dbus_pending_call_set_data() may
+ * no longer be used with this slot.  Existing data stored on existing
+ * DBusPendingCall objects will be freed when the #DBusPendingCall is
+ * finalized, but may not be retrieved (and may only be replaced if
+ * someone else reallocates the slot).  When the refcount on the
+ * passed-in slot reaches 0, it is set to -1.
+ *
+ * @param slot_p address storing the slot to deallocate
+ */
+void
+dbus_pending_call_free_data_slot (dbus_int32_t *slot_p)
+{
+  _dbus_return_if_fail (slot_p != NULL);
+  _dbus_return_if_fail (*slot_p >= 0);
+
+  _dbus_data_slot_allocator_free (&slot_allocator, slot_p);
+}
+
+/**
+ * Stores a pointer on a #DBusPendingCall, along
+ * with an optional function to be used for freeing
+ * the data when the data is set again, or when
+ * the pending call is finalized. The slot number
+ * must have been allocated with dbus_pending_call_allocate_data_slot().
+ *
+ * @param pending the pending_call
+ * @param slot the slot number
+ * @param data the data to store
+ * @param free_data_func finalizer function for the data
+ * @returns #TRUE if there was enough memory to store the data
+ */
+dbus_bool_t
+dbus_pending_call_set_data (DBusPendingCall  *pending,
+                            dbus_int32_t      slot,
+                            void             *data,
+                            DBusFreeFunction  free_data_func)
+{
+  dbus_bool_t retval;
+  
+  _dbus_return_val_if_fail (pending != NULL, FALSE);
+  _dbus_return_val_if_fail (slot >= 0, FALSE);
+
+  
+  CONNECTION_LOCK (pending->connection);
+  retval = _dbus_pending_call_set_data_unlocked (pending, slot, data, free_data_func);
+  CONNECTION_UNLOCK (pending->connection);
+  return retval;
+}
+
+/**
+ * Retrieves data previously set with dbus_pending_call_set_data().
+ * The slot must still be allocated (must not have been freed).
+ *
+ * @param pending the pending_call
+ * @param slot the slot to get data from
+ * @returns the data, or #NULL if not found
+ */
+void*
+dbus_pending_call_get_data (DBusPendingCall   *pending,
+                            dbus_int32_t       slot)
+{
+  void *res;
+
+  _dbus_return_val_if_fail (pending != NULL, NULL);
+
+  CONNECTION_LOCK (pending->connection);
+  res = _dbus_data_slot_list_get (&slot_allocator,
+                                  &pending->slot_list,
+                                  slot);
+  CONNECTION_UNLOCK (pending->connection);
+
+  return res;
+}
+
+/** @} */
+
+#ifdef DBUS_BUILD_TESTS
+
+/**
+ * @ingroup DBusPendingCallInternals
+ * Unit test for DBusPendingCall.
+ *
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_pending_call_test (const char *test_data_dir)
+{  
+
+  return TRUE;
+}
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-pending-call.h b/src/dbus/dbus-pending-call.h
new file mode 100644 (file)
index 0000000..b49e08d
--- /dev/null
@@ -0,0 +1,65 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-pending-call.h Object representing a call in progress.
+ *
+ * Copyright (C) 2002, 2003 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_PENDING_CALL_H
+#define DBUS_PENDING_CALL_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-connection.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusPendingCall
+ * @{
+ */
+
+DBusPendingCall* dbus_pending_call_ref       (DBusPendingCall               *pending);
+void         dbus_pending_call_unref         (DBusPendingCall               *pending);
+dbus_bool_t  dbus_pending_call_set_notify    (DBusPendingCall               *pending,
+                                              DBusPendingCallNotifyFunction  function,
+                                              void                          *user_data,
+                                              DBusFreeFunction               free_user_data);
+void         dbus_pending_call_cancel        (DBusPendingCall               *pending);
+dbus_bool_t  dbus_pending_call_get_completed (DBusPendingCall               *pending);
+DBusMessage* dbus_pending_call_steal_reply   (DBusPendingCall               *pending);
+void         dbus_pending_call_block         (DBusPendingCall               *pending);
+
+dbus_bool_t dbus_pending_call_allocate_data_slot (dbus_int32_t     *slot_p);
+void        dbus_pending_call_free_data_slot     (dbus_int32_t     *slot_p);
+dbus_bool_t dbus_pending_call_set_data           (DBusPendingCall  *pending,
+                                                  dbus_int32_t      slot,
+                                                  void             *data,
+                                                  DBusFreeFunction  free_data_func);
+void*       dbus_pending_call_get_data           (DBusPendingCall  *pending,
+                                                  dbus_int32_t      slot);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_PENDING_CALL_H */
diff --git a/src/dbus/dbus-protocol.h b/src/dbus/dbus-protocol.h
new file mode 100644 (file)
index 0000000..814deae
--- /dev/null
@@ -0,0 +1,439 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-protocol.h  D-Bus protocol constants
+ *
+ * Copyright (C) 2002, 2003  CodeFactory AB
+ * Copyright (C) 2004, 2005 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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_PROTOCOL_H
+#define DBUS_PROTOCOL_H
+
+/* Don't include anything in here from anywhere else. It's
+ * intended for use by any random library.
+ */
+
+#ifdef  __cplusplus
+extern "C" {
+#if 0
+} /* avoids confusing emacs indentation */
+#endif
+#endif
+
+/* Normally docs are in .c files, but there isn't a .c file for this. */
+/**
+ * @defgroup DBusProtocol Protocol constants
+ * @ingroup  DBus
+ *
+ * @brief Defines constants which are part of the D-Bus protocol
+ *
+ * This header is intended for use by any library, not only libdbus.
+ *
+ * @{
+ */
+
+
+/* Message byte order */
+#define DBUS_LITTLE_ENDIAN ('l')  /**< Code marking LSB-first byte order in the wire protocol. */
+#define DBUS_BIG_ENDIAN    ('B')  /**< Code marking MSB-first byte order in the wire protocol. */
+
+/** Protocol version. */
+#define DBUS_MAJOR_PROTOCOL_VERSION 1
+
+/** Type code that is never equal to a legitimate type code */
+#define DBUS_TYPE_INVALID       ((int) '\0')
+/** #DBUS_TYPE_INVALID as a string literal instead of a int literal */
+#define DBUS_TYPE_INVALID_AS_STRING        "\0"
+
+/* Primitive types */
+/** Type code marking an 8-bit unsigned integer */
+#define DBUS_TYPE_BYTE          ((int) 'y')
+/** #DBUS_TYPE_BYTE as a string literal instead of a int literal */
+#define DBUS_TYPE_BYTE_AS_STRING           "y"
+/** Type code marking a boolean */
+#define DBUS_TYPE_BOOLEAN       ((int) 'b')
+/** #DBUS_TYPE_BOOLEAN as a string literal instead of a int literal */
+#define DBUS_TYPE_BOOLEAN_AS_STRING        "b"
+/** Type code marking a 16-bit signed integer */
+#define DBUS_TYPE_INT16         ((int) 'n')
+/** #DBUS_TYPE_INT16 as a string literal instead of a int literal */
+#define DBUS_TYPE_INT16_AS_STRING          "n"
+/** Type code marking a 16-bit unsigned integer */
+#define DBUS_TYPE_UINT16        ((int) 'q')
+/** #DBUS_TYPE_UINT16 as a string literal instead of a int literal */
+#define DBUS_TYPE_UINT16_AS_STRING         "q"
+/** Type code marking a 32-bit signed integer */
+#define DBUS_TYPE_INT32         ((int) 'i')
+/** #DBUS_TYPE_INT32 as a string literal instead of a int literal */
+#define DBUS_TYPE_INT32_AS_STRING          "i"
+/** Type code marking a 32-bit unsigned integer */
+#define DBUS_TYPE_UINT32        ((int) 'u')
+/** #DBUS_TYPE_UINT32 as a string literal instead of a int literal */
+#define DBUS_TYPE_UINT32_AS_STRING         "u"
+/** Type code marking a 64-bit signed integer */
+#define DBUS_TYPE_INT64         ((int) 'x')
+/** #DBUS_TYPE_INT64 as a string literal instead of a int literal */
+#define DBUS_TYPE_INT64_AS_STRING          "x"
+/** Type code marking a 64-bit unsigned integer */
+#define DBUS_TYPE_UINT64        ((int) 't')
+/** #DBUS_TYPE_UINT64 as a string literal instead of a int literal */
+#define DBUS_TYPE_UINT64_AS_STRING         "t"
+/** Type code marking an 8-byte double in IEEE 754 format */
+#define DBUS_TYPE_DOUBLE        ((int) 'd')
+/** #DBUS_TYPE_DOUBLE as a string literal instead of a int literal */
+#define DBUS_TYPE_DOUBLE_AS_STRING         "d"
+/** Type code marking a UTF-8 encoded, nul-terminated Unicode string */
+#define DBUS_TYPE_STRING        ((int) 's')
+/** #DBUS_TYPE_STRING as a string literal instead of a int literal */
+#define DBUS_TYPE_STRING_AS_STRING         "s"
+/** Type code marking a D-Bus object path */
+#define DBUS_TYPE_OBJECT_PATH   ((int) 'o')
+/** #DBUS_TYPE_OBJECT_PATH as a string literal instead of a int literal */
+#define DBUS_TYPE_OBJECT_PATH_AS_STRING    "o"
+/** Type code marking a D-Bus type signature */
+#define DBUS_TYPE_SIGNATURE     ((int) 'g')
+/** #DBUS_TYPE_SIGNATURE as a string literal instead of a int literal */
+#define DBUS_TYPE_SIGNATURE_AS_STRING      "g"
+
+/* Compound types */
+/** Type code marking a D-Bus array type */
+#define DBUS_TYPE_ARRAY         ((int) 'a')
+/** #DBUS_TYPE_ARRAY as a string literal instead of a int literal */
+#define DBUS_TYPE_ARRAY_AS_STRING          "a"
+/** Type code marking a D-Bus variant type */
+#define DBUS_TYPE_VARIANT       ((int) 'v')
+/** #DBUS_TYPE_VARIANT as a string literal instead of a int literal */
+#define DBUS_TYPE_VARIANT_AS_STRING        "v"
+
+/** STRUCT and DICT_ENTRY are sort of special since their codes can't
+ * appear in a type string, instead
+ * DBUS_STRUCT_BEGIN_CHAR/DBUS_DICT_ENTRY_BEGIN_CHAR have to appear
+ */
+/** Type code used to represent a struct; however, this type code does not appear
+ * in type signatures, instead #DBUS_STRUCT_BEGIN_CHAR and #DBUS_STRUCT_END_CHAR will
+ * appear in a signature.
+ */
+#define DBUS_TYPE_STRUCT        ((int) 'r')
+/** #DBUS_TYPE_STRUCT as a string literal instead of a int literal */
+#define DBUS_TYPE_STRUCT_AS_STRING         "r"
+/** Type code used to represent a dict entry; however, this type code does not appear
+ * in type signatures, instead #DBUS_DICT_ENTRY_BEGIN_CHAR and #DBUS_DICT_ENTRY_END_CHAR will
+ * appear in a signature.
+ */
+#define DBUS_TYPE_DICT_ENTRY    ((int) 'e')
+/** #DBUS_TYPE_DICT_ENTRY as a string literal instead of a int literal */
+#define DBUS_TYPE_DICT_ENTRY_AS_STRING     "e"
+
+/** Does not include #DBUS_TYPE_INVALID, #DBUS_STRUCT_BEGIN_CHAR, #DBUS_STRUCT_END_CHAR,
+ * #DBUS_DICT_ENTRY_BEGIN_CHAR, or #DBUS_DICT_ENTRY_END_CHAR - i.e. it is the number of
+ * valid types, not the number of distinct characters that may appear in a type signature.
+ */
+#define DBUS_NUMBER_OF_TYPES    (16)
+
+/* characters other than typecodes that appear in type signatures */
+
+/** Code marking the start of a struct type in a type signature */
+#define DBUS_STRUCT_BEGIN_CHAR   ((int) '(')
+/** #DBUS_STRUCT_BEGIN_CHAR as a string literal instead of a int literal */
+#define DBUS_STRUCT_BEGIN_CHAR_AS_STRING   "("
+/** Code marking the end of a struct type in a type signature */
+#define DBUS_STRUCT_END_CHAR     ((int) ')')
+/** #DBUS_STRUCT_END_CHAR a string literal instead of a int literal */
+#define DBUS_STRUCT_END_CHAR_AS_STRING     ")"
+/** Code marking the start of a dict entry type in a type signature */
+#define DBUS_DICT_ENTRY_BEGIN_CHAR   ((int) '{')
+/** #DBUS_DICT_ENTRY_BEGIN_CHAR as a string literal instead of a int literal */
+#define DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING   "{"
+/** Code marking the end of a dict entry type in a type signature */
+#define DBUS_DICT_ENTRY_END_CHAR     ((int) '}')
+/** #DBUS_DICT_ENTRY_END_CHAR as a string literal instead of a int literal */
+#define DBUS_DICT_ENTRY_END_CHAR_AS_STRING     "}"
+
+/** Max length in bytes of a bus name, interface, or member (not object
+ * path, paths are unlimited). This is limited because lots of stuff
+ * is O(n) in this number, plus it would be obnoxious to type in a
+ * paragraph-long method name so most likely something like that would
+ * be an exploit.
+ */
+#define DBUS_MAXIMUM_NAME_LENGTH 255
+
+/** This one is 255 so it fits in a byte */
+#define DBUS_MAXIMUM_SIGNATURE_LENGTH 255
+
+/** Max length of a match rule string; to keep people from hosing the
+ * daemon with some huge rule
+ */
+#define DBUS_MAXIMUM_MATCH_RULE_LENGTH 1024
+
+/** Max arg number you can match on in a match rule, e.g.
+ * arg0='hello' is OK, arg3489720987='hello' is not
+ */
+#define DBUS_MAXIMUM_MATCH_RULE_ARG_NUMBER 63
+  
+/** Max length of a marshaled array in bytes (64M, 2^26) We use signed
+ * int for lengths so must be INT_MAX or less.  We need something a
+ * bit smaller than INT_MAX because the array is inside a message with
+ * header info, etc.  so an INT_MAX array wouldn't allow the message
+ * overhead.  The 64M number is an attempt at a larger number than
+ * we'd reasonably ever use, but small enough that your bus would chew
+ * through it fairly quickly without locking up forever. If you have
+ * data that's likely to be larger than this, you should probably be
+ * sending it in multiple incremental messages anyhow.
+ */
+#define DBUS_MAXIMUM_ARRAY_LENGTH (67108864)
+/** Number of bits you need in an unsigned to store the max array size */
+#define DBUS_MAXIMUM_ARRAY_LENGTH_BITS 26
+
+/** The maximum total message size including header and body; similar
+ * rationale to max array size.
+ */
+#define DBUS_MAXIMUM_MESSAGE_LENGTH (DBUS_MAXIMUM_ARRAY_LENGTH * 2)
+/** Number of bits you need in an unsigned to store the max message size */
+#define DBUS_MAXIMUM_MESSAGE_LENGTH_BITS 27
+
+/** Depth of recursion in the type tree. This is automatically limited
+ * to DBUS_MAXIMUM_SIGNATURE_LENGTH since you could only have an array
+ * of array of array of ... that fit in the max signature.  But that's
+ * probably a bit too large.
+ */
+#define DBUS_MAXIMUM_TYPE_RECURSION_DEPTH 32
+
+/* Types of message */
+
+/** This value is never a valid message type, see dbus_message_get_type() */
+#define DBUS_MESSAGE_TYPE_INVALID       0
+/** Message type of a method call message, see dbus_message_get_type() */
+#define DBUS_MESSAGE_TYPE_METHOD_CALL   1
+/** Message type of a method return message, see dbus_message_get_type() */
+#define DBUS_MESSAGE_TYPE_METHOD_RETURN 2
+/** Message type of an error reply message, see dbus_message_get_type() */
+#define DBUS_MESSAGE_TYPE_ERROR         3
+/** Message type of a signal message, see dbus_message_get_type() */
+#define DBUS_MESSAGE_TYPE_SIGNAL        4
+
+/* Header flags */
+
+/** If set, this flag means that the sender of a message does not care about getting
+ * a reply, so the recipient need not send one. See dbus_message_set_no_reply().
+ */
+#define DBUS_HEADER_FLAG_NO_REPLY_EXPECTED 0x1
+/**
+ * If set, this flag means that even if the message bus knows how to start an owner for
+ * the destination bus name (see dbus_message_set_destination()), it should not
+ * do so. If this flag is not set, the bus may launch a program to process the
+ * message.
+ */
+#define DBUS_HEADER_FLAG_NO_AUTO_START     0x2
+
+/* Header fields */
+
+/** Not equal to any valid header field code */
+#define DBUS_HEADER_FIELD_INVALID        0
+/** Header field code for the path - the path is the object emitting a signal or the object receiving a method call.
+ * See dbus_message_set_path().
+ */
+#define DBUS_HEADER_FIELD_PATH           1
+/** Header field code for the interface containing a member (method or signal).
+ * See dbus_message_set_interface().
+ */
+#define DBUS_HEADER_FIELD_INTERFACE      2
+/** Header field code for a member (method or signal). See dbus_message_set_member(). */
+#define DBUS_HEADER_FIELD_MEMBER         3
+/** Header field code for an error name (found in #DBUS_MESSAGE_TYPE_ERROR messages).
+ * See dbus_message_set_error_name().
+ */
+#define DBUS_HEADER_FIELD_ERROR_NAME     4
+/** Header field code for a reply serial, used to match a #DBUS_MESSAGE_TYPE_METHOD_RETURN message with the
+ * message that it's a reply to. See dbus_message_set_reply_serial().
+ */
+#define DBUS_HEADER_FIELD_REPLY_SERIAL   5
+/**
+ * Header field code for the destination bus name of a message. See dbus_message_set_destination().
+ */
+#define DBUS_HEADER_FIELD_DESTINATION    6
+/**
+ * Header field code for the sender of a message; usually initialized by the message bus.
+ * See dbus_message_set_sender().
+ */
+#define DBUS_HEADER_FIELD_SENDER         7
+/**
+ * Header field code for the type signature of a message.
+ */
+#define DBUS_HEADER_FIELD_SIGNATURE      8
+
+/**
+ * Value of the highest-numbered header field code, can be used to determine
+ * the size of an array indexed by header field code. Remember though
+ * that unknown codes must be ignored, so check for that before
+ * indexing the array.
+ */
+#define DBUS_HEADER_FIELD_LAST DBUS_HEADER_FIELD_SIGNATURE
+
+/** Header format is defined as a signature:
+ *   byte                            byte order
+ *   byte                            message type ID
+ *   byte                            flags
+ *   byte                            protocol version
+ *   uint32                          body length
+ *   uint32                          serial
+ *   array of struct (byte,variant)  (field name, value)
+ *
+ * The length of the header can be computed as the
+ * fixed size of the initial data, plus the length of
+ * the array at the end, plus padding to an 8-boundary.
+ */
+#define DBUS_HEADER_SIGNATURE                   \
+     DBUS_TYPE_BYTE_AS_STRING                   \
+     DBUS_TYPE_BYTE_AS_STRING                   \
+     DBUS_TYPE_BYTE_AS_STRING                   \
+     DBUS_TYPE_BYTE_AS_STRING                   \
+     DBUS_TYPE_UINT32_AS_STRING                 \
+     DBUS_TYPE_UINT32_AS_STRING                 \
+     DBUS_TYPE_ARRAY_AS_STRING                  \
+     DBUS_STRUCT_BEGIN_CHAR_AS_STRING           \
+     DBUS_TYPE_BYTE_AS_STRING                   \
+     DBUS_TYPE_VARIANT_AS_STRING                \
+     DBUS_STRUCT_END_CHAR_AS_STRING
+
+
+/**
+ * The smallest header size that can occur.  (It won't be valid due to
+ * missing required header fields.) This is 4 bytes, two uint32, an
+ * array length. This isn't any kind of resource limit, just the
+ * necessary/logical outcome of the header signature.
+ */
+#define DBUS_MINIMUM_HEADER_SIZE 16
+
+/* Errors */
+/* WARNING these get autoconverted to an enum in dbus-glib.h. Thus,
+ * if you change the order it breaks the ABI. Keep them in order.
+ * Also, don't change the formatting since that will break the sed
+ * script.
+ */
+/** A generic error; "something went wrong" - see the error message for more. */
+#define DBUS_ERROR_FAILED                     "org.freedesktop.DBus.Error.Failed"
+/** There was not enough memory to complete an operation. */
+#define DBUS_ERROR_NO_MEMORY                  "org.freedesktop.DBus.Error.NoMemory"
+/** The bus doesn't know how to launch a service to supply the bus name you wanted. */
+#define DBUS_ERROR_SERVICE_UNKNOWN            "org.freedesktop.DBus.Error.ServiceUnknown"
+/** The bus name you referenced doesn't exist (i.e. no application owns it). */
+#define DBUS_ERROR_NAME_HAS_NO_OWNER          "org.freedesktop.DBus.Error.NameHasNoOwner"
+/** No reply to a message expecting one, usually means a timeout occurred. */
+#define DBUS_ERROR_NO_REPLY                   "org.freedesktop.DBus.Error.NoReply"
+/** Something went wrong reading or writing to a socket, for example. */
+#define DBUS_ERROR_IO_ERROR                   "org.freedesktop.DBus.Error.IOError"
+/** A D-Bus bus address was malformed. */
+#define DBUS_ERROR_BAD_ADDRESS                "org.freedesktop.DBus.Error.BadAddress"
+/** Requested operation isn't supported (like ENOSYS on UNIX). */
+#define DBUS_ERROR_NOT_SUPPORTED              "org.freedesktop.DBus.Error.NotSupported"
+/** Some limited resource is exhausted. */
+#define DBUS_ERROR_LIMITS_EXCEEDED            "org.freedesktop.DBus.Error.LimitsExceeded"
+/** Security restrictions don't allow doing what you're trying to do. */
+#define DBUS_ERROR_ACCESS_DENIED              "org.freedesktop.DBus.Error.AccessDenied"
+/** Authentication didn't work. */
+#define DBUS_ERROR_AUTH_FAILED                "org.freedesktop.DBus.Error.AuthFailed"
+/** Unable to connect to server (probably caused by ECONNREFUSED on a socket). */
+#define DBUS_ERROR_NO_SERVER                  "org.freedesktop.DBus.Error.NoServer"
+/** Certain timeout errors, possibly ETIMEDOUT on a socket.
+ * Note that #DBUS_ERROR_NO_REPLY is used for message reply timeouts.
+ * @warning this is confusingly-named given that #DBUS_ERROR_TIMED_OUT also exists. We can't fix
+ * it for compatibility reasons so just be careful.
+ */
+#define DBUS_ERROR_TIMEOUT                    "org.freedesktop.DBus.Error.Timeout"
+/** No network access (probably ENETUNREACH on a socket). */
+#define DBUS_ERROR_NO_NETWORK                 "org.freedesktop.DBus.Error.NoNetwork"
+/** Can't bind a socket since its address is in use (i.e. EADDRINUSE). */
+#define DBUS_ERROR_ADDRESS_IN_USE             "org.freedesktop.DBus.Error.AddressInUse"
+/** The connection is disconnected and you're trying to use it. */
+#define DBUS_ERROR_DISCONNECTED               "org.freedesktop.DBus.Error.Disconnected"
+/** Invalid arguments passed to a method call. */
+#define DBUS_ERROR_INVALID_ARGS               "org.freedesktop.DBus.Error.InvalidArgs"
+/** Missing file. */
+#define DBUS_ERROR_FILE_NOT_FOUND             "org.freedesktop.DBus.Error.FileNotFound"
+/** Existing file and the operation you're using does not silently overwrite. */
+#define DBUS_ERROR_FILE_EXISTS                "org.freedesktop.DBus.Error.FileExists"
+/** Method name you invoked isn't known by the object you invoked it on. */
+#define DBUS_ERROR_UNKNOWN_METHOD             "org.freedesktop.DBus.Error.UnknownMethod"
+/** Certain timeout errors, e.g. while starting a service.
+ * @warning this is confusingly-named given that #DBUS_ERROR_TIMEOUT also exists. We can't fix
+ * it for compatibility reasons so just be careful.
+ */
+#define DBUS_ERROR_TIMED_OUT                  "org.freedesktop.DBus.Error.TimedOut"
+/** Tried to remove or modify a match rule that didn't exist. */
+#define DBUS_ERROR_MATCH_RULE_NOT_FOUND       "org.freedesktop.DBus.Error.MatchRuleNotFound"
+/** The match rule isn't syntactically valid. */
+#define DBUS_ERROR_MATCH_RULE_INVALID         "org.freedesktop.DBus.Error.MatchRuleInvalid"
+/** While starting a new process, the exec() call failed. */
+#define DBUS_ERROR_SPAWN_EXEC_FAILED          "org.freedesktop.DBus.Error.Spawn.ExecFailed"
+/** While starting a new process, the fork() call failed. */
+#define DBUS_ERROR_SPAWN_FORK_FAILED          "org.freedesktop.DBus.Error.Spawn.ForkFailed"
+/** While starting a new process, the child exited with a status code. */
+#define DBUS_ERROR_SPAWN_CHILD_EXITED         "org.freedesktop.DBus.Error.Spawn.ChildExited"
+/** While starting a new process, the child exited on a signal. */
+#define DBUS_ERROR_SPAWN_CHILD_SIGNALED       "org.freedesktop.DBus.Error.Spawn.ChildSignaled"
+/** While starting a new process, something went wrong. */
+#define DBUS_ERROR_SPAWN_FAILED               "org.freedesktop.DBus.Error.Spawn.Failed"
+/** We failed to setup the environment correctly. */
+#define DBUS_ERROR_SPAWN_SETUP_FAILED         "org.freedesktop.DBus.Error.Spawn.FailedToSetup"
+/** We failed to setup the config parser correctly. */
+#define DBUS_ERROR_SPAWN_CONFIG_INVALID       "org.freedesktop.DBus.Error.Spawn.ConfigInvalid"
+/** Bus name was not valid. */
+#define DBUS_ERROR_SPAWN_SERVICE_INVALID      "org.freedesktop.DBus.Error.Spawn.ServiceNotValid"
+/** Service file not found in system-services directory. */
+#define DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND    "org.freedesktop.DBus.Error.Spawn.ServiceNotFound"
+/** Permissions are incorrect on the setuid helper. */
+#define DBUS_ERROR_SPAWN_PERMISSIONS_INVALID  "org.freedesktop.DBus.Error.Spawn.PermissionsInvalid"
+/** Service file invalid (Name, User or Exec missing). */
+#define DBUS_ERROR_SPAWN_FILE_INVALID         "org.freedesktop.DBus.Error.Spawn.FileInvalid"
+/** Tried to get a UNIX process ID and it wasn't available. */
+#define DBUS_ERROR_SPAWN_NO_MEMORY            "org.freedesktop.DBus.Error.Spawn.NoMemory"
+/** Tried to get a UNIX process ID and it wasn't available. */
+#define DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN    "org.freedesktop.DBus.Error.UnixProcessIdUnknown"
+/** A type signature is not valid. */
+#define DBUS_ERROR_INVALID_SIGNATURE          "org.freedesktop.DBus.Error.InvalidSignature"
+/** A file contains invalid syntax or is otherwise broken. */
+#define DBUS_ERROR_INVALID_FILE_CONTENT       "org.freedesktop.DBus.Error.InvalidFileContent"
+/** Asked for SELinux security context and it wasn't available. */
+#define DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN    "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown"
+/** Asked for ADT audit data and it wasn't available. */
+#define DBUS_ERROR_ADT_AUDIT_DATA_UNKNOWN     "org.freedesktop.DBus.Error.AdtAuditDataUnknown"
+/** There's already an object with the requested object path. */
+#define DBUS_ERROR_OBJECT_PATH_IN_USE         "org.freedesktop.DBus.Error.ObjectPathInUse"
+
+/* XML introspection format */
+
+/** XML namespace of the introspection format version 1.0 */
+#define DBUS_INTROSPECT_1_0_XML_NAMESPACE         "http://www.freedesktop.org/standards/dbus"
+/** XML public identifier of the introspection format version 1.0 */
+#define DBUS_INTROSPECT_1_0_XML_PUBLIC_IDENTIFIER "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+/** XML system identifier of the introspection format version 1.0 */
+#define DBUS_INTROSPECT_1_0_XML_SYSTEM_IDENTIFIER "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"
+/** XML document type declaration of the introspection format version 1.0 */
+#define DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<!DOCTYPE node PUBLIC \""DBUS_INTROSPECT_1_0_XML_PUBLIC_IDENTIFIER"\"\n\""DBUS_INTROSPECT_1_0_XML_SYSTEM_IDENTIFIER"\">\n"
+
+/** @} */
+
+#ifdef __cplusplus
+#if 0
+{ /* avoids confusing emacs indentation */
+#endif
+}
+#endif
+
+#endif /* DBUS_PROTOCOL_H */
diff --git a/src/dbus/dbus-resources.c b/src/dbus/dbus-resources.c
new file mode 100644 (file)
index 0000000..5ff1622
--- /dev/null
@@ -0,0 +1,194 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-resources.c Resource tracking/limits
+ *
+ * Copyright (C) 2003  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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/dbus-resources.h>
+#include <dbus/dbus-internals.h>
+
+/**
+ * @defgroup DBusResources Resource limits related code
+ * @ingroup  DBusInternals
+ * @brief DBusCounter and other stuff related to resource limits
+ *
+ * Types and functions related to tracking resource limits,
+ * such as the maximum amount of memory a connection can use
+ * for messages, etc.
+ */
+
+/**
+ * @defgroup DBusResourcesInternals Resource limits implementation details
+ * @ingroup  DBusInternals
+ * @brief Resource limits implementation details
+ *
+ * Implementation details of resource limits code.
+ *
+ * @{
+ */
+
+/**
+ * @brief Internals of DBusCounter.
+ * 
+ * DBusCounter internals. DBusCounter is an opaque object, it must be
+ * used via accessor functions.
+ */
+struct DBusCounter
+{
+  int refcount;  /**< reference count */
+
+  long value;    /**< current counter value */
+
+  long notify_guard_value; /**< call notify function when crossing this value */
+  DBusCounterNotifyFunction notify_function; /**< notify function */
+  void *notify_data; /**< data for notify function */
+};
+
+/** @} */  /* end of resource limits internals docs */
+
+/**
+ * @addtogroup DBusResources
+ * @{
+ */
+
+/**
+ * Creates a new DBusCounter. DBusCounter is used
+ * to count usage of some resource such as memory.
+ *
+ * @returns new counter or #NULL on failure
+ */
+DBusCounter*
+_dbus_counter_new (void)
+{
+  DBusCounter *counter;
+
+  counter = dbus_new (DBusCounter, 1);
+  if (counter == NULL)
+    return NULL;
+  
+  counter->refcount = 1;
+  counter->value = 0;
+
+  counter->notify_guard_value = 0;
+  counter->notify_function = NULL;
+  counter->notify_data = NULL;
+  
+  return counter;
+}
+
+/**
+ * Increments refcount of the counter
+ *
+ * @param counter the counter
+ * @returns the counter
+ */
+DBusCounter *
+_dbus_counter_ref (DBusCounter *counter)
+{
+  _dbus_assert (counter->refcount > 0);
+  
+  counter->refcount += 1;
+
+  return counter;
+}
+
+/**
+ * Decrements refcount of the counter and possibly
+ * finalizes the counter.
+ *
+ * @param counter the counter
+ */
+void
+_dbus_counter_unref (DBusCounter *counter)
+{
+  _dbus_assert (counter->refcount > 0);
+
+  counter->refcount -= 1;
+
+  if (counter->refcount == 0)
+    {
+      
+      dbus_free (counter);
+    }
+}
+
+/**
+ * Adjusts the value of the counter by the given
+ * delta which may be positive or negative.
+ * Calls the notify function from _dbus_counter_set_notify()
+ * if that function has been specified.
+ *
+ * @param counter the counter
+ * @param delta value to add to the counter's current value
+ */
+void
+_dbus_counter_adjust (DBusCounter *counter,
+                      long         delta)
+{
+  long old = counter->value;
+  
+  counter->value += delta;
+
+#if 0
+  _dbus_verbose ("Adjusting counter %ld by %ld = %ld\n",
+                 old, delta, counter->value);
+#endif
+  
+  if (counter->notify_function != NULL &&
+      ((old < counter->notify_guard_value &&
+        counter->value >= counter->notify_guard_value) ||
+       (old >= counter->notify_guard_value &&
+        counter->value < counter->notify_guard_value)))
+    (* counter->notify_function) (counter, counter->notify_data);
+}
+
+/**
+ * Gets the current value of the counter.
+ *
+ * @param counter the counter
+ * @returns its current value
+ */
+long
+_dbus_counter_get_value (DBusCounter *counter)
+{
+  return counter->value;
+}
+
+/**
+ * Sets the notify function for this counter; the notify function is
+ * called whenever the counter's value crosses the guard value in
+ * either direction (moving up, or moving down).
+ *
+ * @param counter the counter
+ * @param guard_value the value we're notified if the counter crosses
+ * @param function function to call in order to notify
+ * @param user_data data to pass to the function
+ */
+void
+_dbus_counter_set_notify (DBusCounter               *counter,
+                          long                       guard_value,
+                          DBusCounterNotifyFunction  function,
+                          void                      *user_data)
+{
+  counter->notify_guard_value = guard_value;
+  counter->notify_function = function;
+  counter->notify_data = user_data;
+}
+
+/** @} */  /* end of resource limits exported API */
diff --git a/src/dbus/dbus-resources.h b/src/dbus/dbus-resources.h
new file mode 100644 (file)
index 0000000..7b6e0d4
--- /dev/null
@@ -0,0 +1,52 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-resources.h Resource tracking/limits
+ *
+ * Copyright (C) 2003  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_RESOURCES_H
+#define DBUS_RESOURCES_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-connection.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusCounter DBusCounter;
+
+typedef void (* DBusCounterNotifyFunction) (DBusCounter *counter,
+                                            void        *user_data);
+
+DBusCounter* _dbus_counter_new       (void);
+DBusCounter* _dbus_counter_ref       (DBusCounter *counter);
+void         _dbus_counter_unref     (DBusCounter *counter);
+void         _dbus_counter_adjust    (DBusCounter *counter,
+                                      long         delta);
+long         _dbus_counter_get_value (DBusCounter *counter);
+
+void _dbus_counter_set_notify (DBusCounter               *counter,
+                               long                       guard_value,
+                               DBusCounterNotifyFunction  function,
+                               void                      *user_data);
+
+
+DBUS_END_DECLS
+
+#endif /* DBUS_RESOURCES_H */
diff --git a/src/dbus/dbus-server-debug-pipe.c b/src/dbus/dbus-server-debug-pipe.c
new file mode 100644 (file)
index 0000000..24b0ce3
--- /dev/null
@@ -0,0 +1,432 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-server-debug-pipe.c In-proc debug server implementation 
+ *
+ * Copyright (C) 2003  CodeFactory AB
+ * Copyright (C) 2003, 2004  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-internals.h"
+#include "dbus-server-debug-pipe.h"
+#include "dbus-transport-socket.h"
+#include "dbus-connection-internal.h"
+#include "dbus-hash.h"
+#include "dbus-string.h"
+#include "dbus-protocol.h"
+
+#ifdef DBUS_BUILD_TESTS
+
+/**
+ * @defgroup DBusServerDebugPipe DBusServerDebugPipe
+ * @ingroup  DBusInternals
+ * @brief In-process pipe debug server used in unit tests.
+ *
+ * Types and functions related to DBusServerDebugPipe.
+ * This is used for unit testing.
+ *
+ * @{
+ */
+
+/**
+ * Opaque object representing a debug server implementation.
+ */
+typedef struct DBusServerDebugPipe DBusServerDebugPipe;
+
+/**
+ * Implementation details of DBusServerDebugPipe. All members
+ * are private.
+ */
+struct DBusServerDebugPipe
+{
+  DBusServer base;  /**< Parent class members. */
+
+  char *name; /**< Server name. */
+
+  dbus_bool_t disconnected; /**< TRUE if disconnect has been called */
+};
+
+/* FIXME not threadsafe (right now the test suite doesn't use threads anyhow ) */
+static DBusHashTable *server_pipe_hash;
+static int server_pipe_hash_refcount = 0;
+
+static dbus_bool_t
+pipe_hash_ref (void)
+{
+  if (!server_pipe_hash)
+    {
+      _dbus_assert (server_pipe_hash_refcount == 0);
+      
+      server_pipe_hash = _dbus_hash_table_new (DBUS_HASH_STRING, NULL, NULL);
+
+      if (!server_pipe_hash)
+        return FALSE;
+    }
+
+  server_pipe_hash_refcount = 1;
+
+  return TRUE;
+}
+
+static void
+pipe_hash_unref (void)
+{
+  _dbus_assert (server_pipe_hash != NULL);
+  _dbus_assert (server_pipe_hash_refcount > 0);
+
+  server_pipe_hash_refcount -= 1;
+  if (server_pipe_hash_refcount == 0)
+    {
+      _dbus_hash_table_unref (server_pipe_hash);
+      server_pipe_hash = NULL;
+    }
+}
+
+static void
+debug_finalize (DBusServer *server)
+{
+  DBusServerDebugPipe *debug_server = (DBusServerDebugPipe*) server;
+
+  pipe_hash_unref ();
+  
+  _dbus_server_finalize_base (server);
+
+  dbus_free (debug_server->name);
+  dbus_free (server);
+}
+
+static void
+debug_disconnect (DBusServer *server)
+{
+  ((DBusServerDebugPipe*)server)->disconnected = TRUE;
+}
+
+static DBusServerVTable debug_vtable = {
+  debug_finalize,
+  debug_disconnect
+};
+
+/**
+ * Creates a new debug server using an in-process pipe
+ *
+ * @param server_name the name of the server.
+ * @param error address where an error can be returned.
+ * @returns a new server, or #NULL on failure.
+ */
+DBusServer*
+_dbus_server_debug_pipe_new (const char     *server_name,
+                             DBusError      *error)
+{
+  DBusServerDebugPipe *debug_server;
+  DBusString address;
+  DBusString name_str;
+  
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  if (!pipe_hash_ref ())
+    return NULL;
+  
+  if (_dbus_hash_table_lookup_string (server_pipe_hash, server_name) != NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_ADDRESS_IN_USE, NULL);
+      pipe_hash_unref ();
+      return NULL;
+    }
+  
+  debug_server = dbus_new0 (DBusServerDebugPipe, 1);
+  if (debug_server == NULL)
+    goto nomem_0;
+
+  if (!_dbus_string_init (&address))
+    goto nomem_1;
+
+  _dbus_string_init_const (&name_str, server_name);
+  if (!_dbus_string_append (&address, "debug-pipe:name=") ||
+      !_dbus_address_append_escaped (&address, &name_str))
+    goto nomem_2;
+  
+  debug_server->name = _dbus_strdup (server_name);
+  if (debug_server->name == NULL)
+    goto nomem_2;
+  
+  if (!_dbus_server_init_base (&debug_server->base,
+                              &debug_vtable, &address))
+    goto nomem_3;
+
+  if (!_dbus_hash_table_insert_string (server_pipe_hash,
+                                      debug_server->name,
+                                      debug_server))
+    goto nomem_4;
+
+  _dbus_string_free (&address);
+
+  /* server keeps the pipe hash ref */
+  
+  return (DBusServer *)debug_server;
+
+ nomem_4:
+  _dbus_server_finalize_base (&debug_server->base);
+ nomem_3:
+  dbus_free (debug_server->name);
+ nomem_2:
+  _dbus_string_free (&address);
+ nomem_1:
+  dbus_free (debug_server);
+ nomem_0:
+  pipe_hash_unref ();
+  dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+  return NULL;
+}
+
+/**
+ * Creates the client-side transport for
+ * a debug-pipe connection connected to the
+ * given debug-pipe server name.
+ * 
+ * @param server_name name of server to connect to
+ * @param error address where an error can be returned.
+ * @returns #NULL on no memory or transport
+ */
+DBusTransport*
+_dbus_transport_debug_pipe_new (const char     *server_name,
+                                DBusError      *error)
+{
+  DBusTransport *client_transport;
+  DBusTransport *server_transport;
+  DBusConnection *connection;
+  int client_fd, server_fd;
+  DBusServer *server;
+  DBusString address;
+  
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  if (server_pipe_hash == NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_SERVER, NULL);
+      return NULL;
+    }
+  
+  server = _dbus_hash_table_lookup_string (server_pipe_hash,
+                                           server_name);
+  if (server == NULL ||
+      ((DBusServerDebugPipe*)server)->disconnected)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_SERVER, NULL);
+      return NULL;
+    }
+
+  if (!_dbus_string_init (&address))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      return NULL;
+    }
+
+  if (!_dbus_string_append (&address, "debug-pipe:name=") ||
+      !_dbus_string_append (&address, server_name))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      _dbus_string_free (&address);
+      return NULL;
+    }
+  
+  if (!_dbus_full_duplex_pipe (&client_fd, &server_fd, FALSE,
+                               NULL))
+    {
+      _dbus_verbose ("failed to create full duplex pipe\n");
+      dbus_set_error (error, DBUS_ERROR_FAILED, "Could not create full-duplex pipe");
+      _dbus_string_free (&address);
+      return NULL;
+    }
+
+  _dbus_fd_set_close_on_exec (client_fd);
+  _dbus_fd_set_close_on_exec (server_fd);
+  
+  client_transport = _dbus_transport_new_for_socket (client_fd,
+                                                     NULL, &address);
+  if (client_transport == NULL)
+    {
+      _dbus_close_socket (client_fd, NULL);
+      _dbus_close_socket (server_fd, NULL);
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      _dbus_string_free (&address);
+      return NULL;
+    }
+
+  _dbus_string_free (&address);
+  
+  client_fd = -1;
+
+  server_transport = _dbus_transport_new_for_socket (server_fd,
+                                                     &server->guid_hex, NULL);
+  if (server_transport == NULL)
+    {
+      _dbus_transport_unref (client_transport);
+      _dbus_close_socket (server_fd, NULL);
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      return NULL;
+    }
+
+  server_fd = -1;
+
+  if (!_dbus_transport_set_auth_mechanisms (server_transport,
+                                            (const char**) server->auth_mechanisms))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      _dbus_transport_unref (server_transport);
+      _dbus_transport_unref (client_transport);
+      return NULL;
+    }
+  
+  connection = _dbus_connection_new_for_transport (server_transport);
+  _dbus_transport_unref (server_transport);
+  server_transport = NULL;
+  
+  if (connection == NULL)
+    {
+      _dbus_transport_unref (client_transport);
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      return NULL;
+    }
+
+  /* See if someone wants to handle this new connection,
+   * self-referencing for paranoia
+   */
+  if (server->new_connection_function)
+    {
+      dbus_server_ref (server);
+      (* server->new_connection_function) (server, connection,
+                                           server->new_connection_data);
+      dbus_server_unref (server);
+    }
+  
+  /* If no one grabbed a reference, the connection will die,
+   * and the client transport will get an immediate disconnect
+   */
+  _dbus_connection_close_if_only_one_ref (connection);
+  dbus_connection_unref (connection);
+
+  return client_transport;
+}
+
+/**
+ * Tries to interpret the address entry as a debug pipe entry.
+ * 
+ * Sets error if the result is not OK.
+ * 
+ * @param entry an address entry
+ * @param server_p location to store a new DBusServer, or #NULL on failure.
+ * @param error location to store rationale for failure on bad address
+ * @returns the outcome
+ * 
+ */
+DBusServerListenResult
+_dbus_server_listen_debug_pipe (DBusAddressEntry *entry,
+                                DBusServer      **server_p,
+                                DBusError        *error)
+{
+  const char *method;
+
+  *server_p = NULL;
+  
+  method = dbus_address_entry_get_method (entry);
+  
+  if (strcmp (method, "debug-pipe") == 0)
+    {
+      const char *name = dbus_address_entry_get_value (entry, "name");
+      
+      if (name == NULL)
+        {
+          _dbus_set_bad_address(error, "debug-pipe", "name",
+                                NULL);
+          return DBUS_SERVER_LISTEN_BAD_ADDRESS;
+        }
+
+      *server_p = _dbus_server_debug_pipe_new (name, error);
+      
+      if (*server_p)
+        {
+          _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+          return DBUS_SERVER_LISTEN_OK;
+        }
+      else
+        {
+          _DBUS_ASSERT_ERROR_IS_SET(error);
+          return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
+        }
+    }
+  else
+    {
+      _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+      return DBUS_SERVER_LISTEN_NOT_HANDLED;
+    }
+}
+
+/**
+ * Opens a debug pipe transport, used in the test suite.
+ * 
+ * @param entry the address entry to try opening as debug-pipe
+ * @param transport_p return location for the opened transport
+ * @param error error to be set
+ * @returns result of the attempt
+ */
+DBusTransportOpenResult
+_dbus_transport_open_debug_pipe (DBusAddressEntry  *entry,
+                                 DBusTransport    **transport_p,
+                                 DBusError         *error)
+{
+  const char *method;
+  
+  method = dbus_address_entry_get_method (entry);
+  _dbus_assert (method != NULL);
+
+  if (strcmp (method, "debug-pipe") == 0)
+    {
+      const char *name = dbus_address_entry_get_value (entry, "name");
+
+      if (name == NULL)
+        {
+          _dbus_set_bad_address (error, "debug-pipe", "name",
+                                 NULL);
+          return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
+        }
+          
+      *transport_p = _dbus_transport_debug_pipe_new (name, error);
+
+      if (*transport_p == NULL)
+        {
+          _DBUS_ASSERT_ERROR_IS_SET (error);
+          return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+        }
+      else
+        {
+          _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+          return DBUS_TRANSPORT_OPEN_OK;
+        }      
+    }
+  else
+    {
+      _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+      return DBUS_TRANSPORT_OPEN_NOT_HANDLED;
+    }
+}
+
+
+/** @} */
+
+#endif /* DBUS_BUILD_TESTS */
+
diff --git a/src/dbus/dbus-server-debug-pipe.h b/src/dbus/dbus-server-debug-pipe.h
new file mode 100644 (file)
index 0000000..e86ec5e
--- /dev/null
@@ -0,0 +1,47 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-server-debug-pipe.h In-proc debug server implementation 
+ *
+ * Copyright (C) 2003  CodeFactory AB
+ * Copyright (C) 2003  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_SERVER_DEBUG_PIPE_H
+#define DBUS_SERVER_DEBUG_PIPE_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-server-protected.h>
+#include <dbus/dbus-transport-protected.h>
+
+DBUS_BEGIN_DECLS
+
+DBusServer*             _dbus_server_debug_pipe_new     (const char        *server_name,
+                                                         DBusError         *error);
+DBusTransport*          _dbus_transport_debug_pipe_new  (const char        *server_name,
+                                                         DBusError         *error);
+DBusServerListenResult  _dbus_server_listen_debug_pipe  (DBusAddressEntry  *entry,
+                                                         DBusServer       **server_p,
+                                                         DBusError         *error);
+DBusTransportOpenResult _dbus_transport_open_debug_pipe (DBusAddressEntry  *entry,
+                                                         DBusTransport    **transport_p,
+                                                         DBusError         *error);
+
+
+DBUS_END_DECLS
+
+#endif /* DBUS_SERVER_DEBUG_PIPE_H */
diff --git a/src/dbus/dbus-server-protected.h b/src/dbus/dbus-server-protected.h
new file mode 100644 (file)
index 0000000..e8ef37a
--- /dev/null
@@ -0,0 +1,160 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-server-protected.h Used by subclasses of DBusServer object (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_SERVER_PROTECTED_H
+#define DBUS_SERVER_PROTECTED_H
+
+#include <config.h>
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-threads-internal.h>
+#include <dbus/dbus-server.h>
+#include <dbus/dbus-address.h>
+#include <dbus/dbus-timeout.h>
+#include <dbus/dbus-watch.h>
+#include <dbus/dbus-resources.h>
+#include <dbus/dbus-dataslot.h>
+#include <dbus/dbus-string.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusServerVTable DBusServerVTable;
+
+/**
+ * Virtual table to be implemented by all server "subclasses"
+ */
+struct DBusServerVTable
+{
+  void        (* finalize)      (DBusServer *server);
+  /**< The finalize method must free the server. */
+  
+  void        (* disconnect)    (DBusServer *server);
+  /**< Disconnect this server. */
+};
+
+/**
+ * Internals of DBusServer object
+ */
+struct DBusServer
+{
+  DBusAtomic refcount;                        /**< Reference count. */
+  const DBusServerVTable *vtable;             /**< Virtual methods for this instance. */
+  DBusMutex *mutex;                           /**< Lock on the server object */
+
+  DBusGUID guid;                              /**< Globally unique ID of server */
+
+  DBusString guid_hex;                        /**< Hex-encoded version of GUID */
+  
+  DBusWatchList *watches;                     /**< Our watches */
+  DBusTimeoutList *timeouts;                  /**< Our timeouts */  
+
+  char *address;                              /**< Address this server is listening on. */
+  
+  int max_connections;                        /**< Max number of connections allowed at once. */
+
+  DBusDataSlotList slot_list;   /**< Data stored by allocated integer ID */
+  
+  DBusNewConnectionFunction  new_connection_function;
+  /**< Callback to invoke when a new connection is created. */
+  void *new_connection_data;
+  /**< Data for new connection callback */
+  DBusFreeFunction new_connection_free_data_function;
+  /**< Callback to invoke to free new_connection_data
+   * when server is finalized or data is replaced.
+   */
+
+  char **auth_mechanisms; /**< Array of allowed authentication mechanisms */
+  
+  unsigned int disconnected : 1;              /**< TRUE if we are disconnected. */
+
+#ifndef DBUS_DISABLE_CHECKS
+  unsigned int have_server_lock : 1; /**< Does someone have the server mutex locked */
+#endif
+};
+
+dbus_bool_t _dbus_server_init_base      (DBusServer             *server,
+                                         const DBusServerVTable *vtable,
+                                         const DBusString       *address);
+void        _dbus_server_finalize_base  (DBusServer             *server);
+dbus_bool_t _dbus_server_add_watch      (DBusServer             *server,
+                                         DBusWatch              *watch);
+void        _dbus_server_remove_watch   (DBusServer             *server,
+                                         DBusWatch              *watch);
+void        _dbus_server_toggle_watch   (DBusServer             *server,
+                                         DBusWatch              *watch,
+                                         dbus_bool_t             enabled);
+dbus_bool_t _dbus_server_add_timeout    (DBusServer             *server,
+                                         DBusTimeout            *timeout);
+void        _dbus_server_remove_timeout (DBusServer             *server,
+                                         DBusTimeout            *timeout);
+void        _dbus_server_toggle_timeout (DBusServer             *server,
+                                         DBusTimeout            *timeout,
+                                         dbus_bool_t             enabled);
+
+void        _dbus_server_ref_unlocked   (DBusServer             *server);
+void        _dbus_server_unref_unlocked (DBusServer             *server);
+
+typedef enum
+{
+  DBUS_SERVER_LISTEN_NOT_HANDLED, /**< we aren't in charge of this address type */
+  DBUS_SERVER_LISTEN_OK,          /**< we set up the listen */
+  DBUS_SERVER_LISTEN_BAD_ADDRESS, /**< malformed address */
+  DBUS_SERVER_LISTEN_DID_NOT_CONNECT /**< well-formed address but failed to set it up */
+} DBusServerListenResult;
+
+DBusServerListenResult _dbus_server_listen_platform_specific (DBusAddressEntry  *entry,
+                                                              DBusServer       **server_p,
+                                                              DBusError         *error);
+
+#ifdef DBUS_DISABLE_CHECKS
+#define TOOK_LOCK_CHECK(server)
+#define RELEASING_LOCK_CHECK(server)
+#define HAVE_LOCK_CHECK(server)
+#else
+#define TOOK_LOCK_CHECK(server) do {                \
+    _dbus_assert (!(server)->have_server_lock); \
+    (server)->have_server_lock = TRUE;          \
+  } while (0)
+#define RELEASING_LOCK_CHECK(server) do {            \
+    _dbus_assert ((server)->have_server_lock);   \
+    (server)->have_server_lock = FALSE;          \
+  } while (0)
+#define HAVE_LOCK_CHECK(server)        _dbus_assert ((server)->have_server_lock)
+/* A "DO_NOT_HAVE_LOCK_CHECK" is impossible since we need the lock to check the flag */
+#endif
+
+#define TRACE_LOCKS 0
+
+#define SERVER_LOCK(server)   do {                                              \
+    if (TRACE_LOCKS) { _dbus_verbose ("  LOCK: %s\n", _DBUS_FUNCTION_NAME); }   \
+    _dbus_mutex_lock ((server)->mutex);                                          \
+    TOOK_LOCK_CHECK (server);                                                   \
+  } while (0)
+
+#define SERVER_UNLOCK(server) do {                                                      \
+    if (TRACE_LOCKS) { _dbus_verbose ("  UNLOCK: %s\n", _DBUS_FUNCTION_NAME);  }        \
+    RELEASING_LOCK_CHECK (server);                                                      \
+    _dbus_mutex_unlock ((server)->mutex);                                                \
+  } while (0)
+
+DBUS_END_DECLS
+
+#endif /* DBUS_SERVER_PROTECTED_H */
diff --git a/src/dbus/dbus-server-socket.c b/src/dbus/dbus-server-socket.c
new file mode 100644 (file)
index 0000000..0cd2bb6
--- /dev/null
@@ -0,0 +1,539 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-server-socket.c Server implementation for sockets
+ *
+ * Copyright (C) 2002, 2003, 2004, 2006  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-internals.h"
+#include "dbus-server-socket.h"
+#include "dbus-transport-socket.h"
+#include "dbus-connection-internal.h"
+#include "dbus-string.h"
+
+/**
+ * @defgroup DBusServerSocket DBusServer implementations for SOCKET
+ * @ingroup  DBusInternals
+ * @brief Implementation details of DBusServer on SOCKET
+ *
+ * @{
+ */
+/**
+ * 
+ * Opaque object representing a Socket server implementation.
+ */
+typedef struct DBusServerSocket DBusServerSocket;
+
+/**
+ * Implementation details of DBusServerSocket. All members
+ * are private.
+ */
+struct DBusServerSocket
+{
+  DBusServer base;   /**< Parent class members. */
+  int n_fds;         /**< Number of active file handles */
+  int *fds;          /**< File descriptor or -1 if disconnected. */
+  DBusWatch **watch; /**< File descriptor watch. */
+  char *socket_name; /**< Name of domain socket, to unlink if appropriate */
+};
+
+static void
+socket_finalize (DBusServer *server)
+{
+  DBusServerSocket *socket_server = (DBusServerSocket*) server;
+  int i;
+  
+  _dbus_server_finalize_base (server);
+
+  for (i = 0 ; i < socket_server->n_fds ; i++)
+    if (socket_server->watch[i])
+      {
+        _dbus_watch_unref (socket_server->watch[i]);
+        socket_server->watch[i] = NULL;
+      }
+  
+  dbus_free (socket_server->fds);
+  dbus_free (socket_server->watch);
+  dbus_free (socket_server->socket_name);
+  dbus_free (server);
+}
+
+/* Return value is just for memory, not other failures. */
+static dbus_bool_t
+handle_new_client_fd_and_unlock (DBusServer *server,
+                                 int         client_fd)
+{
+  DBusConnection *connection;
+  DBusTransport *transport;
+  DBusNewConnectionFunction new_connection_function;
+  void *new_connection_data;
+  
+  _dbus_verbose ("Creating new client connection with fd %d\n", client_fd);
+
+  HAVE_LOCK_CHECK (server);
+  
+  if (!_dbus_set_fd_nonblocking (client_fd, NULL))
+    {
+      SERVER_UNLOCK (server);
+      return TRUE;
+    }
+  
+  transport = _dbus_transport_new_for_socket (client_fd, &server->guid_hex, NULL);
+  if (transport == NULL)
+    {
+      _dbus_close_socket (client_fd, NULL);
+      SERVER_UNLOCK (server);
+      return FALSE;
+    }
+
+  if (!_dbus_transport_set_auth_mechanisms (transport,
+                                            (const char **) server->auth_mechanisms))
+    {
+      _dbus_transport_unref (transport);
+      SERVER_UNLOCK (server);
+      return FALSE;
+    }
+  
+  /* note that client_fd is now owned by the transport, and will be
+   * closed on transport disconnection/finalization
+   */
+  
+  connection = _dbus_connection_new_for_transport (transport);
+  _dbus_transport_unref (transport);
+  transport = NULL; /* now under the connection lock */
+  
+  if (connection == NULL)
+    {
+      SERVER_UNLOCK (server);
+      return FALSE;
+    }
+  
+  /* See if someone wants to handle this new connection, self-referencing
+   * for paranoia.
+   */
+  new_connection_function = server->new_connection_function;
+  new_connection_data = server->new_connection_data;
+
+  _dbus_server_ref_unlocked (server);
+  SERVER_UNLOCK (server);
+  
+  if (new_connection_function)
+    {
+      (* new_connection_function) (server, connection,
+                                   new_connection_data);
+    }
+  dbus_server_unref (server);
+  
+  /* If no one grabbed a reference, the connection will die. */
+  _dbus_connection_close_if_only_one_ref (connection);
+  dbus_connection_unref (connection);
+
+  return TRUE;
+}
+
+static dbus_bool_t
+socket_handle_watch (DBusWatch    *watch,
+                   unsigned int  flags,
+                   void         *data)
+{
+  DBusServer *server = data;
+#ifndef DBUS_DISABLE_ASSERT
+  DBusServerSocket *socket_server = data;
+  int i;
+  dbus_bool_t found = FALSE;
+#endif
+
+  SERVER_LOCK (server);
+  
+#ifndef DBUS_DISABLE_ASSERT
+  for (i = 0 ; i < socket_server->n_fds ; i++)
+    {
+      if (socket_server->watch[i] == watch)
+        found = TRUE;
+    }
+  _dbus_assert (found);
+#endif
+
+  _dbus_verbose ("Handling client connection, flags 0x%x\n", flags);
+  
+  if (flags & DBUS_WATCH_READABLE)
+    {
+      int client_fd;
+      int listen_fd;
+      
+      listen_fd = dbus_watch_get_socket (watch);
+
+      client_fd = _dbus_accept (listen_fd);
+      
+      if (client_fd < 0)
+        {
+          /* EINTR handled for us */
+          
+          if (_dbus_get_is_errno_eagain_or_ewouldblock ())
+            _dbus_verbose ("No client available to accept after all\n");
+          else
+            _dbus_verbose ("Failed to accept a client connection: %s\n",
+                           _dbus_strerror_from_errno ());
+
+          SERVER_UNLOCK (server);
+        }
+      else
+        {
+         _dbus_fd_set_close_on_exec (client_fd);         
+
+          if (!handle_new_client_fd_and_unlock (server, client_fd))
+            _dbus_verbose ("Rejected client connection due to lack of memory\n");
+        }
+    }
+
+  if (flags & DBUS_WATCH_ERROR)
+    _dbus_verbose ("Error on server listening socket\n");
+
+  if (flags & DBUS_WATCH_HANGUP)
+    _dbus_verbose ("Hangup on server listening socket\n");
+
+  return TRUE;
+}
+  
+static void
+socket_disconnect (DBusServer *server)
+{
+  DBusServerSocket *socket_server = (DBusServerSocket*) server;
+  int i;
+
+  HAVE_LOCK_CHECK (server);
+  
+  for (i = 0 ; i < socket_server->n_fds ; i++)
+    {
+      if (socket_server->watch[i])
+        {
+          _dbus_server_remove_watch (server,
+                                     socket_server->watch[i]);
+          _dbus_watch_unref (socket_server->watch[i]);
+          socket_server->watch[i] = NULL;
+        }
+
+      _dbus_close_socket (socket_server->fds[i], NULL);
+      socket_server->fds[i] = -1;
+    }
+
+  if (socket_server->socket_name != NULL)
+    {
+      DBusString tmp;
+      _dbus_string_init_const (&tmp, socket_server->socket_name);
+      _dbus_delete_file (&tmp, NULL);
+    }
+
+  HAVE_LOCK_CHECK (server);
+}
+
+static const DBusServerVTable socket_vtable = {
+  socket_finalize,
+  socket_disconnect
+};
+
+/**
+ * Creates a new server listening on the given file descriptor.  The
+ * file descriptor should be nonblocking (use
+ * _dbus_set_fd_nonblocking() to make it so). The file descriptor
+ * should be listening for connections, that is, listen() should have
+ * been successfully invoked on it. The server will use accept() to
+ * accept new client connections.
+ *
+ * @param fds list of file descriptors.
+ * @param n_fds number of file descriptors
+ * @param address the server's address
+ * @returns the new server, or #NULL if no memory.
+ * 
+ */
+DBusServer*
+_dbus_server_new_for_socket (int              *fds,
+                             int               n_fds,
+                             const DBusString *address)
+{
+  DBusServerSocket *socket_server;
+  DBusServer *server;
+  int i;
+  
+  socket_server = dbus_new0 (DBusServerSocket, 1);
+  if (socket_server == NULL)
+    return NULL;
+
+  socket_server->fds = dbus_new (int, n_fds);
+  if (!socket_server->fds)
+    goto failed_0;
+
+  socket_server->watch = dbus_new0 (DBusWatch *, n_fds);
+  if (!socket_server->watch)
+    goto failed_1;
+
+  for (i = 0 ; i < n_fds ; i++)
+    {
+      DBusWatch *watch;
+
+      watch = _dbus_watch_new (fds[i],
+                               DBUS_WATCH_READABLE,
+                               TRUE,
+                               socket_handle_watch, socket_server,
+                               NULL);
+      if (watch == NULL)
+        goto failed_2;
+
+      socket_server->n_fds++;
+      socket_server->fds[i] = fds[i];
+      socket_server->watch[i] = watch;
+    }
+
+  if (!_dbus_server_init_base (&socket_server->base,
+                               &socket_vtable, address))
+    goto failed_2;
+
+  server = (DBusServer*)socket_server;
+
+  SERVER_LOCK (server);
+  
+  for (i = 0 ; i < n_fds ; i++)
+    {
+      if (!_dbus_server_add_watch (&socket_server->base,
+                                   socket_server->watch[i]))
+        {
+          int j;
+          for (j = 0 ; j < i ; j++)
+            _dbus_server_remove_watch (server,
+                                       socket_server->watch[j]);
+
+          SERVER_UNLOCK (server);
+          _dbus_server_finalize_base (&socket_server->base);
+          goto failed_2;
+        }
+    }
+
+  SERVER_UNLOCK (server);
+  
+  return (DBusServer*) socket_server;
+
+ failed_2:
+  for (i = 0 ; i < n_fds ; i++)
+    {
+      if (socket_server->watch[i] != NULL)
+        {
+          _dbus_watch_unref (socket_server->watch[i]);
+          socket_server->watch[i] = NULL;
+        }
+    }
+  dbus_free (socket_server->watch);
+
+ failed_1:
+  dbus_free (socket_server->fds);
+
+ failed_0:
+  dbus_free (socket_server);
+  return NULL;
+}
+
+/**
+ * Creates a new server listening on TCP.
+ * If host is NULL, it will default to localhost.
+ * If bind is NULL, it will default to the value for the host
+ * parameter, and if that is NULL, then localhost
+ * If bind is a hostname, it will be resolved and will listen
+ * on all returned addresses.
+ * If family is NULL, hostname resolution will try all address
+ * families, otherwise it can be ipv4 or ipv6 to restrict the
+ * addresses considered.
+ *
+ * @param host the hostname to report for the listen address
+ * @param bind the hostname to listen on
+ * @param port the port to listen on or 0 to let the OS choose
+ * @param family 
+ * @param error location to store reason for failure.
+ * @returns the new server, or #NULL on failure.
+ */
+DBusServer*
+_dbus_server_new_for_tcp_socket (const char     *host,
+                                 const char     *bind,
+                                 const char     *port,
+                                 const char     *family,
+                                 DBusError      *error)
+{
+  DBusServer *server;
+  int *listen_fds = NULL;
+  int nlisten_fds = 0, i;
+  DBusString address;
+  DBusString host_str;
+  DBusString port_str;
+  
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  if (!_dbus_string_init (&address))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      return NULL;
+    }
+
+  if (!_dbus_string_init (&port_str))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto failed_0;
+    }
+
+  if (host == NULL)
+    host = "localhost";
+
+  if (port == NULL)
+    port = "0";
+
+  if (bind == NULL)
+    bind = host;
+  else if (strcmp (bind, "*") == 0)
+    bind = NULL;
+
+  nlisten_fds =_dbus_listen_tcp_socket (bind, port, family,
+                                        &port_str,
+                                        &listen_fds, error);
+  if (nlisten_fds <= 0)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET(error);
+      goto failed_1;
+    }
+
+  for (i = 0 ; i < nlisten_fds ; i++)
+    _dbus_fd_set_close_on_exec (listen_fds[i]);
+
+  _dbus_string_init_const (&host_str, host);
+  if (!_dbus_string_append (&address, "tcp:host=") ||
+      !_dbus_address_append_escaped (&address, &host_str) ||
+      !_dbus_string_append (&address, ",port=") ||
+      !_dbus_string_append (&address, _dbus_string_get_const_data(&port_str)))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto failed_2;
+    }
+  if (family &&
+      (!_dbus_string_append (&address, ",family=") ||
+       !_dbus_string_append (&address, family)))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto failed_2;
+    }
+  
+  server = _dbus_server_new_for_socket (listen_fds, nlisten_fds, &address);
+  if (server == NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto failed_2;
+    }
+
+  _dbus_string_free (&port_str);
+  _dbus_string_free (&address);
+  dbus_free(listen_fds);
+
+  return server;
+
+ failed_2:
+  for (i = 0 ; i < nlisten_fds ; i++)
+    _dbus_close_socket (listen_fds[i], NULL);
+  dbus_free(listen_fds);
+
+ failed_1:
+  _dbus_string_free (&port_str);
+
+ failed_0:
+  _dbus_string_free (&address);
+
+  return NULL;
+}
+
+/**
+ * Tries to interpret the address entry for various socket-related
+ * addresses (well, currently only tcp).
+ * 
+ * Sets error if the result is not OK.
+ * 
+ * @param entry an address entry
+ * @param server_p a new DBusServer, or #NULL on failure.
+ * @param error location to store rationale for failure on bad address
+ * @returns the outcome
+ * 
+ */
+DBusServerListenResult
+_dbus_server_listen_socket (DBusAddressEntry *entry,
+                            DBusServer      **server_p,
+                            DBusError        *error)
+{
+  const char *method;
+
+  *server_p = NULL;
+  
+  method = dbus_address_entry_get_method (entry);
+  
+  if (strcmp (method, "tcp") == 0)
+    {
+      const char *host;
+      const char *port;
+      const char *bind;
+      const char *family;
+
+      host = dbus_address_entry_get_value (entry, "host");
+      bind = dbus_address_entry_get_value (entry, "bind");
+      port = dbus_address_entry_get_value (entry, "port");
+      family = dbus_address_entry_get_value (entry, "family");
+
+      *server_p = _dbus_server_new_for_tcp_socket (host, bind, port,
+                                                   family, error);
+
+      if (*server_p)
+        {
+          _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+          return DBUS_SERVER_LISTEN_OK;
+        }
+      else
+        {
+          _DBUS_ASSERT_ERROR_IS_SET(error);
+          return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
+        }
+    }
+  else
+    {
+      _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+      return DBUS_SERVER_LISTEN_NOT_HANDLED;
+    }
+}
+
+/**
+ * This is a bad hack since it's really unix domain socket
+ * specific. Also, the function weirdly adopts ownership
+ * of the passed-in string.
+ * 
+ * @param server a socket server
+ * @param filename socket filename to report/delete
+ * 
+ */
+void
+_dbus_server_socket_own_filename (DBusServer *server,
+                                  char       *filename)
+{
+  DBusServerSocket *socket_server = (DBusServerSocket*) server;
+
+  socket_server->socket_name = filename;
+}
+
+
+/** @} */
+
diff --git a/src/dbus/dbus-server-socket.h b/src/dbus/dbus-server-socket.h
new file mode 100644 (file)
index 0000000..34900b4
--- /dev/null
@@ -0,0 +1,49 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-server-socket.h Server implementation for sockets
+ *
+ * Copyright (C) 2002, 2006  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_SERVER_SOCKET_H
+#define DBUS_SERVER_SOCKET_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-server-protected.h>
+
+DBUS_BEGIN_DECLS
+
+DBusServer* _dbus_server_new_for_socket           (int              *fds,
+                                                   int               n_fds,
+                                                   const DBusString *address);
+DBusServer* _dbus_server_new_for_tcp_socket       (const char       *host,
+                                                   const char       *bind,
+                                                   const char       *port,
+                                                   const char       *family,
+                                                   DBusError        *error);
+DBusServerListenResult _dbus_server_listen_socket (DBusAddressEntry  *entry,
+                                                   DBusServer       **server_p,
+                                                   DBusError         *error);
+
+
+void _dbus_server_socket_own_filename (DBusServer *server,
+                                       char       *filename);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_SERVER_SOCKET_H */
diff --git a/src/dbus/dbus-server-unix.c b/src/dbus/dbus-server-unix.c
new file mode 100644 (file)
index 0000000..1dda5d1
--- /dev/null
@@ -0,0 +1,236 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-server-unix.c Server implementation for Unix network protocols.
+ *
+ * Copyright (C) 2002, 2003, 2004  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-internals.h"
+#include "dbus-server-unix.h"
+#include "dbus-server-socket.h"
+#include "dbus-transport-unix.h"
+#include "dbus-connection-internal.h"
+#include "dbus-sysdeps-unix.h"
+#include "dbus-string.h"
+
+/**
+ * @defgroup DBusServerUnix DBusServer implementations for UNIX
+ * @ingroup  DBusInternals
+ * @brief Implementation details of DBusServer on UNIX
+ *
+ * @{
+ */
+
+/**
+ * Tries to interpret the address entry in a platform-specific
+ * way, creating a platform-specific server type if appropriate.
+ * Sets error if the result is not OK.
+ * 
+ * @param entry an address entry
+ * @param server_p location to store a new DBusServer, or #NULL on failure.
+ * @param error location to store rationale for failure on bad address
+ * @returns the outcome
+ * 
+ */
+DBusServerListenResult
+_dbus_server_listen_platform_specific (DBusAddressEntry *entry,
+                                       DBusServer      **server_p,
+                                       DBusError        *error)
+{
+  const char *method;
+
+  *server_p = NULL;
+  
+  method = dbus_address_entry_get_method (entry);
+
+  if (strcmp (method, "unix") == 0)
+    {
+      const char *path = dbus_address_entry_get_value (entry, "path");
+      const char *tmpdir = dbus_address_entry_get_value (entry, "tmpdir");
+      const char *abstract = dbus_address_entry_get_value (entry, "abstract");
+          
+      if (path == NULL && tmpdir == NULL && abstract == NULL)
+        {
+          _dbus_set_bad_address(error, "unix",
+                                "path or tmpdir or abstract",
+                                NULL);
+          return DBUS_SERVER_LISTEN_BAD_ADDRESS;
+        }
+
+      if ((path && tmpdir) ||
+          (path && abstract) ||
+          (tmpdir && abstract))
+        {
+          _dbus_set_bad_address(error, NULL, NULL,
+                                "cannot specify two of \"path\" and \"tmpdir\" and \"abstract\" at the same time");
+          return DBUS_SERVER_LISTEN_BAD_ADDRESS;
+        }
+
+      if (tmpdir != NULL)
+        {
+          DBusString full_path;
+          DBusString filename;
+              
+          if (!_dbus_string_init (&full_path))
+            {
+              dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+              return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
+            }
+                  
+          if (!_dbus_string_init (&filename))
+            {
+              _dbus_string_free (&full_path);
+              dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+              return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
+            }
+              
+          if (!_dbus_string_append (&filename,
+                                    "dbus-") ||
+              !_dbus_generate_random_ascii (&filename, 10) ||
+              !_dbus_string_append (&full_path, tmpdir) ||
+              !_dbus_concat_dir_and_file (&full_path, &filename))
+            {
+              _dbus_string_free (&full_path);
+              _dbus_string_free (&filename);
+              dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+              return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
+            }
+              
+          /* Always use abstract namespace if possible with tmpdir */
+              
+          *server_p =
+            _dbus_server_new_for_domain_socket (_dbus_string_get_const_data (&full_path),
+#ifdef HAVE_ABSTRACT_SOCKETS
+                                                TRUE,
+#else
+                                                FALSE,
+#endif
+                                                error);
+
+          _dbus_string_free (&full_path);
+          _dbus_string_free (&filename);
+        }
+      else
+        {
+          if (path)
+            *server_p = _dbus_server_new_for_domain_socket (path, FALSE, error);
+          else
+            *server_p = _dbus_server_new_for_domain_socket (abstract, TRUE, error);
+        }
+
+      if (*server_p != NULL)
+        {
+          _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+          return DBUS_SERVER_LISTEN_OK;
+        }
+      else
+        {
+          _DBUS_ASSERT_ERROR_IS_SET(error);
+          return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
+        }
+    }
+  else
+    {
+      /* If we don't handle the method, we return NULL with the
+       * error unset
+       */
+      _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+      return DBUS_SERVER_LISTEN_NOT_HANDLED;
+    }
+}
+
+/**
+ * Creates a new server listening on the given Unix domain socket.
+ *
+ * @param path the path for the domain socket.
+ * @param abstract #TRUE to use abstract socket namespace
+ * @param error location to store reason for failure.
+ * @returns the new server, or #NULL on failure.
+ */
+DBusServer*
+_dbus_server_new_for_domain_socket (const char     *path,
+                                    dbus_bool_t     abstract,
+                                    DBusError      *error)
+{
+  DBusServer *server;
+  int listen_fd;
+  DBusString address;
+  char *path_copy;
+  DBusString path_str;
+  
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  if (!_dbus_string_init (&address))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      return NULL;
+    }
+
+  _dbus_string_init_const (&path_str, path);
+  if ((abstract &&
+       !_dbus_string_append (&address, "unix:abstract=")) ||
+      (!abstract &&
+       !_dbus_string_append (&address, "unix:path=")) ||
+      !_dbus_address_append_escaped (&address, &path_str))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto failed_0;
+    }
+
+  path_copy = _dbus_strdup (path);
+  if (path_copy == NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto failed_0;
+    }
+  
+  listen_fd = _dbus_listen_unix_socket (path, abstract, error);
+  _dbus_fd_set_close_on_exec (listen_fd);
+  
+  if (listen_fd < 0)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      goto failed_1;
+    }
+  
+  server = _dbus_server_new_for_socket (&listen_fd, 1, &address);
+  if (server == NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto failed_2;
+    }
+
+  _dbus_server_socket_own_filename(server, path_copy);
+  
+  _dbus_string_free (&address);
+  
+  return server;
+
+ failed_2:
+  _dbus_close_socket (listen_fd, NULL);
+ failed_1:
+  dbus_free (path_copy);
+ failed_0:
+  _dbus_string_free (&address);
+
+  return NULL;
+}
+
+/** @} */
+
diff --git a/src/dbus/dbus-server-unix.h b/src/dbus/dbus-server-unix.h
new file mode 100644 (file)
index 0000000..34f5a71
--- /dev/null
@@ -0,0 +1,37 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-server-unix.h Server implementation for Unix network protocols.
+ *
+ * Copyright (C) 2002  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_SERVER_UNIX_H
+#define DBUS_SERVER_UNIX_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-server-protected.h>
+
+DBUS_BEGIN_DECLS
+
+DBusServer* _dbus_server_new_for_domain_socket (const char       *path,
+                                                dbus_bool_t       abstract,
+                                                DBusError        *error);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_SERVER_UNIX_H */
diff --git a/src/dbus/dbus-server.c b/src/dbus/dbus-server.c
new file mode 100644 (file)
index 0000000..f04829b
--- /dev/null
@@ -0,0 +1,1202 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-server.c DBusServer object
+ *
+ * Copyright (C) 2002, 2003, 2004, 2005 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-server.h"
+#include "dbus-server-unix.h"
+#include "dbus-server-socket.h"
+#include "dbus-string.h"
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-server-debug-pipe.h"
+#endif
+#include "dbus-address.h"
+#include "dbus-protocol.h"
+
+/**
+ * @defgroup DBusServer DBusServer
+ * @ingroup  DBus
+ * @brief Server that listens for new connections.
+ *
+ * A DBusServer represents a server that other applications
+ * can connect to. Each connection from another application
+ * is represented by a #DBusConnection.
+ *
+ * @todo Thread safety hasn't been tested much for #DBusServer
+ * @todo Need notification to apps of disconnection, may matter for some transports
+ */
+
+/**
+ * @defgroup DBusServerInternals DBusServer implementation details
+ * @ingroup  DBusInternals
+ * @brief Implementation details of DBusServer
+ *
+ * @{
+ */
+
+/* this is a little fragile since it assumes the address doesn't
+ * already have a guid, but it shouldn't
+ */
+static char*
+copy_address_with_guid_appended (const DBusString *address,
+                                 const DBusString *guid_hex)
+{
+  DBusString with_guid;
+  char *retval;
+  
+  if (!_dbus_string_init (&with_guid))
+    return NULL;
+
+  if (!_dbus_string_copy (address, 0, &with_guid,
+                          _dbus_string_get_length (&with_guid)) ||
+      !_dbus_string_append (&with_guid, ",guid=") ||
+      !_dbus_string_copy (guid_hex, 0,
+                          &with_guid, _dbus_string_get_length (&with_guid)))
+    {
+      _dbus_string_free (&with_guid);
+      return NULL;
+    }
+
+  retval = NULL;
+  _dbus_string_steal_data (&with_guid, &retval);
+
+  _dbus_string_free (&with_guid);
+      
+  return retval; /* may be NULL if steal_data failed */
+}
+
+/**
+ * Initializes the members of the DBusServer base class.
+ * Chained up to by subclass constructors.
+ *
+ * @param server the server.
+ * @param vtable the vtable for the subclass.
+ * @param address the server's address
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_server_init_base (DBusServer             *server,
+                        const DBusServerVTable *vtable,
+                        const DBusString       *address)
+{
+  server->vtable = vtable;
+  server->refcount.value = 1;
+
+  server->address = NULL;
+  server->watches = NULL;
+  server->timeouts = NULL;
+
+  if (!_dbus_string_init (&server->guid_hex))
+    return FALSE;
+
+  _dbus_generate_uuid (&server->guid);
+
+  if (!_dbus_uuid_encode (&server->guid, &server->guid_hex))
+    goto failed;
+  
+  server->address = copy_address_with_guid_appended (address,
+                                                     &server->guid_hex);
+  if (server->address == NULL)
+    goto failed;
+  
+  _dbus_mutex_new_at_location (&server->mutex);
+  if (server->mutex == NULL)
+    goto failed;
+  
+  server->watches = _dbus_watch_list_new ();
+  if (server->watches == NULL)
+    goto failed;
+
+  server->timeouts = _dbus_timeout_list_new ();
+  if (server->timeouts == NULL)
+    goto failed;
+
+  _dbus_data_slot_list_init (&server->slot_list);
+
+  _dbus_verbose ("Initialized server on address %s\n", server->address);
+  
+  return TRUE;
+
+ failed:
+  _dbus_mutex_free_at_location (&server->mutex);
+  server->mutex = NULL;
+  if (server->watches)
+    {
+      _dbus_watch_list_free (server->watches);
+      server->watches = NULL;
+    }
+  if (server->timeouts)
+    {
+      _dbus_timeout_list_free (server->timeouts);
+      server->timeouts = NULL;
+    }
+  if (server->address)
+    {
+      dbus_free (server->address);
+      server->address = NULL;
+    }
+  _dbus_string_free (&server->guid_hex);
+  
+  return FALSE;
+}
+
+/**
+ * Finalizes the members of the DBusServer base class.
+ * Chained up to by subclass finalizers.
+ *
+ * @param server the server.
+ */
+void
+_dbus_server_finalize_base (DBusServer *server)
+{
+  /* We don't have the lock, but nobody should be accessing
+   * concurrently since they don't have a ref
+   */
+#ifndef DBUS_DISABLE_CHECKS
+  _dbus_assert (!server->have_server_lock);
+#endif
+  _dbus_assert (server->disconnected);
+  
+  /* calls out to application code... */
+  _dbus_data_slot_list_free (&server->slot_list);
+
+  dbus_server_set_new_connection_function (server, NULL, NULL, NULL);
+
+  _dbus_watch_list_free (server->watches);
+  _dbus_timeout_list_free (server->timeouts);
+
+  _dbus_mutex_free_at_location (&server->mutex);
+  
+  dbus_free (server->address);
+
+  dbus_free_string_array (server->auth_mechanisms);
+
+  _dbus_string_free (&server->guid_hex);
+}
+
+
+/** Function to be called in protected_change_watch() with refcount held */
+typedef dbus_bool_t (* DBusWatchAddFunction)     (DBusWatchList *list,
+                                                  DBusWatch     *watch);
+/** Function to be called in protected_change_watch() with refcount held */
+typedef void        (* DBusWatchRemoveFunction)  (DBusWatchList *list,
+                                                  DBusWatch     *watch);
+/** Function to be called in protected_change_watch() with refcount held */
+typedef void        (* DBusWatchToggleFunction)  (DBusWatchList *list,
+                                                  DBusWatch     *watch,
+                                                  dbus_bool_t    enabled);
+
+static dbus_bool_t
+protected_change_watch (DBusServer             *server,
+                        DBusWatch              *watch,
+                        DBusWatchAddFunction    add_function,
+                        DBusWatchRemoveFunction remove_function,
+                        DBusWatchToggleFunction toggle_function,
+                        dbus_bool_t             enabled)
+{
+  DBusWatchList *watches;
+  dbus_bool_t retval;
+  
+  HAVE_LOCK_CHECK (server);
+
+  /* This isn't really safe or reasonable; a better pattern is the "do
+   * everything, then drop lock and call out" one; but it has to be
+   * propagated up through all callers
+   */
+  
+  watches = server->watches;
+  if (watches)
+    {
+      server->watches = NULL;
+      _dbus_server_ref_unlocked (server);
+      SERVER_UNLOCK (server);
+
+      if (add_function)
+        retval = (* add_function) (watches, watch);
+      else if (remove_function)
+        {
+          retval = TRUE;
+          (* remove_function) (watches, watch);
+        }
+      else
+        {
+          retval = TRUE;
+          (* toggle_function) (watches, watch, enabled);
+        }
+      
+      SERVER_LOCK (server);
+      server->watches = watches;
+      _dbus_server_unref_unlocked (server);
+
+      return retval;
+    }
+  else
+    return FALSE;
+}
+
+/**
+ * Adds a watch for this server, chaining out to application-provided
+ * watch handlers.
+ *
+ * @param server the server.
+ * @param watch the watch to add.
+ */
+dbus_bool_t
+_dbus_server_add_watch (DBusServer *server,
+                        DBusWatch  *watch)
+{
+  HAVE_LOCK_CHECK (server);
+  return protected_change_watch (server, watch,
+                                 _dbus_watch_list_add_watch,
+                                 NULL, NULL, FALSE);
+}
+
+/**
+ * Removes a watch previously added with _dbus_server_remove_watch().
+ *
+ * @param server the server.
+ * @param watch the watch to remove.
+ */
+void
+_dbus_server_remove_watch  (DBusServer *server,
+                            DBusWatch  *watch)
+{
+  HAVE_LOCK_CHECK (server);
+  protected_change_watch (server, watch,
+                          NULL,
+                          _dbus_watch_list_remove_watch,
+                          NULL, FALSE);
+}
+
+/**
+ * Toggles a watch and notifies app via server's
+ * DBusWatchToggledFunction if available. It's an error to call this
+ * function on a watch that was not previously added.
+ *
+ * @param server the server.
+ * @param watch the watch to toggle.
+ * @param enabled whether to enable or disable
+ */
+void
+_dbus_server_toggle_watch (DBusServer  *server,
+                           DBusWatch   *watch,
+                           dbus_bool_t  enabled)
+{
+  _dbus_assert (watch != NULL);
+
+  HAVE_LOCK_CHECK (server);
+  protected_change_watch (server, watch,
+                          NULL, NULL,
+                          _dbus_watch_list_toggle_watch,
+                          enabled);
+}
+
+/** Function to be called in protected_change_timeout() with refcount held */
+typedef dbus_bool_t (* DBusTimeoutAddFunction)    (DBusTimeoutList *list,
+                                                   DBusTimeout     *timeout);
+/** Function to be called in protected_change_timeout() with refcount held */
+typedef void        (* DBusTimeoutRemoveFunction) (DBusTimeoutList *list,
+                                                   DBusTimeout     *timeout);
+/** Function to be called in protected_change_timeout() with refcount held */
+typedef void        (* DBusTimeoutToggleFunction) (DBusTimeoutList *list,
+                                                   DBusTimeout     *timeout,
+                                                   dbus_bool_t      enabled);
+
+
+static dbus_bool_t
+protected_change_timeout (DBusServer               *server,
+                          DBusTimeout              *timeout,
+                          DBusTimeoutAddFunction    add_function,
+                          DBusTimeoutRemoveFunction remove_function,
+                          DBusTimeoutToggleFunction toggle_function,
+                          dbus_bool_t               enabled)
+{
+  DBusTimeoutList *timeouts;
+  dbus_bool_t retval;
+  
+  HAVE_LOCK_CHECK (server);
+
+  /* This isn't really safe or reasonable; a better pattern is the "do everything, then
+   * drop lock and call out" one; but it has to be propagated up through all callers
+   */
+  
+  timeouts = server->timeouts;
+  if (timeouts)
+    {
+      server->timeouts = NULL;
+      _dbus_server_ref_unlocked (server);
+      SERVER_UNLOCK (server);
+
+      if (add_function)
+        retval = (* add_function) (timeouts, timeout);
+      else if (remove_function)
+        {
+          retval = TRUE;
+          (* remove_function) (timeouts, timeout);
+        }
+      else
+        {
+          retval = TRUE;
+          (* toggle_function) (timeouts, timeout, enabled);
+        }
+      
+      SERVER_LOCK (server);
+      server->timeouts = timeouts;
+      _dbus_server_unref_unlocked (server);
+
+      return retval;
+    }
+  else
+    return FALSE;
+}
+
+/**
+ * Adds a timeout for this server, chaining out to
+ * application-provided timeout handlers. The timeout should be
+ * repeatedly handled with dbus_timeout_handle() at its given interval
+ * until it is removed.
+ *
+ * @param server the server.
+ * @param timeout the timeout to add.
+ */
+dbus_bool_t
+_dbus_server_add_timeout (DBusServer  *server,
+                         DBusTimeout *timeout)
+{
+  return protected_change_timeout (server, timeout,
+                                   _dbus_timeout_list_add_timeout,
+                                   NULL, NULL, FALSE);
+}
+
+/**
+ * Removes a timeout previously added with _dbus_server_add_timeout().
+ *
+ * @param server the server.
+ * @param timeout the timeout to remove.
+ */
+void
+_dbus_server_remove_timeout (DBusServer  *server,
+                            DBusTimeout *timeout)
+{
+  protected_change_timeout (server, timeout,
+                            NULL,
+                            _dbus_timeout_list_remove_timeout,
+                            NULL, FALSE);
+}
+
+/**
+ * Toggles a timeout and notifies app via server's
+ * DBusTimeoutToggledFunction if available. It's an error to call this
+ * function on a timeout that was not previously added.
+ *
+ * @param server the server.
+ * @param timeout the timeout to toggle.
+ * @param enabled whether to enable or disable
+ */
+void
+_dbus_server_toggle_timeout (DBusServer  *server,
+                             DBusTimeout *timeout,
+                             dbus_bool_t  enabled)
+{
+  protected_change_timeout (server, timeout,
+                            NULL, NULL,
+                            _dbus_timeout_list_toggle_timeout,
+                            enabled);
+}
+
+
+/**
+ * Like dbus_server_ref() but does not acquire the lock (must already be held)
+ *
+ * @param server the server.
+ */
+void
+_dbus_server_ref_unlocked (DBusServer *server)
+{
+  _dbus_assert (server != NULL);
+  _dbus_assert (server->refcount.value > 0);
+  
+  HAVE_LOCK_CHECK (server);
+
+#ifdef DBUS_HAVE_ATOMIC_INT
+  _dbus_atomic_inc (&server->refcount);
+#else
+  _dbus_assert (server->refcount.value > 0);
+
+  server->refcount.value += 1;
+#endif
+}
+
+/**
+ * Like dbus_server_unref() but does not acquire the lock (must already be held)
+ *
+ * @param server the server.
+ */
+void
+_dbus_server_unref_unlocked (DBusServer *server)
+{
+  dbus_bool_t last_unref;
+
+  /* Keep this in sync with dbus_server_unref */
+  
+  _dbus_assert (server != NULL);
+  _dbus_assert (server->refcount.value > 0);
+
+  HAVE_LOCK_CHECK (server);
+  
+#ifdef DBUS_HAVE_ATOMIC_INT
+  last_unref = (_dbus_atomic_dec (&server->refcount) == 1);
+#else
+  _dbus_assert (server->refcount.value > 0);
+
+  server->refcount.value -= 1;
+  last_unref = (server->refcount.value == 0);
+#endif
+  
+  if (last_unref)
+    {
+      _dbus_assert (server->disconnected);
+      
+      SERVER_UNLOCK (server);
+      
+      _dbus_assert (server->vtable->finalize != NULL);
+      
+      (* server->vtable->finalize) (server);
+    }
+}
+
+/** @} */
+
+/**
+ * @addtogroup DBusServer
+ *
+ * @{
+ */
+
+
+/**
+ * @typedef DBusServer
+ *
+ * An opaque object representing a server that listens for
+ * connections from other applications. Each time a connection
+ * is made, a new DBusConnection is created and made available
+ * via an application-provided DBusNewConnectionFunction.
+ * The DBusNewConnectionFunction is provided with
+ * dbus_server_set_new_connection_function().
+ * 
+ */
+
+static const struct {
+  DBusServerListenResult (* func) (DBusAddressEntry *entry,
+                                   DBusServer      **server_p,
+                                   DBusError        *error);
+} listen_funcs[] = {
+  { _dbus_server_listen_socket }
+  , { _dbus_server_listen_platform_specific }
+#ifdef DBUS_BUILD_TESTS
+  , { _dbus_server_listen_debug_pipe }
+#endif
+};
+
+/**
+ * Listens for new connections on the given address.  If there are
+ * multiple semicolon-separated address entries in the address, tries
+ * each one and listens on the first one that works.
+ * 
+ * Returns #NULL and sets error if listening fails for any reason.
+ * Otherwise returns a new #DBusServer.
+ * dbus_server_set_new_connection_function(),
+ * dbus_server_set_watch_functions(), and
+ * dbus_server_set_timeout_functions() should be called immediately to
+ * render the server fully functional.
+ *
+ * To free the server, applications must call first
+ * dbus_server_disconnect() and then dbus_server_unref().
+ * 
+ * @param address the address of this server.
+ * @param error location to store reason for failure.
+ * @returns a new #DBusServer, or #NULL on failure.
+ * 
+ */
+DBusServer*
+dbus_server_listen (const char     *address,
+                    DBusError      *error)
+{
+  DBusServer *server;
+  DBusAddressEntry **entries;
+  int len, i;
+  DBusError first_connect_error = DBUS_ERROR_INIT;
+  dbus_bool_t handled_once;
+  
+  _dbus_return_val_if_fail (address != NULL, NULL);
+  _dbus_return_val_if_error_is_set (error, NULL);
+  
+  if (!dbus_parse_address (address, &entries, &len, error))
+    return NULL;
+
+  server = NULL;
+  handled_once = FALSE;
+
+  for (i = 0; i < len; i++)
+    {
+      int j;
+
+      for (j = 0; j < (int) _DBUS_N_ELEMENTS (listen_funcs); ++j)
+        {
+          DBusServerListenResult result;
+          DBusError tmp_error = DBUS_ERROR_INIT;
+
+          result = (* listen_funcs[j].func) (entries[i],
+                                             &server,
+                                             &tmp_error);
+
+          if (result == DBUS_SERVER_LISTEN_OK)
+            {
+              _dbus_assert (server != NULL);
+              _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
+              handled_once = TRUE;
+              goto out;
+            }
+          else if (result == DBUS_SERVER_LISTEN_BAD_ADDRESS)
+            {
+              _dbus_assert (server == NULL);
+              _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
+              dbus_move_error (&tmp_error, error);
+              handled_once = TRUE;
+              goto out;
+            }
+          else if (result == DBUS_SERVER_LISTEN_NOT_HANDLED)
+            {
+              _dbus_assert (server == NULL);
+              _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
+
+              /* keep trying addresses */
+            }
+          else if (result == DBUS_SERVER_LISTEN_DID_NOT_CONNECT)
+            {
+              _dbus_assert (server == NULL);
+              _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
+              if (!dbus_error_is_set (&first_connect_error))
+                dbus_move_error (&tmp_error, &first_connect_error);
+              else
+                dbus_error_free (&tmp_error);
+
+              handled_once = TRUE;
+              
+              /* keep trying addresses */
+            }
+        }
+
+      _dbus_assert (server == NULL);
+      _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+    }
+
+ out:
+
+  if (!handled_once)
+    {
+      _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+      if (len > 0)
+        dbus_set_error (error,
+                       DBUS_ERROR_BAD_ADDRESS,
+                       "Unknown address type '%s'",
+                       dbus_address_entry_get_method (entries[0]));
+      else
+        dbus_set_error (error,
+                        DBUS_ERROR_BAD_ADDRESS,
+                        "Empty address '%s'",
+                        address);
+    }
+  
+  dbus_address_entries_free (entries);
+
+  if (server == NULL)
+    {
+      _dbus_assert (error == NULL || dbus_error_is_set (&first_connect_error) ||
+                   dbus_error_is_set (error));
+      
+      if (error && dbus_error_is_set (error))
+        {
+          /* already set the error */
+        }
+      else
+        {
+          /* didn't set the error but either error should be
+           * NULL or first_connect_error should be set.
+           */
+          _dbus_assert (error == NULL || dbus_error_is_set (&first_connect_error));
+          dbus_move_error (&first_connect_error, error);
+        }
+
+      _DBUS_ASSERT_ERROR_IS_CLEAR (&first_connect_error); /* be sure we freed it */
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+
+      return NULL;
+    }
+  else
+    {
+      _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+      return server;
+    }
+}
+
+/**
+ * Increments the reference count of a DBusServer.
+ *
+ * @param server the server.
+ * @returns the server
+ */
+DBusServer *
+dbus_server_ref (DBusServer *server)
+{
+  _dbus_return_val_if_fail (server != NULL, NULL);
+  _dbus_return_val_if_fail (server->refcount.value > 0, NULL);
+
+#ifdef DBUS_HAVE_ATOMIC_INT
+  _dbus_atomic_inc (&server->refcount);
+#else
+  SERVER_LOCK (server);
+  _dbus_assert (server->refcount.value > 0);
+
+  server->refcount.value += 1;
+  SERVER_UNLOCK (server);
+#endif
+
+  return server;
+}
+
+/**
+ * Decrements the reference count of a DBusServer.  Finalizes the
+ * server if the reference count reaches zero.
+ *
+ * The server must be disconnected before the refcount reaches zero.
+ *
+ * @param server the server.
+ */
+void
+dbus_server_unref (DBusServer *server)
+{
+  dbus_bool_t last_unref;
+
+  /* keep this in sync with unref_unlocked */
+  
+  _dbus_return_if_fail (server != NULL);
+  _dbus_return_if_fail (server->refcount.value > 0);
+
+#ifdef DBUS_HAVE_ATOMIC_INT
+  last_unref = (_dbus_atomic_dec (&server->refcount) == 1);
+#else
+  SERVER_LOCK (server);
+  
+  _dbus_assert (server->refcount.value > 0);
+
+  server->refcount.value -= 1;
+  last_unref = (server->refcount.value == 0);
+  
+  SERVER_UNLOCK (server);
+#endif
+  
+  if (last_unref)
+    {
+      /* lock not held! */
+      _dbus_assert (server->disconnected);
+      
+      _dbus_assert (server->vtable->finalize != NULL);
+      
+      (* server->vtable->finalize) (server);
+    }
+}
+
+/**
+ * Releases the server's address and stops listening for
+ * new clients. If called more than once, only the first
+ * call has an effect. Does not modify the server's
+ * reference count.
+ * 
+ * @param server the server.
+ */
+void
+dbus_server_disconnect (DBusServer *server)
+{
+  _dbus_return_if_fail (server != NULL);
+  _dbus_return_if_fail (server->refcount.value > 0);
+
+  SERVER_LOCK (server);
+  _dbus_server_ref_unlocked (server);
+  
+  _dbus_assert (server->vtable->disconnect != NULL);
+
+  if (!server->disconnected)
+    {
+      /* this has to be first so recursive calls to disconnect don't happen */
+      server->disconnected = TRUE;
+      
+      (* server->vtable->disconnect) (server);
+    }
+
+  SERVER_UNLOCK (server);
+  dbus_server_unref (server);
+}
+
+/**
+ * Returns #TRUE if the server is still listening for new connections.
+ *
+ * @param server the server.
+ */
+dbus_bool_t
+dbus_server_get_is_connected (DBusServer *server)
+{
+  dbus_bool_t retval;
+  
+  _dbus_return_val_if_fail (server != NULL, FALSE);
+
+  SERVER_LOCK (server);
+  retval = !server->disconnected;
+  SERVER_UNLOCK (server);
+
+  return retval;
+}
+
+/**
+ * Returns the address of the server, as a newly-allocated
+ * string which must be freed by the caller.
+ *
+ * @param server the server
+ * @returns the address or #NULL if no memory
+ */
+char*
+dbus_server_get_address (DBusServer *server)
+{
+  char *retval;
+  
+  _dbus_return_val_if_fail (server != NULL, NULL);
+
+  SERVER_LOCK (server);
+  retval = _dbus_strdup (server->address);
+  SERVER_UNLOCK (server);
+
+  return retval;
+}
+
+/**
+ * Returns the unique ID of the server, as a newly-allocated
+ * string which must be freed by the caller. This ID is
+ * normally used by clients to tell when two #DBusConnection
+ * would be equivalent (because the server address passed
+ * to dbus_connection_open() will have the same guid in the
+ * two cases). dbus_connection_open() can re-use an existing
+ * connection with the same ID instead of opening a new
+ * connection.
+ *
+ * This is an ID unique to each #DBusServer. Remember that
+ * a #DBusServer represents only one mode of connecting,
+ * so e.g. a bus daemon can listen on multiple addresses
+ * which will mean it has multiple #DBusServer each with
+ * their own ID.
+ *
+ * The ID is not a UUID in the sense of RFC4122; the details
+ * are explained in the D-Bus specification.
+ *
+ * @param server the server
+ * @returns the id of the server or #NULL if no memory
+ */
+char*
+dbus_server_get_id (DBusServer *server)
+{
+  char *retval;
+  
+  _dbus_return_val_if_fail (server != NULL, NULL);
+
+  SERVER_LOCK (server);
+  retval = NULL;
+  _dbus_string_copy_data (&server->guid_hex, &retval);
+  SERVER_UNLOCK (server);
+
+  return retval;
+}
+
+/**
+ * Sets a function to be used for handling new connections.  The given
+ * function is passed each new connection as the connection is
+ * created. If the new connection function increments the connection's
+ * reference count, the connection will stay alive. Otherwise, the
+ * connection will be unreferenced and closed. The new connection
+ * function may also close the connection itself, which is considered
+ * good form if the connection is not wanted.
+ *
+ * The connection here is private in the sense of
+ * dbus_connection_open_private(), so if the new connection function
+ * keeps a reference it must arrange for the connection to be closed.
+ * i.e. libdbus does not own this connection once the new connection
+ * function takes a reference.
+ *
+ * @param server the server.
+ * @param function a function to handle new connections.
+ * @param data data to pass to the new connection handler.
+ * @param free_data_function function to free the data.
+ */
+void
+dbus_server_set_new_connection_function (DBusServer                *server,
+                                         DBusNewConnectionFunction  function,
+                                         void                      *data,
+                                         DBusFreeFunction           free_data_function)
+{
+  DBusFreeFunction old_free_function;
+  void *old_data;
+  
+  _dbus_return_if_fail (server != NULL);
+
+  SERVER_LOCK (server);
+  old_free_function = server->new_connection_free_data_function;
+  old_data = server->new_connection_data;
+  
+  server->new_connection_function = function;
+  server->new_connection_data = data;
+  server->new_connection_free_data_function = free_data_function;
+  SERVER_UNLOCK (server);
+    
+  if (old_free_function != NULL)
+    (* old_free_function) (old_data);
+}
+
+/**
+ * Sets the watch functions for the server. These functions are
+ * responsible for making the application's main loop aware of file
+ * descriptors that need to be monitored for events.
+ *
+ * This function behaves exactly like dbus_connection_set_watch_functions();
+ * see the documentation for that routine.
+ *
+ * @param server the server.
+ * @param add_function function to begin monitoring a new descriptor.
+ * @param remove_function function to stop monitoring a descriptor.
+ * @param toggled_function function to notify when the watch is enabled/disabled
+ * @param data data to pass to add_function and remove_function.
+ * @param free_data_function function to be called to free the data.
+ * @returns #FALSE on failure (no memory)
+ */
+dbus_bool_t
+dbus_server_set_watch_functions (DBusServer              *server,
+                                 DBusAddWatchFunction     add_function,
+                                 DBusRemoveWatchFunction  remove_function,
+                                 DBusWatchToggledFunction toggled_function,
+                                 void                    *data,
+                                 DBusFreeFunction         free_data_function)
+{
+  dbus_bool_t result;
+  DBusWatchList *watches;
+  
+  _dbus_return_val_if_fail (server != NULL, FALSE);
+
+  SERVER_LOCK (server);
+  watches = server->watches;
+  server->watches = NULL;
+  if (watches)
+    {
+      SERVER_UNLOCK (server);
+      result = _dbus_watch_list_set_functions (watches,
+                                               add_function,
+                                               remove_function,
+                                               toggled_function,
+                                               data,
+                                               free_data_function);
+      SERVER_LOCK (server);
+    }
+  else
+    {
+      _dbus_warn_check_failed ("Re-entrant call to %s\n", _DBUS_FUNCTION_NAME);
+      result = FALSE;
+    }
+  server->watches = watches;
+  SERVER_UNLOCK (server);
+  
+  return result;
+}
+
+/**
+ * Sets the timeout functions for the server. These functions are
+ * responsible for making the application's main loop aware of timeouts.
+ *
+ * This function behaves exactly like dbus_connection_set_timeout_functions();
+ * see the documentation for that routine.
+ *
+ * @param server the server.
+ * @param add_function function to add a timeout.
+ * @param remove_function function to remove a timeout.
+ * @param toggled_function function to notify when the timeout is enabled/disabled
+ * @param data data to pass to add_function and remove_function.
+ * @param free_data_function function to be called to free the data.
+ * @returns #FALSE on failure (no memory)
+ */
+dbus_bool_t
+dbus_server_set_timeout_functions (DBusServer                *server,
+                                  DBusAddTimeoutFunction     add_function,
+                                  DBusRemoveTimeoutFunction  remove_function,
+                                   DBusTimeoutToggledFunction toggled_function,
+                                  void                      *data,
+                                  DBusFreeFunction           free_data_function)
+{
+  dbus_bool_t result;
+  DBusTimeoutList *timeouts;
+  
+  _dbus_return_val_if_fail (server != NULL, FALSE);
+
+  SERVER_LOCK (server);
+  timeouts = server->timeouts;
+  server->timeouts = NULL;
+  if (timeouts)
+    {
+      SERVER_UNLOCK (server);
+      result = _dbus_timeout_list_set_functions (timeouts,
+                                                 add_function,
+                                                 remove_function,
+                                                 toggled_function,
+                                                 data,
+                                                 free_data_function);
+      SERVER_LOCK (server);
+    }
+  else
+    {
+      _dbus_warn_check_failed ("Re-entrant call to %s\n", _DBUS_FUNCTION_NAME);
+      result = FALSE;
+    }
+  server->timeouts = timeouts;
+  SERVER_UNLOCK (server);
+  
+  return result;
+}
+
+/**
+ * Sets the authentication mechanisms that this server offers to
+ * clients, as a #NULL-terminated array of mechanism names. This
+ * function only affects connections created <em>after</em> it is
+ * called.  Pass #NULL instead of an array to use all available
+ * mechanisms (this is the default behavior).
+ *
+ * The D-Bus specification describes some of the supported mechanisms.
+ *
+ * @param server the server
+ * @param mechanisms #NULL-terminated array of mechanisms
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+dbus_server_set_auth_mechanisms (DBusServer  *server,
+                                 const char **mechanisms)
+{
+  char **copy;
+
+  _dbus_return_val_if_fail (server != NULL, FALSE);
+
+  SERVER_LOCK (server);
+  
+  if (mechanisms != NULL)
+    {
+      copy = _dbus_dup_string_array (mechanisms);
+      if (copy == NULL)
+        return FALSE;
+    }
+  else
+    copy = NULL;
+
+  dbus_free_string_array (server->auth_mechanisms);
+  server->auth_mechanisms = copy;
+
+  SERVER_UNLOCK (server);
+  
+  return TRUE;
+}
+
+
+static DBusDataSlotAllocator slot_allocator;
+_DBUS_DEFINE_GLOBAL_LOCK (server_slots);
+
+/**
+ * Allocates an integer ID to be used for storing application-specific
+ * data on any DBusServer. The allocated ID may then be used
+ * with dbus_server_set_data() and dbus_server_get_data().
+ * The slot must be initialized with -1. If a nonnegative
+ * slot is passed in, the refcount is incremented on that
+ * slot, rather than creating a new slot.
+ *  
+ * The allocated slot is global, i.e. all DBusServer objects will have
+ * a slot with the given integer ID reserved.
+ *
+ * @param slot_p address of global variable storing the slot ID
+ * @returns #FALSE on no memory
+ */
+dbus_bool_t
+dbus_server_allocate_data_slot (dbus_int32_t *slot_p)
+{
+  return _dbus_data_slot_allocator_alloc (&slot_allocator,
+                                          (DBusMutex **)&_DBUS_LOCK_NAME (server_slots),
+                                          slot_p);
+}
+
+/**
+ * Deallocates a global ID for server data slots.
+ * dbus_server_get_data() and dbus_server_set_data()
+ * may no longer be used with this slot.
+ * Existing data stored on existing DBusServer objects
+ * will be freed when the server is finalized,
+ * but may not be retrieved (and may only be replaced
+ * if someone else reallocates the slot).
+ *
+ * @param slot_p address of the slot to deallocate
+ */
+void
+dbus_server_free_data_slot (dbus_int32_t *slot_p)
+{
+  _dbus_return_if_fail (*slot_p >= 0);
+  
+  _dbus_data_slot_allocator_free (&slot_allocator, slot_p);
+}
+
+/**
+ * Stores a pointer on a DBusServer, along
+ * with an optional function to be used for freeing
+ * the data when the data is set again, or when
+ * the server is finalized. The slot number
+ * must have been allocated with dbus_server_allocate_data_slot().
+ *
+ * @param server the server
+ * @param slot the slot number
+ * @param data the data to store
+ * @param free_data_func finalizer function for the data
+ * @returns #TRUE if there was enough memory to store the data
+ */
+dbus_bool_t
+dbus_server_set_data (DBusServer       *server,
+                      int               slot,
+                      void             *data,
+                      DBusFreeFunction  free_data_func)
+{
+  DBusFreeFunction old_free_func;
+  void *old_data;
+  dbus_bool_t retval;
+
+  _dbus_return_val_if_fail (server != NULL, FALSE);
+
+  SERVER_LOCK (server);
+  
+  retval = _dbus_data_slot_list_set (&slot_allocator,
+                                     &server->slot_list,
+                                     slot, data, free_data_func,
+                                     &old_free_func, &old_data);
+
+
+  SERVER_UNLOCK (server);
+  
+  if (retval)
+    {
+      /* Do the actual free outside the server lock */
+      if (old_free_func)
+        (* old_free_func) (old_data);
+    }
+
+  return retval;
+}
+
+/**
+ * Retrieves data previously set with dbus_server_set_data().
+ * The slot must still be allocated (must not have been freed).
+ *
+ * @param server the server
+ * @param slot the slot to get data from
+ * @returns the data, or #NULL if not found
+ */
+void*
+dbus_server_get_data (DBusServer   *server,
+                      int           slot)
+{
+  void *res;
+
+  _dbus_return_val_if_fail (server != NULL, NULL);
+  
+  SERVER_LOCK (server);
+  
+  res = _dbus_data_slot_list_get (&slot_allocator,
+                                  &server->slot_list,
+                                  slot);
+
+  SERVER_UNLOCK (server);
+  
+  return res;
+}
+
+/** @} */
+
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-test.h"
+#include <string.h>
+
+dbus_bool_t
+_dbus_server_test (void)
+{
+  const char *valid_addresses[] = {
+    "tcp:port=1234",
+    "tcp:host=localhost,port=1234",
+    "tcp:host=localhost,port=1234;tcp:port=5678",
+#ifdef DBUS_UNIX
+    "unix:path=./boogie",
+    "tcp:port=1234;unix:path=./boogie",
+#endif
+  };
+
+  DBusServer *server;
+  int i;
+  
+  for (i = 0; i < _DBUS_N_ELEMENTS (valid_addresses); i++)
+    {
+      DBusError error = DBUS_ERROR_INIT;
+      char *address;
+      char *id;
+
+      server = dbus_server_listen (valid_addresses[i], &error);
+      if (server == NULL)
+        {
+          _dbus_warn ("server listen error: %s: %s\n", error.name, error.message);
+          dbus_error_free (&error);
+          _dbus_assert_not_reached ("Failed to listen for valid address.");
+        }
+
+      id = dbus_server_get_id (server);
+      _dbus_assert (id != NULL);
+      address = dbus_server_get_address (server);
+      _dbus_assert (address != NULL);
+
+      if (strstr (address, id) == NULL)
+        {
+          _dbus_warn ("server id '%s' is not in the server address '%s'\n",
+                      id, address);
+          _dbus_assert_not_reached ("bad server id or address");
+        }
+
+      dbus_free (id);
+      dbus_free (address);
+      
+      dbus_server_disconnect (server);
+      dbus_server_unref (server);
+    }
+
+  return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-server.h b/src/dbus/dbus-server.h
new file mode 100644 (file)
index 0000000..77f8788
--- /dev/null
@@ -0,0 +1,91 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-server.h DBusServer object
+ *
+ * Copyright (C) 2002, 2003  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_SERVER_H
+#define DBUS_SERVER_H
+
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-message.h>
+#include <dbus/dbus-connection.h>
+#include <dbus/dbus-protocol.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusServer
+ * @{
+ */
+
+typedef struct DBusServer DBusServer;
+
+/** Called when a new connection to the server is available. Must reference and save the new
+ * connection, or close the new connection. Set with dbus_server_set_new_connection_function().
+ */
+typedef void (* DBusNewConnectionFunction) (DBusServer     *server,
+                                            DBusConnection *new_connection,
+                                            void           *data);
+
+DBusServer* dbus_server_listen           (const char     *address,
+                                          DBusError      *error);
+DBusServer* dbus_server_ref              (DBusServer     *server);
+void        dbus_server_unref            (DBusServer     *server);
+void        dbus_server_disconnect       (DBusServer     *server);
+dbus_bool_t dbus_server_get_is_connected (DBusServer     *server);
+char*       dbus_server_get_address      (DBusServer     *server);
+char*       dbus_server_get_id           (DBusServer     *server);
+void        dbus_server_set_new_connection_function (DBusServer                *server,
+                                                     DBusNewConnectionFunction  function,
+                                                     void                      *data,
+                                                     DBusFreeFunction           free_data_function);
+dbus_bool_t dbus_server_set_watch_functions         (DBusServer                *server,
+                                                     DBusAddWatchFunction       add_function,
+                                                     DBusRemoveWatchFunction    remove_function,
+                                                     DBusWatchToggledFunction   toggled_function,
+                                                     void                      *data,
+                                                     DBusFreeFunction           free_data_function);
+dbus_bool_t dbus_server_set_timeout_functions       (DBusServer                *server,
+                                                     DBusAddTimeoutFunction     add_function,
+                                                     DBusRemoveTimeoutFunction  remove_function,
+                                                     DBusTimeoutToggledFunction toggled_function,
+                                                     void                      *data,
+                                                     DBusFreeFunction           free_data_function);
+dbus_bool_t dbus_server_set_auth_mechanisms         (DBusServer                *server,
+                                                     const char               **mechanisms);
+
+dbus_bool_t dbus_server_allocate_data_slot (dbus_int32_t     *slot_p);
+void        dbus_server_free_data_slot     (dbus_int32_t     *slot_p);
+dbus_bool_t dbus_server_set_data           (DBusServer       *server,
+                                            int               slot,
+                                            void             *data,
+                                            DBusFreeFunction  free_data_func);
+void*       dbus_server_get_data           (DBusServer       *server,
+                                            int               slot);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_SERVER_H */
diff --git a/src/dbus/dbus-sha.c b/src/dbus/dbus-sha.c
new file mode 100644 (file)
index 0000000..8ec50b6
--- /dev/null
@@ -0,0 +1,968 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sha.c SHA-1 implementation
+ *
+ * Copyright (C) 2003 Red Hat Inc.
+ * Copyright (C) 1995 A. M. Kuchling
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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-internals.h"
+#include "dbus-sha.h"
+#include "dbus-marshal-basic.h" /* for byteswap routines */
+#include <string.h>
+
+/* The following comments have the history of where this code
+ * comes from. I actually copied it from GNet in GNOME CVS.
+ * - hp@redhat.com
+ */
+
+/*
+ *  sha.h : Implementation of the Secure Hash Algorithm
+ *
+ * Part of the Python Cryptography Toolkit, version 1.0.0
+ *
+ * Copyright (C) 1995, A.M. Kuchling
+ *
+ * Distribute and use freely; there are no restrictions on further
+ * dissemination and usage except those imposed by the laws of your
+ * country of residence.
+ *
+ */
+
+/* SHA: NIST's Secure Hash Algorithm */
+
+/* Based on SHA code originally posted to sci.crypt by Peter Gutmann
+   in message <30ajo5$oe8@ccu2.auckland.ac.nz>.
+   Modified to test for endianness on creation of SHA objects by AMK.
+   Also, the original specification of SHA was found to have a weakness
+   by NSA/NIST.  This code implements the fixed version of SHA.
+*/
+
+/* Here's the first paragraph of Peter Gutmann's posting:
+
+The following is my SHA (FIPS 180) code updated to allow use of the "fixed"
+SHA, thanks to Jim Gillogly and an anonymous contributor for the information on
+what's changed in the new version.  The fix is a simple change which involves
+adding a single rotate in the initial expansion function.  It is unknown
+whether this is an optimal solution to the problem which was discovered in the
+SHA or whether it's simply a bandaid which fixes the problem with a minimum of
+effort (for example the reengineering of a great many Capstone chips).
+*/
+
+/**
+ * @defgroup DBusSHA SHA implementation
+ * @ingroup  DBusInternals
+ * @brief SHA-1 hash
+ *
+ * Types and functions related to computing SHA-1 hash.
+ */
+
+/**
+ * @defgroup DBusSHAInternals SHA implementation details
+ * @ingroup  DBusInternals
+ * @brief Internals of SHA implementation.
+ *
+ * The implementation of SHA-1 (see http://www.itl.nist.gov/fipspubs/fip180-1.htm).
+ * This SHA implementation was written by A.M. Kuchling
+ *
+ * @{
+ */
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+/* The SHA block size and message digest sizes, in bytes */
+
+#define SHA_DATASIZE    64
+#define SHA_DIGESTSIZE  20
+
+/* The SHA f()-functions.  The f1 and f3 functions can be optimized to
+   save one boolean operation each - thanks to Rich Schroeppel,
+   rcs@cs.arizona.edu for discovering this */
+
+/*#define f1(x,y,z) ( ( x & y ) | ( ~x & z ) )          // Rounds  0-19 */
+#define f1(x,y,z)  ( z ^ ( x & ( y ^ z ) ) )           /* Rounds  0-19 */
+#define f2(x,y,z)  ( x ^ y ^ z )                       /* Rounds 20-39 */
+/*#define f3(x,y,z) ( ( x & y ) | ( x & z ) | ( y & z ) )   // Rounds 40-59 */
+#define f3(x,y,z)  ( ( x & y ) | ( z & ( x | y ) ) )   /* Rounds 40-59 */
+#define f4(x,y,z)  ( x ^ y ^ z )                       /* Rounds 60-79 */
+
+/* The SHA Mysterious Constants */
+
+#define K1  0x5A827999L                                 /* Rounds  0-19 */
+#define K2  0x6ED9EBA1L                                 /* Rounds 20-39 */
+#define K3  0x8F1BBCDCL                                 /* Rounds 40-59 */
+#define K4  0xCA62C1D6L                                 /* Rounds 60-79 */
+
+/* SHA initial values */
+
+#define h0init  0x67452301L
+#define h1init  0xEFCDAB89L
+#define h2init  0x98BADCFEL
+#define h3init  0x10325476L
+#define h4init  0xC3D2E1F0L
+
+/* Note that it may be necessary to add parentheses to these macros if they
+   are to be called with expressions as arguments */
+/* 32-bit rotate left - kludged with shifts */
+
+#define ROTL(n,X) ( ( ( X ) << n ) | ( ( X ) >> ( 32 - n ) ) )
+
+/* The initial expanding function.  The hash function is defined over an
+   80-word expanded input array W, where the first 16 are copies of the input
+   data, and the remaining 64 are defined by
+
+        W[ i ] = W[ i - 16 ] ^ W[ i - 14 ] ^ W[ i - 8 ] ^ W[ i - 3 ]
+
+   This implementation generates these values on the fly in a circular
+   buffer - thanks to Colin Plumb, colin@nyx10.cs.du.edu for this
+   optimization.
+
+   The updated SHA changes the expanding function by adding a rotate of 1
+   bit.  Thanks to Jim Gillogly, jim@rand.org, and an anonymous contributor
+   for this information */
+
+#define expand(W,i) ( W[ i & 15 ] = ROTL( 1, ( W[ i & 15 ] ^ W[ (i - 14) & 15 ] ^ \
+                                                 W[ (i - 8) & 15 ] ^ W[ (i - 3) & 15 ] ) ) )
+
+
+/* The prototype SHA sub-round.  The fundamental sub-round is:
+
+        a' = e + ROTL( 5, a ) + f( b, c, d ) + k + data;
+        b' = a;
+        c' = ROTL( 30, b );
+        d' = c;
+        e' = d;
+
+   but this is implemented by unrolling the loop 5 times and renaming the
+   variables ( e, a, b, c, d ) = ( a', b', c', d', e' ) each iteration.
+   This code is then replicated 20 times for each of the 4 functions, using
+   the next 20 values from the W[] array each time */
+
+#define subRound(a, b, c, d, e, f, k, data) \
+   ( e += ROTL( 5, a ) + f( b, c, d ) + k + data, b = ROTL( 30, b ) )
+
+#endif /* !DOXYGEN_SHOULD_SKIP_THIS */
+
+/* Perform the SHA transformation.  Note that this code, like MD5, seems to
+   break some optimizing compilers due to the complexity of the expressions
+   and the size of the basic block.  It may be necessary to split it into
+   sections, e.g. based on the four subrounds
+
+   Note that this corrupts the context->data area */
+
+static void
+SHATransform(dbus_uint32_t *digest, dbus_uint32_t *data)
+{
+  dbus_uint32_t A, B, C, D, E;     /* Local vars */
+  dbus_uint32_t eData[16];       /* Expanded data */
+
+  /* Set up first buffer and local data buffer */
+  A = digest[0];
+  B = digest[1];
+  C = digest[2];
+  D = digest[3];
+  E = digest[4];
+  memmove (eData, data, SHA_DATASIZE);
+
+  /* Heavy mangling, in 4 sub-rounds of 20 interations each. */
+  subRound (A, B, C, D, E, f1, K1, eData[0]);
+  subRound (E, A, B, C, D, f1, K1, eData[1]);
+  subRound (D, E, A, B, C, f1, K1, eData[2]);
+  subRound (C, D, E, A, B, f1, K1, eData[3]);
+  subRound (B, C, D, E, A, f1, K1, eData[4]);
+  subRound (A, B, C, D, E, f1, K1, eData[5]);
+  subRound (E, A, B, C, D, f1, K1, eData[6]);
+  subRound (D, E, A, B, C, f1, K1, eData[7]);
+  subRound (C, D, E, A, B, f1, K1, eData[8]);
+  subRound (B, C, D, E, A, f1, K1, eData[9]);
+  subRound (A, B, C, D, E, f1, K1, eData[10]);
+  subRound (E, A, B, C, D, f1, K1, eData[11]);
+  subRound (D, E, A, B, C, f1, K1, eData[12]);
+  subRound (C, D, E, A, B, f1, K1, eData[13]);
+  subRound (B, C, D, E, A, f1, K1, eData[14]);
+  subRound (A, B, C, D, E, f1, K1, eData[15]);
+  subRound (E, A, B, C, D, f1, K1, expand ( eData, 16) );
+  subRound (D, E, A, B, C, f1, K1, expand ( eData, 17) );
+  subRound (C, D, E, A, B, f1, K1, expand ( eData, 18) );
+  subRound (B, C, D, E, A, f1, K1, expand ( eData, 19) );
+
+  subRound (A, B, C, D, E, f2, K2, expand ( eData, 20) );
+  subRound (E, A, B, C, D, f2, K2, expand ( eData, 21) );
+  subRound (D, E, A, B, C, f2, K2, expand ( eData, 22) );
+  subRound (C, D, E, A, B, f2, K2, expand ( eData, 23) );
+  subRound (B, C, D, E, A, f2, K2, expand ( eData, 24) );
+  subRound (A, B, C, D, E, f2, K2, expand ( eData, 25) );
+  subRound (E, A, B, C, D, f2, K2, expand ( eData, 26) );
+  subRound (D, E, A, B, C, f2, K2, expand ( eData, 27) );
+  subRound (C, D, E, A, B, f2, K2, expand ( eData, 28) );
+  subRound (B, C, D, E, A, f2, K2, expand ( eData, 29) );
+  subRound (A, B, C, D, E, f2, K2, expand ( eData, 30) );
+  subRound (E, A, B, C, D, f2, K2, expand ( eData, 31) );
+  subRound (D, E, A, B, C, f2, K2, expand ( eData, 32) );
+  subRound (C, D, E, A, B, f2, K2, expand ( eData, 33) );
+  subRound (B, C, D, E, A, f2, K2, expand ( eData, 34) );
+  subRound (A, B, C, D, E, f2, K2, expand ( eData, 35) );
+  subRound (E, A, B, C, D, f2, K2, expand ( eData, 36) );
+  subRound (D, E, A, B, C, f2, K2, expand ( eData, 37) );
+  subRound (C, D, E, A, B, f2, K2, expand ( eData, 38) );
+  subRound (B, C, D, E, A, f2, K2, expand ( eData, 39) );
+
+  subRound (A, B, C, D, E, f3, K3, expand ( eData, 40) );
+  subRound (E, A, B, C, D, f3, K3, expand ( eData, 41) );
+  subRound (D, E, A, B, C, f3, K3, expand ( eData, 42) );
+  subRound (C, D, E, A, B, f3, K3, expand ( eData, 43) );
+  subRound (B, C, D, E, A, f3, K3, expand ( eData, 44) );
+  subRound (A, B, C, D, E, f3, K3, expand ( eData, 45) );
+  subRound (E, A, B, C, D, f3, K3, expand ( eData, 46) );
+  subRound (D, E, A, B, C, f3, K3, expand ( eData, 47) );
+  subRound (C, D, E, A, B, f3, K3, expand ( eData, 48) );
+  subRound (B, C, D, E, A, f3, K3, expand ( eData, 49) );
+  subRound (A, B, C, D, E, f3, K3, expand ( eData, 50) );
+  subRound (E, A, B, C, D, f3, K3, expand ( eData, 51) );
+  subRound (D, E, A, B, C, f3, K3, expand ( eData, 52) );
+  subRound (C, D, E, A, B, f3, K3, expand ( eData, 53) );
+  subRound (B, C, D, E, A, f3, K3, expand ( eData, 54) );
+  subRound (A, B, C, D, E, f3, K3, expand ( eData, 55) );
+  subRound (E, A, B, C, D, f3, K3, expand ( eData, 56) );
+  subRound (D, E, A, B, C, f3, K3, expand ( eData, 57) );
+  subRound (C, D, E, A, B, f3, K3, expand ( eData, 58) );
+  subRound (B, C, D, E, A, f3, K3, expand ( eData, 59) );
+
+  subRound (A, B, C, D, E, f4, K4, expand ( eData, 60) );
+  subRound (E, A, B, C, D, f4, K4, expand ( eData, 61) );
+  subRound (D, E, A, B, C, f4, K4, expand ( eData, 62) );
+  subRound (C, D, E, A, B, f4, K4, expand ( eData, 63) );
+  subRound (B, C, D, E, A, f4, K4, expand ( eData, 64) );
+  subRound (A, B, C, D, E, f4, K4, expand ( eData, 65) );
+  subRound (E, A, B, C, D, f4, K4, expand ( eData, 66) );
+  subRound (D, E, A, B, C, f4, K4, expand ( eData, 67) );
+  subRound (C, D, E, A, B, f4, K4, expand ( eData, 68) );
+  subRound (B, C, D, E, A, f4, K4, expand ( eData, 69) );
+  subRound (A, B, C, D, E, f4, K4, expand ( eData, 70) );
+  subRound (E, A, B, C, D, f4, K4, expand ( eData, 71) );
+  subRound (D, E, A, B, C, f4, K4, expand ( eData, 72) );
+  subRound (C, D, E, A, B, f4, K4, expand ( eData, 73) );
+  subRound (B, C, D, E, A, f4, K4, expand ( eData, 74) );
+  subRound (A, B, C, D, E, f4, K4, expand ( eData, 75) );
+  subRound (E, A, B, C, D, f4, K4, expand ( eData, 76) );
+  subRound (D, E, A, B, C, f4, K4, expand ( eData, 77) );
+  subRound (C, D, E, A, B, f4, K4, expand ( eData, 78) );
+  subRound (B, C, D, E, A, f4, K4, expand ( eData, 79) );
+
+  /* Build message digest */
+  digest[0] += A;
+  digest[1] += B;
+  digest[2] += C;
+  digest[3] += D;
+  digest[4] += E;
+}
+
+/* When run on a little-endian CPU we need to perform byte reversal on an
+   array of longwords. */
+
+#ifdef WORDS_BIGENDIAN
+#define swap_words(buffer, byte_count)
+#else
+static void
+swap_words (dbus_uint32_t *buffer,
+            int            byte_count)
+{
+  byte_count /= sizeof (dbus_uint32_t);
+  while (byte_count--)
+    {
+      *buffer = DBUS_UINT32_SWAP_LE_BE (*buffer);
+      ++buffer;
+    }
+}
+#endif
+
+static void
+sha_init (DBusSHAContext *context)
+{
+  /* Set the h-vars to their initial values */
+  context->digest[0] = h0init;
+  context->digest[1] = h1init;
+  context->digest[2] = h2init;
+  context->digest[3] = h3init;
+  context->digest[4] = h4init;
+
+  /* Initialise bit count */
+  context->count_lo = context->count_hi = 0;
+}
+
+static void
+sha_append (DBusSHAContext      *context,
+            const unsigned char *buffer,
+            unsigned int         count)
+{
+  dbus_uint32_t tmp;
+  unsigned int dataCount;
+
+  /* Update bitcount */
+  tmp = context->count_lo;
+  if (( context->count_lo = tmp + ( ( dbus_uint32_t) count << 3) ) < tmp)
+    context->count_hi++;             /* Carry from low to high */
+  context->count_hi += count >> 29;
+
+  /* Get count of bytes already in data */
+  dataCount = (int) (tmp >> 3) & 0x3F;
+
+  /* Handle any leading odd-sized chunks */
+  if (dataCount)
+    {
+      unsigned char *p = (unsigned char *) context->data + dataCount;
+
+      dataCount = SHA_DATASIZE - dataCount;
+      if (count < dataCount)
+        {
+          memmove (p, buffer, count);
+          return;
+        }
+      memmove (p, buffer, dataCount);
+      swap_words (context->data, SHA_DATASIZE);
+      SHATransform (context->digest, context->data);
+      buffer += dataCount;
+      count -= dataCount;
+    }
+
+  /* Process data in SHA_DATASIZE chunks */
+  while (count >= SHA_DATASIZE)
+    {
+      memmove (context->data, buffer, SHA_DATASIZE);
+      swap_words (context->data, SHA_DATASIZE);
+      SHATransform (context->digest, context->data);
+      buffer += SHA_DATASIZE;
+      count -= SHA_DATASIZE;
+    }
+
+  /* Handle any remaining bytes of data. */
+  memmove (context->data, buffer, count);
+}
+
+
+/* Final wrapup - pad to SHA_DATASIZE-byte boundary with the bit pattern
+   1 0* (64-bit count of bits processed, MSB-first) */
+
+static void
+sha_finish (DBusSHAContext *context, unsigned char digest[20])
+{
+  int count;
+  unsigned char *data_p;
+
+  /* Compute number of bytes mod 64 */
+  count = (int) context->count_lo;
+  count = (count >> 3) & 0x3F;
+
+  /* Set the first char of padding to 0x80.  This is safe since there is
+     always at least one byte free */
+  data_p = (unsigned char *) context->data + count;
+  *data_p++ = 0x80;
+
+  /* Bytes of padding needed to make 64 bytes */
+  count = SHA_DATASIZE - 1 - count;
+
+  /* Pad out to 56 mod 64 */
+  if (count < 8)
+    {
+      /* Two lots of padding:  Pad the first block to 64 bytes */
+      memset (data_p, 0, count);
+      swap_words (context->data, SHA_DATASIZE);
+      SHATransform (context->digest, context->data);
+
+      /* Now fill the next block with 56 bytes */
+      memset (context->data, 0, SHA_DATASIZE - 8);
+    }
+  else
+    /* Pad block to 56 bytes */
+    memset (data_p, 0, count - 8);
+
+  /* Append length in bits and transform */
+  context->data[14] = context->count_hi;
+  context->data[15] = context->count_lo;
+
+  swap_words (context->data, SHA_DATASIZE - 8);
+  SHATransform (context->digest, context->data);
+  swap_words (context->digest, SHA_DIGESTSIZE);
+  memmove (digest, context->digest, SHA_DIGESTSIZE);
+}
+
+/** @} */ /* End of internals */
+
+/**
+ * @addtogroup DBusSHA
+ *
+ * @{
+ */
+
+/**
+ * Initializes the SHA context.
+ *
+ * @param context an uninitialized context, typically on the stack.
+ */
+void
+_dbus_sha_init (DBusSHAContext *context)
+{
+  sha_init (context);
+}
+
+/**
+ * Feeds more data into an existing shasum computation.
+ *
+ * @param context the SHA context
+ * @param data the additional data to hash
+ */
+void
+_dbus_sha_update (DBusSHAContext   *context,
+                  const DBusString *data)
+{
+  unsigned int inputLen;
+  const unsigned char *input;
+
+  input = (const unsigned char*) _dbus_string_get_const_data (data);
+  inputLen = _dbus_string_get_length (data);
+
+  sha_append (context, input, inputLen);
+}
+
+/**
+ * SHA finalization. Ends an SHA message-digest operation, writing the
+ * the message digest and zeroing the context.  The results are
+ * returned as a raw 20-byte digest, not as the ascii-hex-digits
+ * string form of the digest.
+ *
+ * @param context the SHA context
+ * @param results string to append the 20-byte SHA digest to
+ * @returns #FALSE if not enough memory to append the digest
+ *
+ */
+dbus_bool_t
+_dbus_sha_final (DBusSHAContext   *context,
+                 DBusString       *results)
+{
+  unsigned char digest[20];
+
+  sha_finish (context, digest);
+
+  if (!_dbus_string_append_len (results, digest, 20))
+    return FALSE;
+
+  /* some kind of security paranoia, though it seems pointless
+   * to me given the nonzeroed stuff flying around
+   */
+  memset ((void*)context, '\0', sizeof (DBusSHAContext));
+
+  return TRUE;
+}
+
+/**
+ * Computes the ASCII hex-encoded shasum of the given data and
+ * appends it to the output string.
+ *
+ * @param data input data to be hashed
+ * @param ascii_output string to append ASCII shasum to
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_sha_compute (const DBusString *data,
+                   DBusString       *ascii_output)
+{
+  DBusSHAContext context;
+  DBusString digest;
+
+  _dbus_sha_init (&context);
+
+  _dbus_sha_update (&context, data);
+
+  if (!_dbus_string_init (&digest))
+    return FALSE;
+
+  if (!_dbus_sha_final (&context, &digest))
+    goto error;
+
+  if (!_dbus_string_hex_encode (&digest, 0, ascii_output,
+                                _dbus_string_get_length (ascii_output)))
+    goto error;
+
+  _dbus_string_free (&digest);
+  
+  return TRUE;
+
+ error:
+  _dbus_string_free (&digest);
+  return FALSE;
+}
+
+/** @} */ /* end of exported functions */
+
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-test.h"
+#include <stdio.h>
+
+static dbus_bool_t
+check_sha_binary (const unsigned char *input,
+                  int                  input_len,
+                  const char          *expected)
+{
+  DBusString input_str;
+  DBusString expected_str;
+  DBusString results;
+
+  _dbus_string_init_const_len (&input_str, input, input_len);
+  _dbus_string_init_const (&expected_str, expected);
+
+  if (!_dbus_string_init (&results))
+    _dbus_assert_not_reached ("no memory for SHA-1 results");
+
+  if (!_dbus_sha_compute (&input_str, &results))
+    _dbus_assert_not_reached ("no memory for SHA-1 results");
+
+  if (!_dbus_string_equal (&expected_str, &results))
+    {
+      _dbus_warn ("Expected hash %s got %s for SHA-1 sum\n",
+                  expected,
+                  _dbus_string_get_const_data (&results));
+      _dbus_string_free (&results);
+      return FALSE;
+    }
+
+  _dbus_string_free (&results);
+  return TRUE;
+}
+
+static dbus_bool_t
+check_sha_str (const char *input,
+               const char *expected)
+{
+  return check_sha_binary (input, strlen (input), expected);
+}
+
+static dbus_bool_t
+decode_compact_string (const DBusString *line,
+                       DBusString       *decoded)
+{
+  int n_bits;
+  dbus_bool_t current_b;
+  int offset;
+  int next;
+  long val;
+  int length_bytes;
+  
+  offset = 0;
+  next = 0;
+
+  if (!_dbus_string_parse_int (line, offset, &val, &next))
+    {
+      fprintf (stderr, "could not parse length at start of compact string: %s\n",
+               _dbus_string_get_const_data (line));
+      return FALSE;
+    }
+
+  _dbus_string_skip_blank (line, next, &next);
+  
+  offset = next;
+  if (!_dbus_string_parse_int (line, offset, &val, &next))
+    {
+      fprintf (stderr, "could not parse start bit 'b' in compact string: %s\n",
+               _dbus_string_get_const_data (line));
+      return FALSE;
+    }
+  
+  if (!(val == 0 || val == 1))
+    {
+      fprintf (stderr, "the value 'b' must be 0 or 1, see sha-1/Readme.txt\n");
+      return FALSE;
+    }
+
+  _dbus_string_skip_blank (line, next, &next);
+  
+  current_b = val;
+  n_bits = 0;
+  
+  while (next < _dbus_string_get_length (line))
+    {
+      int total_bits;
+      
+      offset = next;
+
+      if (_dbus_string_get_byte (line, offset) == '^')
+        break;
+      
+      if (!_dbus_string_parse_int (line, offset, &val, &next))
+        {
+          fprintf (stderr, "could not parse bit count in compact string\n");
+          return FALSE;
+        }
+
+      /* We now append "val" copies of "current_b" bits to the string */
+      total_bits = n_bits + val;
+      while (n_bits < total_bits)
+        {
+          int byte_containing_next_bit = n_bits / 8;
+          int bit_containing_next_bit = 7 - (n_bits % 8);
+          unsigned char old_byte;
+          
+          if (byte_containing_next_bit >= _dbus_string_get_length (decoded))
+            {
+              if (!_dbus_string_set_length (decoded, byte_containing_next_bit + 1))
+                _dbus_assert_not_reached ("no memory to extend to next byte");
+            }
+
+          old_byte = _dbus_string_get_byte (decoded, byte_containing_next_bit);
+          old_byte |= current_b << bit_containing_next_bit;
+
+#if 0
+          printf ("Appending bit %d to byte %d at bit %d resulting in byte 0x%x\n",
+                  current_b, byte_containing_next_bit,
+                  bit_containing_next_bit, old_byte);
+#endif
+          
+          _dbus_string_set_byte (decoded, byte_containing_next_bit, old_byte);
+          
+          ++n_bits;
+        }
+
+      _dbus_string_skip_blank (line, next, &next);
+          
+      current_b = !current_b;
+    }
+
+  length_bytes = (n_bits / 8 + ((n_bits % 8) ? 1 : 0));
+  
+  if (_dbus_string_get_length (decoded) != length_bytes)
+    {
+      fprintf (stderr, "Expected length %d bytes %d bits for compact string, got %d bytes\n",
+               length_bytes, n_bits, _dbus_string_get_length (decoded));
+      return FALSE;
+    }
+  else
+    return TRUE;
+}
+
+static dbus_bool_t
+get_next_expected_result (DBusString *results,
+                          DBusString *result)
+{
+  DBusString line;
+  dbus_bool_t retval;
+
+  retval = FALSE;
+  
+  if (!_dbus_string_init (&line))
+    _dbus_assert_not_reached ("no memory");
+  
+ next_iteration:
+  while (_dbus_string_pop_line (results, &line))
+    {
+      _dbus_string_delete_leading_blanks (&line);
+
+      if (_dbus_string_get_length (&line) == 0)
+        goto next_iteration;
+      else if (_dbus_string_starts_with_c_str (&line, "#"))
+        goto next_iteration;
+      else if (_dbus_string_starts_with_c_str (&line, "H>"))
+        {
+          /* don't print */
+        }
+      else if (_dbus_string_starts_with_c_str (&line, "D>") ||
+               _dbus_string_starts_with_c_str (&line, "<D"))
+        goto next_iteration;
+      else
+        {
+          int i;
+          
+          if (!_dbus_string_move (&line, 0, result, 0))
+            _dbus_assert_not_reached ("no memory");
+
+          i = 0;
+          while (i < _dbus_string_get_length (result))
+            {
+              switch (_dbus_string_get_byte (result, i))
+                {
+                case 'A':
+                  _dbus_string_set_byte (result, i, 'a');
+                  break;
+                case 'B':
+                  _dbus_string_set_byte (result, i, 'b');
+                  break;
+                case 'C':
+                  _dbus_string_set_byte (result, i, 'c');
+                  break;
+                case 'D':
+                  _dbus_string_set_byte (result, i, 'd');
+                  break;
+                case 'E':
+                  _dbus_string_set_byte (result, i, 'e');
+                  break;
+                case 'F':
+                  _dbus_string_set_byte (result, i, 'f');
+                  break;
+                case '^':
+                case ' ':
+                  _dbus_string_delete (result, i, 1);
+                  --i; /* to offset ++i below */
+                  break;
+                }
+
+              ++i;
+            }
+          
+          break;
+        }
+    }
+  
+  retval = TRUE;
+  
+  /* out: */
+  _dbus_string_free (&line);
+  return retval;
+}
+
+static dbus_bool_t
+process_test_data (const char *test_data_dir)
+{
+  DBusString tests_file;
+  DBusString results_file;
+  DBusString tests;
+  DBusString results;
+  DBusString line;
+  DBusString tmp;
+  int line_no;
+  dbus_bool_t retval;
+  int success_count;
+  DBusError error = DBUS_ERROR_INIT;
+
+  retval = FALSE;
+  
+  if (!_dbus_string_init (&tests_file))
+    _dbus_assert_not_reached ("no memory");
+
+  if (!_dbus_string_init (&results_file))
+    _dbus_assert_not_reached ("no memory");
+
+  if (!_dbus_string_init (&tests))
+    _dbus_assert_not_reached ("no memory");
+
+  if (!_dbus_string_init (&results))
+    _dbus_assert_not_reached ("no memory");
+
+  if (!_dbus_string_init (&line))
+    _dbus_assert_not_reached ("no memory");
+  
+  if (!_dbus_string_append (&tests_file, test_data_dir))
+    _dbus_assert_not_reached ("no memory");
+
+  if (!_dbus_string_append (&results_file, test_data_dir))
+    _dbus_assert_not_reached ("no memory");
+
+  _dbus_string_init_const (&tmp, "sha-1/byte-messages.sha1");
+  if (!_dbus_concat_dir_and_file (&tests_file, &tmp))
+    _dbus_assert_not_reached ("no memory");
+
+  _dbus_string_init_const (&tmp, "sha-1/byte-hashes.sha1");
+  if (!_dbus_concat_dir_and_file (&results_file, &tmp))
+    _dbus_assert_not_reached ("no memory");
+
+  if (!_dbus_file_get_contents (&tests, &tests_file, &error))
+    {
+      fprintf (stderr, "could not load test data file %s: %s\n",
+               _dbus_string_get_const_data (&tests_file),
+               error.message);
+      dbus_error_free (&error);
+      goto out;
+    }
+
+  if (!_dbus_file_get_contents (&results, &results_file, &error))
+    {
+      fprintf (stderr, "could not load results data file %s: %s\n",
+               _dbus_string_get_const_data (&results_file), error.message);
+      dbus_error_free (&error);
+      goto out;
+    }
+
+  success_count = 0;
+  line_no = 0;
+ next_iteration:
+  while (_dbus_string_pop_line (&tests, &line))
+    {
+      line_no += 1;
+
+      _dbus_string_delete_leading_blanks (&line);
+
+      if (_dbus_string_get_length (&line) == 0)
+        goto next_iteration;
+      else if (_dbus_string_starts_with_c_str (&line, "#"))
+        goto next_iteration;
+      else if (_dbus_string_starts_with_c_str (&line, "H>"))
+        {
+          printf ("SHA-1: %s\n", _dbus_string_get_const_data (&line));
+
+          if (_dbus_string_find (&line, 0, "Type 3", NULL))
+            {
+              /* See sha-1/Readme.txt - the "Type 3" tests are
+               * random seeds, rather than data to be hashed.
+               * we'd have to do a little bit more implementation
+               * to use those tests.
+               */
+              
+              printf (" (ending tests due to Type 3 tests seen - this is normal)\n");
+              break;
+            }
+        }
+      else if (_dbus_string_starts_with_c_str (&line, "D>") ||
+               _dbus_string_starts_with_c_str (&line, "<D"))
+        goto next_iteration;
+      else
+        {
+          DBusString test;
+          DBusString result;
+          DBusString next_line;
+          DBusString expected;
+          dbus_bool_t success;
+
+          success = FALSE;
+          
+          if (!_dbus_string_init (&next_line))
+            _dbus_assert_not_reached ("no memory");
+
+          if (!_dbus_string_init (&expected))
+            _dbus_assert_not_reached ("no memory");
+          
+          if (!_dbus_string_init (&test))
+            _dbus_assert_not_reached ("no memory");
+
+          if (!_dbus_string_init (&result))
+            _dbus_assert_not_reached ("no memory");
+
+          /* the "compact strings" are "^"-terminated not
+           * newline-terminated so readahead to find the
+           * "^"
+           */
+          while (!_dbus_string_find (&line, 0, "^", NULL) &&
+                 _dbus_string_pop_line (&tests, &next_line))
+            {
+              if (!_dbus_string_append_byte (&line, ' ') ||
+                  !_dbus_string_move (&next_line, 0, &line,
+                                      _dbus_string_get_length (&line)))
+                _dbus_assert_not_reached ("no memory");
+            }
+          
+          if (!decode_compact_string (&line, &test))
+            {
+              fprintf (stderr, "Failed to decode line %d as a compact string\n",
+                       line_no);
+              goto failure;
+            }
+          
+          if (!_dbus_sha_compute (&test, &result))
+            _dbus_assert_not_reached ("no memory for SHA-1 result");
+
+          if (!get_next_expected_result (&results, &expected))
+            {
+              fprintf (stderr, "Failed to read an expected result\n");
+              goto failure;
+            }
+          
+          if (!_dbus_string_equal (&result, &expected))
+            {              
+              fprintf (stderr, " for line %d got hash %s expected %s\n",
+                       line_no,
+                       _dbus_string_get_const_data (&result),
+                       _dbus_string_get_const_data (&expected));
+              goto failure;
+            }
+          else
+            {
+              success_count += 1;
+            }
+
+          success = TRUE;
+
+        failure:
+          _dbus_string_free (&test);
+          _dbus_string_free (&result);
+          _dbus_string_free (&next_line);
+          _dbus_string_free (&expected);
+
+          if (!success)
+            goto out;
+        }
+    }
+
+  retval = TRUE;
+
+  printf ("Passed the %d SHA-1 tests in the test file\n",
+          success_count);
+  
+ out:
+  _dbus_string_free (&tests_file);
+  _dbus_string_free (&results_file);
+  _dbus_string_free (&tests);
+  _dbus_string_free (&results);
+  _dbus_string_free (&line);
+
+  return retval;
+}
+
+/**
+ * @ingroup DBusSHAInternals
+ * Unit test for SHA computation.
+ *
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_sha_test (const char *test_data_dir)
+{
+  unsigned char all_bytes[256];
+  int i;
+
+  if (test_data_dir != NULL)
+    {
+      if (!process_test_data (test_data_dir))
+        return FALSE;
+    }
+  else
+    printf ("No test data dir\n");
+  
+  i = 0;
+  while (i < 256)
+    {
+      all_bytes[i] = i;
+      ++i;
+    }
+
+  if (!check_sha_binary (all_bytes, 256,
+                         "4916d6bdb7f78e6803698cab32d1586ea457dfc8"))
+    return FALSE;
+
+#define CHECK(input,expected) if (!check_sha_str (input, expected)) return FALSE
+
+  CHECK ("", "da39a3ee5e6b4b0d3255bfef95601890afd80709");
+  CHECK ("a", "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8");
+  CHECK ("abc", "a9993e364706816aba3e25717850c26c9cd0d89d");
+  CHECK ("message digest", "c12252ceda8be8994d5fa0290a47231c1d16aae3");
+  CHECK ("abcdefghijklmnopqrstuvwxyz", "32d10c7b8cf96570ca04ce37f2a19d84240d3a89");
+  CHECK ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+         "761c457bf73b14d27e9e9265c46f4b4dda11f940");
+  CHECK ("12345678901234567890123456789012345678901234567890123456789012345678901234567890",
+         "50abf5706a150990a08b2c5ea40fa0e585554732");
+
+  return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-sha.h b/src/dbus/dbus-sha.h
new file mode 100644 (file)
index 0000000..5895bb1
--- /dev/null
@@ -0,0 +1,55 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sha.h SHA-1 implementation
+ *
+ * Copyright (C) 2003 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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_SHA_H
+#define DBUS_SHA_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-string.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusSHAContext DBusSHAContext;
+
+/**
+ * Struct storing state of the SHA algorithm
+ */
+struct DBusSHAContext
+{
+  dbus_uint32_t  digest[5];         /**< Message digest */
+  dbus_uint32_t  count_lo;          /**< 64-bit bit count */
+  dbus_uint32_t  count_hi;          /**< No clue */
+  dbus_uint32_t  data[16];          /**< SHA data buffer */
+};
+
+void        _dbus_sha_init    (DBusSHAContext   *context);
+void        _dbus_sha_update  (DBusSHAContext   *context,
+                               const DBusString *data);
+dbus_bool_t _dbus_sha_final   (DBusSHAContext   *context,
+                               DBusString       *results);
+dbus_bool_t _dbus_sha_compute (const DBusString *data,
+                               DBusString       *ascii_output);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_SHA_H */
diff --git a/src/dbus/dbus-shared.h b/src/dbus/dbus-shared.h
new file mode 100644 (file)
index 0000000..b59ed34
--- /dev/null
@@ -0,0 +1,131 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-shared.h  Stuff used by both dbus/dbus.h low-level and C/C++ binding APIs
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_SHARED_H
+#define DBUS_SHARED_H
+
+/* Don't include anything in here from anywhere else. It's
+ * intended for use by any random library.
+ */
+
+#ifdef  __cplusplus
+extern "C" {
+#if 0
+} /* avoids confusing emacs indentation */
+#endif
+#endif
+
+/* Normally docs are in .c files, but there isn't a .c file for this. */
+/**
+ * @defgroup DBusShared Shared constants 
+ * @ingroup  DBus
+ *
+ * @brief Shared header included by both libdbus and C/C++ bindings such as the GLib bindings.
+ *
+ * Usually a C/C++ binding such as the GLib or Qt binding won't want to include dbus.h in its
+ * public headers. However, a few constants and macros may be useful to include; those are
+ * found here and in dbus-protocol.h
+ *
+ * @{
+ */
+
+
+/**
+ * Well-known bus types. See dbus_bus_get().
+ */
+typedef enum
+{
+  DBUS_BUS_SESSION,    /**< The login session bus */
+  DBUS_BUS_SYSTEM,     /**< The systemwide bus */
+  DBUS_BUS_STARTER     /**< The bus that started us, if any */
+} DBusBusType;
+
+/**
+ * Results that a message handler can return.
+ */
+typedef enum
+{
+  DBUS_HANDLER_RESULT_HANDLED,         /**< Message has had its effect - no need to run more handlers. */ 
+  DBUS_HANDLER_RESULT_NOT_YET_HANDLED, /**< Message has not had any effect - see if other handlers want it. */
+  DBUS_HANDLER_RESULT_NEED_MEMORY      /**< Need more memory in order to return #DBUS_HANDLER_RESULT_HANDLED or #DBUS_HANDLER_RESULT_NOT_YET_HANDLED. Please try again later with more memory. */
+} DBusHandlerResult;
+
+/* Bus names */
+
+/** The bus name used to talk to the bus itself. */
+#define DBUS_SERVICE_DBUS      "org.freedesktop.DBus"
+
+/* Paths */
+/** The object path used to talk to the bus itself. */
+#define DBUS_PATH_DBUS  "/org/freedesktop/DBus"
+/** The object path used in local/in-process-generated messages. */
+#define DBUS_PATH_LOCAL "/org/freedesktop/DBus/Local"
+
+/* Interfaces, these #define don't do much other than
+ * catch typos at compile time
+ */
+/** The interface exported by the object with #DBUS_SERVICE_DBUS and #DBUS_PATH_DBUS */
+#define DBUS_INTERFACE_DBUS           "org.freedesktop.DBus"
+/** The interface supported by introspectable objects */
+#define DBUS_INTERFACE_INTROSPECTABLE "org.freedesktop.DBus.Introspectable"
+/** The interface supported by objects with properties */
+#define DBUS_INTERFACE_PROPERTIES     "org.freedesktop.DBus.Properties"
+/** The interface supported by most dbus peers */
+#define DBUS_INTERFACE_PEER           "org.freedesktop.DBus.Peer"
+
+/** This is a special interface whose methods can only be invoked
+ * by the local implementation (messages from remote apps aren't
+ * allowed to specify this interface).
+ */
+#define DBUS_INTERFACE_LOCAL "org.freedesktop.DBus.Local"
+
+/* Owner flags */
+#define DBUS_NAME_FLAG_ALLOW_REPLACEMENT 0x1 /**< Allow another service to become the primary owner if requested */
+#define DBUS_NAME_FLAG_REPLACE_EXISTING  0x2 /**< Request to replace the current primary owner */
+#define DBUS_NAME_FLAG_DO_NOT_QUEUE      0x4 /**< If we can not become the primary owner do not place us in the queue */
+
+/* Replies to request for a name */
+#define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER  1 /**< Service has become the primary owner of the requested name */
+#define DBUS_REQUEST_NAME_REPLY_IN_QUEUE       2 /**< Service could not become the primary owner and has been placed in the queue */
+#define DBUS_REQUEST_NAME_REPLY_EXISTS         3 /**< Service is already in the queue */
+#define DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER  4 /**< Service is already the primary owner */
+
+/* Replies to releasing a name */
+#define DBUS_RELEASE_NAME_REPLY_RELEASED        1 /**< Service was released from the given name */
+#define DBUS_RELEASE_NAME_REPLY_NON_EXISTENT    2 /**< The given name does not exist on the bus */
+#define DBUS_RELEASE_NAME_REPLY_NOT_OWNER       3 /**< Service is not an owner of the given name */
+
+/* Replies to service starts */
+#define DBUS_START_REPLY_SUCCESS         1 /**< Service was auto started */
+#define DBUS_START_REPLY_ALREADY_RUNNING 2 /**< Service was already running */
+
+/** @} */
+
+#ifdef __cplusplus
+#if 0
+{ /* avoids confusing emacs indentation */
+#endif
+}
+#endif
+
+#endif /* DBUS_SHARED_H */
diff --git a/src/dbus/dbus-shell.c b/src/dbus/dbus-shell.c
new file mode 100644 (file)
index 0000000..65038b1
--- /dev/null
@@ -0,0 +1,640 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-shell.c Shell command line utility functions.
+ *
+ * Copyright (C) 2002, 2003  Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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 <string.h>
+#include "dbus-internals.h"
+#include "dbus-list.h"
+#include "dbus-memory.h"
+#include "dbus-protocol.h"
+#include "dbus-shell.h"
+#include "dbus-string.h"
+
+/* Single quotes preserve the literal string exactly. escape
+ * sequences are not allowed; not even \' - if you want a '
+ * in the quoted text, you have to do something like 'foo'\''bar'
+ *
+ * Double quotes allow $ ` " \ and newline to be escaped with backslash.
+ * Otherwise double quotes preserve things literally.
+ */
+
+static dbus_bool_t
+unquote_string_inplace (char* str, char** end)
+{
+  char* dest;
+  char* s;
+  char quote_char;
+  
+  dest = s = str;
+
+  quote_char = *s;
+  
+  if (!(*s == '"' || *s == '\''))
+    {
+      *end = str;
+      return FALSE;
+    }
+
+  /* Skip the initial quote mark */
+  ++s;
+
+  if (quote_char == '"')
+    {
+      while (*s)
+        {
+          _dbus_assert(s > dest); /* loop invariant */
+      
+          switch (*s)
+            {
+            case '"':
+              /* End of the string, return now */
+              *dest = '\0';
+              ++s;
+              *end = s;
+              return TRUE;
+
+            case '\\':
+              /* Possible escaped quote or \ */
+              ++s;
+              switch (*s)
+                {
+                case '"':
+                case '\\':
+                case '`':
+                case '$':
+                case '\n':
+                  *dest = *s;
+                  ++s;
+                  ++dest;
+                  break;
+
+                default:
+                  /* not an escaped char */
+                  *dest = '\\';
+                  ++dest;
+                  /* ++s already done. */
+                  break;
+                }
+              break;
+
+            default:
+              *dest = *s;
+              ++dest;
+              ++s;
+              break;
+            }
+
+          _dbus_assert(s > dest); /* loop invariant */
+        }
+    }
+  else
+    {
+      while (*s)
+        {
+          _dbus_assert(s > dest); /* loop invariant */
+          
+          if (*s == '\'')
+            {
+              /* End of the string, return now */
+              *dest = '\0';
+              ++s;
+              *end = s;
+              return TRUE;
+            }
+          else
+            {
+              *dest = *s;
+              ++dest;
+              ++s;
+            }
+
+          _dbus_assert(s > dest); /* loop invariant */
+        }
+    }
+  
+  /* If we reach here this means the close quote was never encountered */
+
+  *dest = '\0';
+  
+  *end = s;
+  return FALSE;
+}
+
+/** 
+ * Unquotes a string as the shell (/bin/sh) would. Only handles
+ * quotes; if a string contains file globs, arithmetic operators,
+ * variables, backticks, redirections, or other special-to-the-shell
+ * features, the result will be different from the result a real shell
+ * would produce (the variables, backticks, etc. will be passed
+ * through literally instead of being expanded). This function is
+ * guaranteed to succeed if applied to the result of
+ * _dbus_shell_quote(). If it fails, it returns %NULL.
+ * The @quoted_string need not actually contain quoted or
+ * escaped text; _dbus_shell_unquote() simply goes through the string and
+ * unquotes/unescapes anything that the shell would. Both single and
+ * double quotes are handled, as are escapes including escaped
+ * newlines. The return value must be freed with dbus_free().
+ * 
+ * Shell quoting rules are a bit strange. Single quotes preserve the
+ * literal string exactly. escape sequences are not allowed; not even
+ * \' - if you want a ' in the quoted text, you have to do something
+ * like 'foo'\''bar'.  Double quotes allow $, `, ", \, and newline to
+ * be escaped with backslash. Otherwise double quotes preserve things
+ * literally.
+ *
+ * @quoted_string: shell-quoted string
+ **/
+char*
+_dbus_shell_unquote (const char *quoted_string)
+{
+  char *unquoted;
+  char *end;
+  char *start;
+  char *ret;
+  DBusString retval;
+
+  unquoted = _dbus_strdup (quoted_string);
+  if (unquoted == NULL)
+    return NULL;
+
+  start = unquoted;
+  end = unquoted;
+  if (!_dbus_string_init (&retval))
+    {
+      dbus_free (unquoted);
+      return NULL;
+    }
+
+  /* The loop allows cases such as
+   * "foo"blah blah'bar'woo foo"baz"la la la\'\''foo'
+   */
+  while (*start)
+    {
+      /* Append all non-quoted chars, honoring backslash escape
+       */
+      
+      while (*start && !(*start == '"' || *start == '\''))
+        {
+          if (*start == '\\')
+            {
+              /* all characters can get escaped by backslash,
+               * except newline, which is removed if it follows
+               * a backslash outside of quotes
+               */
+              
+              ++start;
+              if (*start)
+                {
+                  if (*start != '\n')
+                   {
+                     if (!_dbus_string_append_byte (&retval, *start))
+                       goto error;
+                   }
+                  ++start;
+                }
+            }
+          else
+            {
+              if (!_dbus_string_append_byte (&retval, *start))
+               goto error;
+              ++start;
+            }
+        }
+
+      if (*start)
+        {
+          if (!unquote_string_inplace (start, &end))
+           goto error;
+          else
+            {
+              if (!_dbus_string_append (&retval, start))
+               goto error;
+              start = end;
+            }
+        }
+    }
+
+  ret = _dbus_strdup (_dbus_string_get_data (&retval));
+  if (!ret)
+    goto error;
+
+  dbus_free (unquoted);
+  _dbus_string_free (&retval);
+  
+  return ret;
+  
+ error:
+  dbus_free (unquoted);
+  _dbus_string_free (&retval);
+  return NULL;
+}
+
+/* _dbus_shell_parse_argv() does a semi-arbitrary weird subset of the way
+ * the shell parses a command line. We don't do variable expansion,
+ * don't understand that operators are tokens, don't do tilde expansion,
+ * don't do command substitution, no arithmetic expansion, IFS gets ignored,
+ * don't do filename globs, don't remove redirection stuff, etc.
+ *
+ * READ THE UNIX98 SPEC on "Shell Command Language" before changing
+ * the behavior of this code.
+ *
+ * Steps to parsing the argv string:
+ *
+ *  - tokenize the string (but since we ignore operators,
+ *    our tokenization may diverge from what the shell would do)
+ *    note that tokenization ignores the internals of a quoted
+ *    word and it always splits on spaces, not on IFS even
+ *    if we used IFS. We also ignore "end of input indicator"
+ *    (I guess this is control-D?)
+ *
+ *    Tokenization steps, from UNIX98 with operator stuff removed,
+ *    are:
+ * 
+ *    1) "If the current character is backslash, single-quote or
+ *        double-quote (\, ' or ") and it is not quoted, it will affect
+ *        quoting for subsequent characters up to the end of the quoted
+ *        text. The rules for quoting are as described in Quoting
+ *        . During token recognition no substitutions will be actually
+ *        performed, and the result token will contain exactly the
+ *        characters that appear in the input (except for newline
+ *        character joining), unmodified, including any embedded or
+ *        enclosing quotes or substitution operators, between the quote
+ *        mark and the end of the quoted text. The token will not be
+ *        delimited by the end of the quoted field."
+ *
+ *    2) "If the current character is an unquoted newline character,
+ *        the current token will be delimited."
+ *
+ *    3) "If the current character is an unquoted blank character, any
+ *        token containing the previous character is delimited and the
+ *        current character will be discarded."
+ *
+ *    4) "If the previous character was part of a word, the current
+ *        character will be appended to that word."
+ *
+ *    5) "If the current character is a "#", it and all subsequent
+ *        characters up to, but excluding, the next newline character
+ *        will be discarded as a comment. The newline character that
+ *        ends the line is not considered part of the comment. The
+ *        "#" starts a comment only when it is at the beginning of a
+ *        token. Since the search for the end-of-comment does not
+ *        consider an escaped newline character specially, a comment
+ *        cannot be continued to the next line."
+ *
+ *    6) "The current character will be used as the start of a new word."
+ *
+ *
+ *  - for each token (word), perform portions of word expansion, namely
+ *    field splitting (using default whitespace IFS) and quote
+ *    removal.  Field splitting may increase the number of words.
+ *    Quote removal does not increase the number of words.
+ *
+ *   "If the complete expansion appropriate for a word results in an
+ *   empty field, that empty field will be deleted from the list of
+ *   fields that form the completely expanded command, unless the
+ *   original word contained single-quote or double-quote characters."
+ *    - UNIX98 spec
+ *
+ *
+ */
+
+static dbus_bool_t
+delimit_token (DBusString *token,
+               DBusList **retval,
+              DBusError *error)
+{
+  char *str;
+
+  str = _dbus_strdup (_dbus_string_get_data (token));
+  if (!str)
+    {
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+
+  if (!_dbus_list_append (retval, str))
+    {
+      dbus_free (str);
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static DBusList*
+tokenize_command_line (const char *command_line, DBusError *error)
+{
+  char current_quote;
+  const char *p;
+  DBusString current_token;
+  DBusList *retval = NULL;
+  dbus_bool_t quoted;;
+
+  current_quote = '\0';
+  quoted = FALSE;
+  p = command_line;
+
+  if (!_dbus_string_init (&current_token))
+    {
+      _DBUS_SET_OOM (error);
+      return NULL;
+    }
+
+  while (*p)
+    {
+      if (current_quote == '\\')
+        {
+          if (*p == '\n')
+            {
+              /* we append nothing; backslash-newline become nothing */
+            }
+          else
+            {
+             if (!_dbus_string_append_byte (&current_token, '\\') || 
+                 !_dbus_string_append_byte (&current_token, *p))
+               {
+                 _DBUS_SET_OOM (error);
+                 goto error;
+               }
+            }
+
+          current_quote = '\0';
+        }
+      else if (current_quote == '#')
+        {
+          /* Discard up to and including next newline */
+          while (*p && *p != '\n')
+            ++p;
+
+          current_quote = '\0';
+          
+          if (*p == '\0')
+            break;
+        }
+      else if (current_quote)
+        {
+          if (*p == current_quote &&
+              /* check that it isn't an escaped double quote */
+              !(current_quote == '"' && quoted))
+            {
+              /* close the quote */
+              current_quote = '\0';
+            }
+
+          /* Everything inside quotes, and the close quote,
+           * gets appended literally.
+           */
+
+          if (!_dbus_string_append_byte (&current_token, *p))
+           {
+             _DBUS_SET_OOM (error);
+             goto error;
+           }
+        }
+      else
+        {
+          switch (*p)
+            {
+            case '\n':
+              if (!delimit_token (&current_token, &retval, error))
+               goto error;
+
+               _dbus_string_free (&current_token);
+
+               if (!_dbus_string_init (&current_token))
+                 {
+                   _DBUS_SET_OOM (error);
+                   goto init_error;
+                 }
+
+              break;
+
+            case ' ':
+            case '\t':
+              /* If the current token contains the previous char, delimit
+               * the current token. A nonzero length
+               * token should always contain the previous char.
+               */
+              if (_dbus_string_get_length (&current_token) > 0)
+                {
+                  if (!delimit_token (&current_token, &retval, error))
+                   goto error;
+
+                 _dbus_string_free (&current_token);
+
+                 if (!_dbus_string_init (&current_token))
+                   {
+                     _DBUS_SET_OOM (error);
+                     goto init_error;
+                   }
+
+                }
+              
+              /* discard all unquoted blanks (don't add them to a token) */
+              break;
+
+
+              /* single/double quotes are appended to the token,
+               * escapes are maybe appended next time through the loop,
+               * comment chars are never appended.
+               */
+              
+            case '\'':
+            case '"':
+              if (!_dbus_string_append_byte (&current_token, *p))
+               {
+                 _DBUS_SET_OOM (error);
+                 goto error;
+               }
+
+              /* FALL THRU */
+              
+            case '#':
+            case '\\':
+              current_quote = *p;
+              break;
+
+            default:
+              /* Combines rules 4) and 6) - if we have a token, append to it,
+               * otherwise create a new token.
+               */
+              if (!_dbus_string_append_byte (&current_token, *p))
+               {
+                 _DBUS_SET_OOM (error);
+                 goto error;
+               }
+              break;
+            }
+        }
+
+      /* We need to count consecutive backslashes mod 2, 
+       * to detect escaped doublequotes.
+       */
+      if (*p != '\\')
+       quoted = FALSE;
+      else
+       quoted = !quoted;
+
+      ++p;
+    }
+
+  if (!delimit_token (&current_token, &retval, error))
+    goto error;
+
+  if (current_quote)
+    {
+      dbus_set_error_const (error, DBUS_ERROR_INVALID_ARGS, "Unclosed quotes in command line");
+      goto error;
+    }
+
+  if (retval == NULL)
+    {
+      dbus_set_error_const (error, DBUS_ERROR_INVALID_ARGS, "No tokens found in command line");
+      goto error;
+    }
+  _dbus_string_free (&current_token);
+  return retval;
+
+ error:
+  _dbus_string_free (&current_token);
+
+ init_error:
+  if (retval)
+    {
+      _dbus_list_foreach (&retval, (DBusForeachFunction) dbus_free, NULL);
+      _dbus_list_clear (&retval);
+    }
+
+  return NULL;
+}
+
+/**
+ * _dbus_shell_parse_argv:
+ * 
+ * Parses a command line into an argument vector, in much the same way
+ * the shell would, but without many of the expansions the shell would
+ * perform (variable expansion, globs, operators, filename expansion,
+ * etc. are not supported). The results are defined to be the same as
+ * those you would get from a UNIX98 /bin/sh, as long as the input
+ * contains none of the unsupported shell expansions. If the input
+ * does contain such expansions, they are passed through
+ * literally. Free the returned vector with dbus_free_string_array().
+ * 
+ * @command_line: command line to parse
+ * @argcp: return location for number of args
+ * @argvp: return location for array of args
+ * @error: error information
+ **/
+dbus_bool_t
+_dbus_shell_parse_argv (const char *command_line,
+                       int        *argcp,
+                       char     ***argvp,
+                       DBusError  *error)
+{
+  /* Code based on poptParseArgvString() from libpopt */
+  int argc = 0;
+  char **argv = NULL;
+  DBusList *tokens = NULL;
+  int i;
+  DBusList *tmp_list;
+  
+  if (!command_line)
+    {
+      _dbus_verbose ("Command line is NULL\n");
+      return FALSE;
+    }
+
+  tokens = tokenize_command_line (command_line, error);
+  if (tokens == NULL)
+    {
+      _dbus_verbose ("No tokens for command line '%s'\n", command_line);
+      return FALSE;
+    }
+
+  /* Because we can't have introduced any new blank space into the
+   * tokens (we didn't do any new expansions), we don't need to
+   * perform field splitting. If we were going to honor IFS or do any
+   * expansions, we would have to do field splitting on each word
+   * here. Also, if we were going to do any expansion we would need to
+   * remove any zero-length words that didn't contain quotes
+   * originally; but since there's no expansion we know all words have
+   * nonzero length, unless they contain quotes.
+   * 
+   * So, we simply remove quotes, and don't do any field splitting or
+   * empty word removal, since we know there was no way to introduce
+   * such things.
+   */
+
+  argc = _dbus_list_get_length (&tokens);
+  argv = dbus_new (char *, argc + 1);
+  if (!argv)
+    {
+      _DBUS_SET_OOM (error);
+      goto error;
+    }
+
+  i = 0;
+  tmp_list = tokens;
+  while (tmp_list)
+    {
+      argv[i] = _dbus_shell_unquote (tmp_list->data);
+
+      if (!argv[i])
+        {
+          int j;
+         for (j = 0; j < i; j++)
+           dbus_free(argv[j]);
+
+          dbus_free (argv);
+         _DBUS_SET_OOM (error);
+         goto error;
+        }
+
+      tmp_list = _dbus_list_get_next_link (&tokens, tmp_list);
+      ++i;
+    }
+  argv[argc] = NULL;
+  
+  _dbus_list_foreach (&tokens, (DBusForeachFunction) dbus_free, NULL);
+  _dbus_list_clear (&tokens);
+  
+  if (argcp)
+    *argcp = argc;
+
+  if (argvp)
+    *argvp = argv;
+  else
+    dbus_free_string_array (argv);
+
+  return TRUE;
+
+ error:
+  _dbus_list_foreach (&tokens, (DBusForeachFunction) dbus_free, NULL);
+  _dbus_list_clear (&tokens);
+
+  return FALSE;
+
+}
diff --git a/src/dbus/dbus-shell.h b/src/dbus/dbus-shell.h
new file mode 100644 (file)
index 0000000..5316d51
--- /dev/null
@@ -0,0 +1,41 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-shell.h Shell command line utility functions.
+ *
+ * Copyright (C) 2002, 2003  Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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_SHELL_H
+#define DBUS_SHELL_H
+
+DBUS_BEGIN_DECLS
+
+char*       _dbus_shell_unquote    (const char *quoted_string);
+dbus_bool_t _dbus_shell_parse_argv (const char *command_line,
+                                    int        *argcp,
+                                    char       ***argvp,
+                                   DBusError  *error);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_SHELL_H */
+
+
diff --git a/src/dbus/dbus-signature.c b/src/dbus/dbus-signature.c
new file mode 100644 (file)
index 0000000..c7f8d0e
--- /dev/null
@@ -0,0 +1,542 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-signature.c  Routines for reading recursive type signatures
+ *
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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-signature.h"
+#include "dbus-marshal-recursive.h"
+#include "dbus-marshal-basic.h"
+#include "dbus-internals.h"
+#include "dbus-test.h"
+
+/**
+ * Implementation details of #DBusSignatureIter, all fields are private
+ */
+typedef struct
+{ 
+  const char *pos;           /**< current position in the signature string */
+  unsigned int finished : 1; /**< true if we are at the end iter */
+  unsigned int in_array : 1; /**< true if we are a subiterator pointing to an array's element type */
+} DBusSignatureRealIter;
+
+/** macro that checks whether a typecode is a container type */
+#define TYPE_IS_CONTAINER(typecode)             \
+    ((typecode) == DBUS_TYPE_STRUCT ||          \
+     (typecode) == DBUS_TYPE_DICT_ENTRY ||      \
+     (typecode) == DBUS_TYPE_VARIANT ||         \
+     (typecode) == DBUS_TYPE_ARRAY)
+
+
+/**
+ * @defgroup DBusSignature Type signature parsing
+ * @ingroup  DBus
+ * @brief Parsing D-Bus type signatures
+ * @{
+ */
+
+/**
+ * Initializes a #DBusSignatureIter for reading a type signature.  This
+ * function is not safe to use on invalid signatures; be sure to
+ * validate potentially invalid signatures with dbus_signature_validate
+ * before using this function.
+ *
+ * @param iter pointer to an iterator to initialize
+ * @param signature the type signature
+ */
+void
+dbus_signature_iter_init (DBusSignatureIter *iter,
+                         const char        *signature)
+{
+  DBusSignatureRealIter *real_iter = (DBusSignatureRealIter *) iter;
+
+  real_iter->pos = signature;
+  real_iter->finished = FALSE;
+  real_iter->in_array = FALSE;
+}
+
+/**
+ * Returns the current type pointed to by the iterator.
+ * If the iterator is pointing at a type code such as 's', then
+ * it will be returned directly.
+ *
+ * However, when the parser encounters a container type start
+ * character such as '(' for a structure, the corresponding type for
+ * the container will be returned, e.g.  DBUS_TYPE_STRUCT, not '('.
+ * In this case, you should initialize a sub-iterator with
+ * dbus_signature_iter_recurse() to parse the container type.
+ *
+ * @param iter pointer to an iterator 
+ * @returns current type (e.g. #DBUS_TYPE_STRING, #DBUS_TYPE_ARRAY)
+ */
+int
+dbus_signature_iter_get_current_type (const DBusSignatureIter *iter)
+{
+  DBusSignatureRealIter *real_iter = (DBusSignatureRealIter *) iter;
+
+  return _dbus_first_type_in_signature_c_str (real_iter->pos, 0);
+}
+
+/**
+ * Returns the signature of the single complete type starting at the
+ * given iterator.
+ *
+ * For example, if the iterator is pointing at the start of "(ii)ii"
+ * (which is "a struct of two ints, followed by an int, followed by an
+ * int"), then "(ii)" would be returned. If the iterator is pointing at
+ * one of the "i" then just that "i" would be returned.
+ *
+ * @param iter pointer to an iterator 
+ * @returns current signature; or #NULL if no memory.  Should be freed with dbus_free()
+ */
+char *
+dbus_signature_iter_get_signature (const DBusSignatureIter *iter)
+{
+  DBusSignatureRealIter *real_iter = (DBusSignatureRealIter *) iter;
+  DBusString str;
+  char *ret;
+  int pos;
+  
+  if (!_dbus_string_init (&str))
+    return NULL;
+
+  pos = 0;
+  _dbus_type_signature_next (real_iter->pos, &pos);
+
+  if (!_dbus_string_append_len (&str, real_iter->pos, pos))
+    return NULL;
+  if (!_dbus_string_steal_data (&str, &ret))
+    ret = NULL;
+  _dbus_string_free (&str);
+
+  return ret; 
+}
+
+/**
+ * Convenience function for returning the element type of an array;
+ * This function allows you to avoid initializing a sub-iterator and
+ * getting its current type.
+ *
+ * Undefined behavior results if you invoke this function when the
+ * current type of the iterator is not #DBUS_TYPE_ARRAY.
+ *
+ * @param iter pointer to an iterator 
+ * @returns current array element type
+ */
+int
+dbus_signature_iter_get_element_type (const DBusSignatureIter *iter)
+{
+  DBusSignatureRealIter *real_iter = (DBusSignatureRealIter *) iter;
+
+  _dbus_return_val_if_fail (dbus_signature_iter_get_current_type (iter) == DBUS_TYPE_ARRAY, DBUS_TYPE_INVALID);
+
+  return _dbus_first_type_in_signature_c_str (real_iter->pos, 1);
+}
+
+/**
+ * Skip to the next value on this "level". e.g. the next field in a
+ * struct, the next value in an array. Returns #FALSE at the end of the
+ * current container.
+ *
+ * @param iter the iterator
+ * @returns FALSE if nothing more to read at or below this level
+ */
+dbus_bool_t
+dbus_signature_iter_next (DBusSignatureIter *iter)
+{
+  DBusSignatureRealIter *real_iter = (DBusSignatureRealIter *) iter;
+
+  if (real_iter->finished)
+    return FALSE;
+  else
+    {
+      int pos;
+
+      if (real_iter->in_array)
+       {
+         real_iter->finished = TRUE;
+         return FALSE;
+       }
+
+      pos = 0;
+      _dbus_type_signature_next (real_iter->pos, &pos);
+      real_iter->pos += pos;
+
+      if (*real_iter->pos == DBUS_STRUCT_END_CHAR
+         || *real_iter->pos == DBUS_DICT_ENTRY_END_CHAR)
+       {
+         real_iter->finished = TRUE;
+         return FALSE;
+       }
+
+      return *real_iter->pos != DBUS_TYPE_INVALID;
+    }
+}
+
+/**
+ * Initialize a new iterator pointing to the first type in the current
+ * container.
+ * 
+ * The results are undefined when calling this if the current type is
+ * a non-container (i.e. if dbus_type_is_container() returns #FALSE
+ * for the result of dbus_signature_iter_get_current_type()).
+ *
+ * @param iter the current interator
+ * @param subiter an iterator to initialize pointing to the first child
+ */
+void
+dbus_signature_iter_recurse (const DBusSignatureIter *iter,
+                            DBusSignatureIter       *subiter)
+{
+  DBusSignatureRealIter *real_iter = (DBusSignatureRealIter *) iter;
+  DBusSignatureRealIter *real_sub_iter = (DBusSignatureRealIter *) subiter;
+
+  _dbus_return_if_fail (dbus_type_is_container (dbus_signature_iter_get_current_type (iter)));
+
+  *real_sub_iter = *real_iter;
+  real_sub_iter->in_array = FALSE;
+  real_sub_iter->pos++;
+
+  if (dbus_signature_iter_get_current_type (iter) == DBUS_TYPE_ARRAY)
+    real_sub_iter->in_array = TRUE;
+}
+
+/**
+ * Check a type signature for validity. Remember that #NULL can always
+ * be passed instead of a DBusError*, if you don't care about having
+ * an error name and message.
+ *
+ * @param signature a potentially invalid type signature
+ * @param error error return
+ * @returns #TRUE if signature is valid or #FALSE if an error is set
+ */
+dbus_bool_t
+dbus_signature_validate (const char       *signature,
+                        DBusError        *error)
+                        
+{
+  DBusString str;
+  DBusValidity reason;
+
+  _dbus_string_init_const (&str, signature);
+  reason = _dbus_validate_signature_with_reason (&str, 0, _dbus_string_get_length (&str));
+
+  if (reason == DBUS_VALID)
+    return TRUE;
+  else
+    {
+      dbus_set_error (error, DBUS_ERROR_INVALID_SIGNATURE, _dbus_validity_to_error_message (reason));
+      return FALSE;
+    }
+}
+
+/**
+ * Check that a type signature is both valid and contains exactly one
+ * complete type. "One complete type" means a single basic type,
+ * array, struct, or dictionary, though the struct or array may be
+ * arbitrarily recursive and complex. More than one complete type
+ * would mean for example "ii" or two integers in sequence.
+ *
+ * @param signature a potentially invalid type signature
+ * @param error error return
+ * @returns #TRUE if signature is valid and has exactly one complete type
+ */
+dbus_bool_t
+dbus_signature_validate_single (const char       *signature,
+                               DBusError        *error)
+{
+  DBusSignatureIter iter;
+
+  if (!dbus_signature_validate (signature, error))
+    return FALSE;
+
+  dbus_signature_iter_init (&iter, signature);
+  if (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_INVALID)
+    goto lose;
+  if (!dbus_signature_iter_next (&iter))
+    return TRUE;
+ lose:
+  dbus_set_error (error, DBUS_ERROR_INVALID_SIGNATURE, "Exactly one complete type required in signature");
+  return FALSE;
+}
+
+/**
+ * A "container type" can contain basic types, or nested
+ * container types. #DBUS_TYPE_INVALID is not a container type.
+ *
+ * This function will crash if passed a typecode that isn't
+ * in dbus-protocol.h
+ *
+ * @returns #TRUE if type is a container
+ */
+dbus_bool_t
+dbus_type_is_container (int typecode)
+{
+  /* only reasonable (non-line-noise) typecodes are allowed */
+  _dbus_return_val_if_fail (_dbus_type_is_valid (typecode) || typecode == DBUS_TYPE_INVALID,
+                           FALSE);
+  return TYPE_IS_CONTAINER (typecode);
+}
+
+/**
+ * A "basic type" is a somewhat arbitrary concept, but the intent is
+ * to include those types that are fully-specified by a single
+ * typecode, with no additional type information or nested values. So
+ * all numbers and strings are basic types and structs, arrays, and
+ * variants are not basic types.  #DBUS_TYPE_INVALID is not a basic
+ * type.
+ *
+ * This function will crash if passed a typecode that isn't
+ * in dbus-protocol.h 
+ *
+ * @returns #TRUE if type is basic
+ */
+dbus_bool_t
+dbus_type_is_basic (int typecode)
+{
+  /* only reasonable (non-line-noise) typecodes are allowed */
+  _dbus_return_val_if_fail (_dbus_type_is_valid (typecode) || typecode == DBUS_TYPE_INVALID,
+                           FALSE);
+
+  /* everything that isn't invalid or a container */
+  return !(typecode == DBUS_TYPE_INVALID || TYPE_IS_CONTAINER (typecode));
+}
+
+/**
+ * Tells you whether values of this type can change length if you set
+ * them to some other value. For this purpose, you assume that the
+ * first byte of the old and new value would be in the same location,
+ * so alignment padding is not a factor.
+ *
+ * This function is useful to determine whether
+ * dbus_message_iter_get_fixed_array() may be used.
+ *
+ * Some structs are fixed-size (if they contain only fixed-size types)
+ * but struct is not considered a fixed type for purposes of this
+ * function.
+ *
+ * This function will crash if passed a typecode that isn't
+ * in dbus-protocol.h
+ * 
+ * @returns #FALSE if the type can occupy different lengths
+ */
+dbus_bool_t
+dbus_type_is_fixed (int typecode)
+{
+  /* only reasonable (non-line-noise) typecodes are allowed */
+  _dbus_return_val_if_fail (_dbus_type_is_valid (typecode) || typecode == DBUS_TYPE_INVALID,
+                           FALSE);
+  
+  switch (typecode)
+    {
+    case DBUS_TYPE_BYTE:
+    case DBUS_TYPE_BOOLEAN:
+    case DBUS_TYPE_INT16:
+    case DBUS_TYPE_UINT16:
+    case DBUS_TYPE_INT32:
+    case DBUS_TYPE_UINT32:
+    case DBUS_TYPE_INT64:
+    case DBUS_TYPE_UINT64:
+    case DBUS_TYPE_DOUBLE:
+      return TRUE;
+    default:
+      return FALSE;
+    }
+}
+
+/** @} */ /* end of DBusSignature group */
+
+#ifdef DBUS_BUILD_TESTS
+
+/**
+ * @ingroup DBusSignatureInternals
+ * Unit test for DBusSignature.
+ *
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_signature_test (void)
+{
+  DBusSignatureIter iter;
+  DBusSignatureIter subiter;
+  DBusSignatureIter subsubiter;
+  DBusSignatureIter subsubsubiter;
+  const char *sig;
+  dbus_bool_t boolres;
+
+  _dbus_assert (sizeof (DBusSignatureIter) >= sizeof (DBusSignatureRealIter));
+
+  sig = "";
+  _dbus_assert (dbus_signature_validate (sig, NULL));
+  _dbus_assert (!dbus_signature_validate_single (sig, NULL));
+  dbus_signature_iter_init (&iter, sig);
+  _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_INVALID);
+
+  sig = DBUS_TYPE_STRING_AS_STRING;
+  _dbus_assert (dbus_signature_validate (sig, NULL));
+  _dbus_assert (dbus_signature_validate_single (sig, NULL));
+  dbus_signature_iter_init (&iter, sig);
+  _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_STRING);
+
+  sig = DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_BYTE_AS_STRING;
+  _dbus_assert (dbus_signature_validate (sig, NULL));
+  dbus_signature_iter_init (&iter, sig);
+  _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_STRING);
+  boolres = dbus_signature_iter_next (&iter);
+  _dbus_assert (boolres);
+  _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_BYTE);
+
+  sig = DBUS_TYPE_UINT16_AS_STRING
+    DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+    DBUS_TYPE_STRING_AS_STRING
+    DBUS_TYPE_UINT32_AS_STRING
+    DBUS_TYPE_VARIANT_AS_STRING
+    DBUS_TYPE_DOUBLE_AS_STRING
+    DBUS_STRUCT_END_CHAR_AS_STRING;
+  _dbus_assert (dbus_signature_validate (sig, NULL));
+  dbus_signature_iter_init (&iter, sig);
+  _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_UINT16);
+  boolres = dbus_signature_iter_next (&iter);
+  _dbus_assert (boolres);
+  _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_STRUCT);
+  dbus_signature_iter_recurse (&iter, &subiter);
+  _dbus_assert (dbus_signature_iter_get_current_type (&subiter) == DBUS_TYPE_STRING);
+  boolres = dbus_signature_iter_next (&subiter);
+  _dbus_assert (boolres);
+  _dbus_assert (dbus_signature_iter_get_current_type (&subiter) == DBUS_TYPE_UINT32);
+  boolres = dbus_signature_iter_next (&subiter);
+  _dbus_assert (boolres);
+  _dbus_assert (dbus_signature_iter_get_current_type (&subiter) == DBUS_TYPE_VARIANT);
+  boolres = dbus_signature_iter_next (&subiter);
+  _dbus_assert (boolres);
+  _dbus_assert (dbus_signature_iter_get_current_type (&subiter) == DBUS_TYPE_DOUBLE);
+
+  sig = DBUS_TYPE_UINT16_AS_STRING
+    DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+    DBUS_TYPE_UINT32_AS_STRING
+    DBUS_TYPE_BYTE_AS_STRING
+    DBUS_TYPE_ARRAY_AS_STRING
+    DBUS_TYPE_ARRAY_AS_STRING
+    DBUS_TYPE_DOUBLE_AS_STRING
+    DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+    DBUS_TYPE_BYTE_AS_STRING
+    DBUS_STRUCT_END_CHAR_AS_STRING
+    DBUS_STRUCT_END_CHAR_AS_STRING;
+  _dbus_assert (dbus_signature_validate (sig, NULL));
+  dbus_signature_iter_init (&iter, sig);
+  _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_UINT16);
+  boolres = dbus_signature_iter_next (&iter);
+  _dbus_assert (boolres);
+  _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_STRUCT);
+  dbus_signature_iter_recurse (&iter, &subiter);
+  _dbus_assert (dbus_signature_iter_get_current_type (&subiter) == DBUS_TYPE_UINT32);
+  boolres = dbus_signature_iter_next (&subiter);
+  _dbus_assert (boolres);
+  _dbus_assert (dbus_signature_iter_get_current_type (&subiter) == DBUS_TYPE_BYTE);
+  boolres = dbus_signature_iter_next (&subiter);
+  _dbus_assert (boolres);
+  _dbus_assert (dbus_signature_iter_get_current_type (&subiter) == DBUS_TYPE_ARRAY);
+  _dbus_assert (dbus_signature_iter_get_element_type (&subiter) == DBUS_TYPE_ARRAY);
+
+  dbus_signature_iter_recurse (&subiter, &subsubiter);
+  _dbus_assert (dbus_signature_iter_get_current_type (&subsubiter) == DBUS_TYPE_ARRAY);
+  _dbus_assert (dbus_signature_iter_get_element_type (&subsubiter) == DBUS_TYPE_DOUBLE);
+
+  dbus_signature_iter_recurse (&subsubiter, &subsubsubiter);
+  _dbus_assert (dbus_signature_iter_get_current_type (&subsubsubiter) == DBUS_TYPE_DOUBLE);
+  boolres = dbus_signature_iter_next (&subiter);
+  _dbus_assert (boolres);
+  _dbus_assert (dbus_signature_iter_get_current_type (&subiter) == DBUS_TYPE_STRUCT);
+  dbus_signature_iter_recurse (&subiter, &subsubiter);
+  _dbus_assert (dbus_signature_iter_get_current_type (&subsubiter) == DBUS_TYPE_BYTE);
+
+  sig = DBUS_TYPE_ARRAY_AS_STRING
+    DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+    DBUS_TYPE_INT16_AS_STRING
+    DBUS_TYPE_STRING_AS_STRING
+    DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+    DBUS_TYPE_VARIANT_AS_STRING;
+  _dbus_assert (dbus_signature_validate (sig, NULL));
+  _dbus_assert (!dbus_signature_validate_single (sig, NULL));
+  dbus_signature_iter_init (&iter, sig);
+  _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_ARRAY);
+  _dbus_assert (dbus_signature_iter_get_element_type (&iter) == DBUS_TYPE_DICT_ENTRY);
+
+  dbus_signature_iter_recurse (&iter, &subiter);
+  dbus_signature_iter_recurse (&subiter, &subsubiter);
+  _dbus_assert (dbus_signature_iter_get_current_type (&subsubiter) == DBUS_TYPE_INT16);
+  boolres = dbus_signature_iter_next (&subsubiter);
+  _dbus_assert (boolres);
+  _dbus_assert (dbus_signature_iter_get_current_type (&subsubiter) == DBUS_TYPE_STRING);
+  boolres = dbus_signature_iter_next (&subsubiter);
+  _dbus_assert (!boolres);
+
+  boolres = dbus_signature_iter_next (&iter);
+  _dbus_assert (boolres);
+  _dbus_assert (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_VARIANT);
+  boolres = dbus_signature_iter_next (&iter);
+  _dbus_assert (!boolres);
+
+  sig = DBUS_TYPE_DICT_ENTRY_AS_STRING;
+  _dbus_assert (!dbus_signature_validate (sig, NULL));
+
+  sig = DBUS_TYPE_ARRAY_AS_STRING;
+  _dbus_assert (!dbus_signature_validate (sig, NULL));
+
+  sig = DBUS_TYPE_UINT32_AS_STRING
+    DBUS_TYPE_ARRAY_AS_STRING;
+  _dbus_assert (!dbus_signature_validate (sig, NULL));
+
+  sig = DBUS_TYPE_ARRAY_AS_STRING
+    DBUS_TYPE_DICT_ENTRY_AS_STRING;
+  _dbus_assert (!dbus_signature_validate (sig, NULL));
+
+  sig = DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING;
+  _dbus_assert (!dbus_signature_validate (sig, NULL));
+
+  sig = DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
+  _dbus_assert (!dbus_signature_validate (sig, NULL));
+
+  sig = DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+    DBUS_TYPE_INT32_AS_STRING;
+  _dbus_assert (!dbus_signature_validate (sig, NULL));
+
+  sig = DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+    DBUS_TYPE_INT32_AS_STRING
+    DBUS_TYPE_STRING_AS_STRING;
+  _dbus_assert (!dbus_signature_validate (sig, NULL));
+
+  sig = DBUS_STRUCT_END_CHAR_AS_STRING
+    DBUS_STRUCT_BEGIN_CHAR_AS_STRING;
+  _dbus_assert (!dbus_signature_validate (sig, NULL));
+
+  sig = DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+    DBUS_TYPE_BOOLEAN_AS_STRING;
+  _dbus_assert (!dbus_signature_validate (sig, NULL));
+  return TRUE;
+#if 0
+ oom:
+  _dbus_assert_not_reached ("out of memory");
+  return FALSE;
+#endif
+}
+
+#endif
+
diff --git a/src/dbus/dbus-signature.h b/src/dbus/dbus-signature.h
new file mode 100644 (file)
index 0000000..3739108
--- /dev/null
@@ -0,0 +1,81 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-signatures.h utility functions for D-Bus types
+ *
+ * Copyright (C) 2005 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_SIGNATURES_H
+#define DBUS_SIGNATURES_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-errors.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusSignature
+ * @{
+ */
+
+/**
+ * DBusSignatureIter struct; contains no public fields 
+ */
+typedef struct
+{ 
+  void *dummy1;         /**< Don't use this */
+  void *dummy2;         /**< Don't use this */
+  dbus_uint32_t dummy8; /**< Don't use this */
+  int dummy12;           /**< Don't use this */
+  int dummy17;           /**< Don't use this */
+} DBusSignatureIter;
+
+void            dbus_signature_iter_init             (DBusSignatureIter       *iter,
+                                                     const char              *signature);
+
+int             dbus_signature_iter_get_current_type (const DBusSignatureIter *iter);
+
+char *          dbus_signature_iter_get_signature    (const DBusSignatureIter *iter);
+
+int             dbus_signature_iter_get_element_type (const DBusSignatureIter *iter);
+
+dbus_bool_t     dbus_signature_iter_next             (DBusSignatureIter       *iter);
+
+void            dbus_signature_iter_recurse          (const DBusSignatureIter *iter,
+                                                     DBusSignatureIter       *subiter);
+
+dbus_bool_t     dbus_signature_validate              (const char       *signature,
+                                                     DBusError        *error);
+
+dbus_bool_t     dbus_signature_validate_single       (const char       *signature,
+                                                     DBusError        *error);
+
+dbus_bool_t     dbus_type_is_basic                   (int            typecode);
+dbus_bool_t     dbus_type_is_container               (int            typecode);
+dbus_bool_t     dbus_type_is_fixed                   (int            typecode);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_SIGNATURE_H */
diff --git a/src/dbus/dbus-spawn.c b/src/dbus/dbus-spawn.c
new file mode 100644 (file)
index 0000000..f4e3b58
--- /dev/null
@@ -0,0 +1,1457 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-spawn.c Wrapper around fork/exec
+ * 
+ * Copyright (C) 2002, 2003, 2004  Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-spawn.h"
+#include "dbus-sysdeps-unix.h"
+#include "dbus-internals.h"
+#include "dbus-test.h"
+#include "dbus-protocol.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+extern char **environ;
+
+/**
+ * @addtogroup DBusInternalsUtils
+ * @{
+ */
+
+/*
+ * I'm pretty sure this whole spawn file could be made simpler,
+ * if you thought about it a bit.
+ */
+
+/**
+ * Enumeration for status of a read()
+ */
+typedef enum
+{
+  READ_STATUS_OK,    /**< Read succeeded */
+  READ_STATUS_ERROR, /**< Some kind of error */
+  READ_STATUS_EOF    /**< EOF returned */
+} ReadStatus;
+
+static ReadStatus
+read_ints (int        fd,
+          int       *buf,
+          int        n_ints_in_buf,
+          int       *n_ints_read,
+          DBusError *error)
+{
+  size_t bytes = 0;    
+  ReadStatus retval;
+  
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  retval = READ_STATUS_OK;
+  
+  while (TRUE)
+    {
+      size_t chunk;
+      ssize_t to_read;
+
+      to_read = sizeof (int) * n_ints_in_buf - bytes;
+
+      if (to_read == 0)
+        break;
+
+    again:
+      
+      chunk = read (fd,
+                    ((char*)buf) + bytes,
+                    to_read);
+      
+      if (chunk < 0 && errno == EINTR)
+        goto again;
+          
+      if (chunk < 0)
+        {
+          dbus_set_error (error,
+                         DBUS_ERROR_SPAWN_FAILED,
+                         "Failed to read from child pipe (%s)",
+                         _dbus_strerror (errno));
+
+          retval = READ_STATUS_ERROR;
+          break;
+        }
+      else if (chunk == 0)
+        {
+          retval = READ_STATUS_EOF;
+          break; /* EOF */
+        }
+      else /* chunk > 0 */
+       bytes += chunk;
+    }
+
+  *n_ints_read = (int)(bytes / sizeof(int));
+
+  return retval;
+}
+
+static ReadStatus
+read_pid (int        fd,
+          pid_t     *buf,
+          DBusError *error)
+{
+  size_t bytes = 0;    
+  ReadStatus retval;
+  
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  retval = READ_STATUS_OK;
+  
+  while (TRUE)
+    {
+      size_t chunk;    
+      ssize_t to_read;
+      
+      to_read = sizeof (pid_t) - bytes;
+
+      if (to_read == 0)
+        break;
+
+    again:
+      
+      chunk = read (fd,
+                    ((char*)buf) + bytes,
+                    to_read);
+      if (chunk < 0 && errno == EINTR)
+        goto again;
+          
+      if (chunk < 0)
+        {
+          dbus_set_error (error,
+                         DBUS_ERROR_SPAWN_FAILED,
+                         "Failed to read from child pipe (%s)",
+                         _dbus_strerror (errno));
+
+          retval = READ_STATUS_ERROR;
+          break;
+        }
+      else if (chunk == 0)
+        {
+          retval = READ_STATUS_EOF;
+          break; /* EOF */
+        }
+      else /* chunk > 0 */
+       bytes += chunk;
+    }
+
+  return retval;
+}
+
+/* The implementation uses an intermediate child between the main process
+ * and the grandchild. The grandchild is our spawned process. The intermediate
+ * child is a babysitter process; it keeps track of when the grandchild
+ * exits/crashes, and reaps the grandchild.
+ */
+
+/* Messages from children to parents */
+enum
+{
+  CHILD_EXITED,            /* This message is followed by the exit status int */
+  CHILD_FORK_FAILED,       /* Followed by errno */
+  CHILD_EXEC_FAILED,       /* Followed by errno */
+  CHILD_PID                /* Followed by pid_t */
+};
+
+/**
+ * Babysitter implementation details
+ */
+struct DBusBabysitter
+{
+  int refcount; /**< Reference count */
+
+  char *executable; /**< executable name to use in error messages */
+  
+  int socket_to_babysitter; /**< Connection to the babysitter process */
+  int error_pipe_from_child; /**< Connection to the process that does the exec() */
+  
+  pid_t sitter_pid;  /**< PID Of the babysitter */
+  pid_t grandchild_pid; /**< PID of the grandchild */
+
+  DBusWatchList *watches; /**< Watches */
+
+  DBusWatch *error_watch; /**< Error pipe watch */
+  DBusWatch *sitter_watch; /**< Sitter pipe watch */
+
+  int errnum; /**< Error number */
+  int status; /**< Exit status code */
+  unsigned int have_child_status : 1; /**< True if child status has been reaped */
+  unsigned int have_fork_errnum : 1; /**< True if we have an error code from fork() */
+  unsigned int have_exec_errnum : 1; /**< True if we have an error code from exec() */
+};
+
+static DBusBabysitter*
+_dbus_babysitter_new (void)
+{
+  DBusBabysitter *sitter;
+
+  sitter = dbus_new0 (DBusBabysitter, 1);
+  if (sitter == NULL)
+    return NULL;
+
+  sitter->refcount = 1;
+
+  sitter->socket_to_babysitter = -1;
+  sitter->error_pipe_from_child = -1;
+  
+  sitter->sitter_pid = -1;
+  sitter->grandchild_pid = -1;
+
+  sitter->watches = _dbus_watch_list_new ();
+  if (sitter->watches == NULL)
+    goto failed;
+  
+  return sitter;
+
+ failed:
+  _dbus_babysitter_unref (sitter);
+  return NULL;
+}
+
+/**
+ * Increment the reference count on the babysitter object.
+ *
+ * @param sitter the babysitter
+ * @returns the babysitter
+ */
+DBusBabysitter *
+_dbus_babysitter_ref (DBusBabysitter *sitter)
+{
+  _dbus_assert (sitter != NULL);
+  _dbus_assert (sitter->refcount > 0);
+  
+  sitter->refcount += 1;
+
+  return sitter;
+}
+
+/**
+ * Decrement the reference count on the babysitter object.
+ * When the reference count of the babysitter object reaches
+ * zero, the babysitter is killed and the child that was being
+ * babysat gets emancipated.
+ *
+ * @param sitter the babysitter
+ */
+void
+_dbus_babysitter_unref (DBusBabysitter *sitter)
+{
+  _dbus_assert (sitter != NULL);
+  _dbus_assert (sitter->refcount > 0);
+  
+  sitter->refcount -= 1;
+  if (sitter->refcount == 0)
+    {      
+      if (sitter->socket_to_babysitter >= 0)
+        {
+          /* If we haven't forked other babysitters
+           * since this babysitter and socket were
+           * created then this close will cause the
+           * babysitter to wake up from poll with
+           * a hangup and then the babysitter will
+           * quit itself.
+           */
+          _dbus_close_socket (sitter->socket_to_babysitter, NULL);
+          sitter->socket_to_babysitter = -1;
+        }
+
+      if (sitter->error_pipe_from_child >= 0)
+        {
+          _dbus_close_socket (sitter->error_pipe_from_child, NULL);
+          sitter->error_pipe_from_child = -1;
+        }
+
+      if (sitter->sitter_pid > 0)
+        {
+          int status;
+          int ret;
+
+          /* It's possible the babysitter died on its own above 
+           * from the close, or was killed randomly
+           * by some other process, so first try to reap it
+           */
+          ret = waitpid (sitter->sitter_pid, &status, WNOHANG);
+
+          /* If we couldn't reap the child then kill it, and
+           * try again
+           */
+          if (ret == 0)
+            kill (sitter->sitter_pid, SIGKILL);
+
+        again:
+          if (ret == 0)
+            ret = waitpid (sitter->sitter_pid, &status, 0);
+
+          if (ret < 0)
+            {
+              if (errno == EINTR)
+                goto again;
+              else if (errno == ECHILD)
+                _dbus_warn ("Babysitter process not available to be reaped; should not happen\n");
+              else
+                _dbus_warn ("Unexpected error %d in waitpid() for babysitter: %s\n",
+                            errno, _dbus_strerror (errno));
+            }
+          else
+            {
+              _dbus_verbose ("Reaped %ld, waiting for babysitter %ld\n",
+                             (long) ret, (long) sitter->sitter_pid);
+              
+              if (WIFEXITED (sitter->status))
+                _dbus_verbose ("Babysitter exited with status %d\n",
+                               WEXITSTATUS (sitter->status));
+              else if (WIFSIGNALED (sitter->status))
+                _dbus_verbose ("Babysitter received signal %d\n",
+                               WTERMSIG (sitter->status));
+              else
+                _dbus_verbose ("Babysitter exited abnormally\n");
+            }
+
+          sitter->sitter_pid = -1;
+        }
+      
+      if (sitter->error_watch)
+        {
+          _dbus_watch_invalidate (sitter->error_watch);
+          _dbus_watch_unref (sitter->error_watch);
+          sitter->error_watch = NULL;
+        }
+
+      if (sitter->sitter_watch)
+        {
+          _dbus_watch_invalidate (sitter->sitter_watch);
+          _dbus_watch_unref (sitter->sitter_watch);
+          sitter->sitter_watch = NULL;
+        }
+      
+      if (sitter->watches)
+        _dbus_watch_list_free (sitter->watches);
+
+      dbus_free (sitter->executable);
+      
+      dbus_free (sitter);
+    }
+}
+
+static ReadStatus
+read_data (DBusBabysitter *sitter,
+           int             fd)
+{
+  int what;
+  int got;
+  DBusError error = DBUS_ERROR_INIT;
+  ReadStatus r;
+
+  r = read_ints (fd, &what, 1, &got, &error);
+
+  switch (r)
+    {
+    case READ_STATUS_ERROR:
+      _dbus_warn ("Failed to read data from fd %d: %s\n", fd, error.message);
+      dbus_error_free (&error);
+      return r;
+
+    case READ_STATUS_EOF:
+      return r;
+
+    case READ_STATUS_OK:
+      break;
+    }
+  
+  if (got == 1)
+    {
+      switch (what)
+        {
+        case CHILD_EXITED:
+        case CHILD_FORK_FAILED:
+        case CHILD_EXEC_FAILED:
+          {
+            int arg;
+            
+            r = read_ints (fd, &arg, 1, &got, &error);
+
+            switch (r)
+              {
+              case READ_STATUS_ERROR:
+                _dbus_warn ("Failed to read arg from fd %d: %s\n", fd, error.message);
+                dbus_error_free (&error);
+                return r;
+              case READ_STATUS_EOF:
+                return r;
+              case READ_STATUS_OK:
+                break;
+              }
+            
+            if (got == 1)
+              {
+                if (what == CHILD_EXITED)
+                  {
+                    sitter->have_child_status = TRUE;
+                    sitter->status = arg;
+                    sitter->errnum = 0;
+                    _dbus_verbose ("recorded child status exited = %d signaled = %d exitstatus = %d termsig = %d\n",
+                                   WIFEXITED (sitter->status), WIFSIGNALED (sitter->status),
+                                   WEXITSTATUS (sitter->status), WTERMSIG (sitter->status));
+                  }
+                else if (what == CHILD_FORK_FAILED)
+                  {
+                    sitter->have_fork_errnum = TRUE;
+                    sitter->errnum = arg;
+                    _dbus_verbose ("recorded fork errnum %d\n", sitter->errnum);
+                  }
+                else if (what == CHILD_EXEC_FAILED)
+                  {
+                    sitter->have_exec_errnum = TRUE;
+                    sitter->errnum = arg;
+                    _dbus_verbose ("recorded exec errnum %d\n", sitter->errnum);
+                  }
+              }
+          }
+          break;
+        case CHILD_PID:
+          {
+            pid_t pid = -1;
+
+            r = read_pid (fd, &pid, &error);
+            
+            switch (r)
+              {
+              case READ_STATUS_ERROR:
+                _dbus_warn ("Failed to read PID from fd %d: %s\n", fd, error.message);
+                dbus_error_free (&error);
+                return r;
+              case READ_STATUS_EOF:
+                return r;
+              case READ_STATUS_OK:
+                break;
+              }
+            
+            sitter->grandchild_pid = pid;
+            
+            _dbus_verbose ("recorded grandchild pid %d\n", sitter->grandchild_pid);
+          }
+          break;
+        default:
+          _dbus_warn ("Unknown message received from babysitter process\n");
+          break;
+        }
+    }
+
+  return r;
+}
+
+static void
+close_socket_to_babysitter (DBusBabysitter *sitter)
+{
+  _dbus_verbose ("Closing babysitter\n");
+  _dbus_close_socket (sitter->socket_to_babysitter, NULL);
+  sitter->socket_to_babysitter = -1;
+}
+
+static void
+close_error_pipe_from_child (DBusBabysitter *sitter)
+{
+  _dbus_verbose ("Closing child error\n");
+  _dbus_close_socket (sitter->error_pipe_from_child, NULL);
+  sitter->error_pipe_from_child = -1;
+}
+
+static void
+handle_babysitter_socket (DBusBabysitter *sitter,
+                          int             revents)
+{
+  /* Even if we have POLLHUP, we want to keep reading
+   * data until POLLIN goes away; so this function only
+   * looks at HUP/ERR if no IN is set.
+   */
+  if (revents & _DBUS_POLLIN)
+    {
+      _dbus_verbose ("Reading data from babysitter\n");
+      if (read_data (sitter, sitter->socket_to_babysitter) != READ_STATUS_OK)
+        close_socket_to_babysitter (sitter);
+    }
+  else if (revents & (_DBUS_POLLERR | _DBUS_POLLHUP))
+    {
+      close_socket_to_babysitter (sitter);
+    }
+}
+
+static void
+handle_error_pipe (DBusBabysitter *sitter,
+                   int             revents)
+{
+  if (revents & _DBUS_POLLIN)
+    {
+      _dbus_verbose ("Reading data from child error\n");
+      if (read_data (sitter, sitter->error_pipe_from_child) != READ_STATUS_OK)
+        close_error_pipe_from_child (sitter);
+    }
+  else if (revents & (_DBUS_POLLERR | _DBUS_POLLHUP))
+    {
+      close_error_pipe_from_child (sitter);
+    }
+}
+
+/* returns whether there were any poll events handled */
+static dbus_bool_t
+babysitter_iteration (DBusBabysitter *sitter,
+                      dbus_bool_t     block)
+{
+  DBusPollFD fds[2];
+  int i;
+  dbus_bool_t descriptors_ready;
+
+  descriptors_ready = FALSE;
+  
+  i = 0;
+
+  if (sitter->error_pipe_from_child >= 0)
+    {
+      fds[i].fd = sitter->error_pipe_from_child;
+      fds[i].events = _DBUS_POLLIN;
+      fds[i].revents = 0;
+      ++i;
+    }
+  
+  if (sitter->socket_to_babysitter >= 0)
+    {
+      fds[i].fd = sitter->socket_to_babysitter;
+      fds[i].events = _DBUS_POLLIN;
+      fds[i].revents = 0;
+      ++i;
+    }
+
+  if (i > 0)
+    {
+      int ret;
+
+      do
+        {
+          ret = _dbus_poll (fds, i, 0);
+        }
+      while (ret < 0 && errno == EINTR);
+
+      if (ret == 0 && block)
+        {
+          do
+            {
+              ret = _dbus_poll (fds, i, -1);
+            }
+          while (ret < 0 && errno == EINTR);
+        }
+
+      if (ret > 0)
+        {
+          descriptors_ready = TRUE;
+          
+          while (i > 0)
+            {
+              --i;
+              if (fds[i].fd == sitter->error_pipe_from_child)
+                handle_error_pipe (sitter, fds[i].revents);
+              else if (fds[i].fd == sitter->socket_to_babysitter)
+                handle_babysitter_socket (sitter, fds[i].revents);
+            }
+        }
+    }
+
+  return descriptors_ready;
+}
+
+/**
+ * Macro returns #TRUE if the babysitter still has live sockets open to the
+ * babysitter child or the grandchild.
+ */
+#define LIVE_CHILDREN(sitter) ((sitter)->socket_to_babysitter >= 0 || (sitter)->error_pipe_from_child >= 0)
+
+/**
+ * Blocks until the babysitter process gives us the PID of the spawned grandchild,
+ * then kills the spawned grandchild.
+ *
+ * @param sitter the babysitter object
+ */
+void
+_dbus_babysitter_kill_child (DBusBabysitter *sitter)
+{
+  /* be sure we have the PID of the child */
+  while (LIVE_CHILDREN (sitter) &&
+         sitter->grandchild_pid == -1)
+    babysitter_iteration (sitter, TRUE);
+
+  _dbus_verbose ("Got child PID %ld for killing\n",
+                 (long) sitter->grandchild_pid);
+  
+  if (sitter->grandchild_pid == -1)
+    return; /* child is already dead, or we're so hosed we'll never recover */
+
+  kill (sitter->grandchild_pid, SIGKILL);
+}
+
+/**
+ * Checks whether the child has exited, without blocking.
+ *
+ * @param sitter the babysitter
+ */
+dbus_bool_t
+_dbus_babysitter_get_child_exited (DBusBabysitter *sitter)
+{
+
+  /* Be sure we're up-to-date */
+  while (LIVE_CHILDREN (sitter) &&
+         babysitter_iteration (sitter, FALSE))
+    ;
+
+  /* We will have exited the babysitter when the child has exited */
+  return sitter->socket_to_babysitter < 0;
+}
+
+/**
+ * Gets the exit status of the child. We do this so implementation specific
+ * detail is not cluttering up dbus, for example the system launcher code.
+ * This can only be called if the child has exited, i.e. call
+ * _dbus_babysitter_get_child_exited(). It returns FALSE if the child
+ * did not return a status code, e.g. because the child was signaled
+ * or we failed to ever launch the child in the first place.
+ *
+ * @param sitter the babysitter
+ * @param status the returned status code
+ * @returns #FALSE on failure
+ */
+dbus_bool_t
+_dbus_babysitter_get_child_exit_status (DBusBabysitter *sitter,
+                                        int            *status)
+{
+  if (!_dbus_babysitter_get_child_exited (sitter))
+    _dbus_assert_not_reached ("Child has not exited");
+  
+  if (!sitter->have_child_status ||
+      !(WIFEXITED (sitter->status)))
+    return FALSE;
+
+  *status = WEXITSTATUS (sitter->status);
+  return TRUE;
+}
+
+/**
+ * Sets the #DBusError with an explanation of why the spawned
+ * child process exited (on a signal, or whatever). If
+ * the child process has not exited, does nothing (error
+ * will remain unset).
+ *
+ * @param sitter the babysitter
+ * @param error an error to fill in
+ */
+void
+_dbus_babysitter_set_child_exit_error (DBusBabysitter *sitter,
+                                       DBusError      *error)
+{
+  if (!_dbus_babysitter_get_child_exited (sitter))
+    return;
+
+  /* Note that if exec fails, we will also get a child status
+   * from the babysitter saying the child exited,
+   * so we need to give priority to the exec error
+   */
+  if (sitter->have_exec_errnum)
+    {
+      dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED,
+                      "Failed to execute program %s: %s",
+                      sitter->executable, _dbus_strerror (sitter->errnum));
+    }
+  else if (sitter->have_fork_errnum)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
+                      "Failed to fork a new process %s: %s",
+                      sitter->executable, _dbus_strerror (sitter->errnum));
+    }
+  else if (sitter->have_child_status)
+    {
+      if (WIFEXITED (sitter->status))
+        dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_EXITED,
+                        "Process %s exited with status %d",
+                        sitter->executable, WEXITSTATUS (sitter->status));
+      else if (WIFSIGNALED (sitter->status))
+        dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_SIGNALED,
+                        "Process %s received signal %d",
+                        sitter->executable, WTERMSIG (sitter->status));
+      else
+        dbus_set_error (error, DBUS_ERROR_FAILED,
+                        "Process %s exited abnormally",
+                        sitter->executable);
+    }
+  else
+    {
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                      "Process %s exited, reason unknown",
+                      sitter->executable);
+    }
+}
+
+/**
+ * Sets watch functions to notify us when the
+ * babysitter object needs to read/write file descriptors.
+ *
+ * @param sitter the babysitter
+ * @param add_function function to begin monitoring a new descriptor.
+ * @param remove_function function to stop monitoring a descriptor.
+ * @param toggled_function function to notify when the watch is enabled/disabled
+ * @param data data to pass to add_function and remove_function.
+ * @param free_data_function function to be called to free the data.
+ * @returns #FALSE on failure (no memory)
+ */
+dbus_bool_t
+_dbus_babysitter_set_watch_functions (DBusBabysitter            *sitter,
+                                      DBusAddWatchFunction       add_function,
+                                      DBusRemoveWatchFunction    remove_function,
+                                      DBusWatchToggledFunction   toggled_function,
+                                      void                      *data,
+                                      DBusFreeFunction           free_data_function)
+{
+  return _dbus_watch_list_set_functions (sitter->watches,
+                                         add_function,
+                                         remove_function,
+                                         toggled_function,
+                                         data,
+                                         free_data_function);
+}
+
+static dbus_bool_t
+handle_watch (DBusWatch       *watch,
+              unsigned int     condition,
+              void            *data)
+{
+  DBusBabysitter *sitter = data;
+  int revents;
+  int fd;
+  
+  revents = 0;
+  if (condition & DBUS_WATCH_READABLE)
+    revents |= _DBUS_POLLIN;
+  if (condition & DBUS_WATCH_ERROR)
+    revents |= _DBUS_POLLERR;
+  if (condition & DBUS_WATCH_HANGUP)
+    revents |= _DBUS_POLLHUP;
+
+  fd = dbus_watch_get_socket (watch);
+
+  if (fd == sitter->error_pipe_from_child)
+    handle_error_pipe (sitter, revents);
+  else if (fd == sitter->socket_to_babysitter)
+    handle_babysitter_socket (sitter, revents);
+
+  while (LIVE_CHILDREN (sitter) &&
+         babysitter_iteration (sitter, FALSE))
+    ;
+  
+  return TRUE;
+}
+
+/** Helps remember which end of the pipe is which */
+#define READ_END 0
+/** Helps remember which end of the pipe is which */
+#define WRITE_END 1
+
+
+/* Avoids a danger in threaded situations (calling close()
+ * on a file descriptor twice, and another thread has
+ * re-opened it since the first close)
+ */
+static int
+close_and_invalidate (int *fd)
+{
+  int ret;
+
+  if (*fd < 0)
+    return -1;
+  else
+    {
+      ret = _dbus_close_socket (*fd, NULL);
+      *fd = -1;
+    }
+
+  return ret;
+}
+
+static dbus_bool_t
+make_pipe (int         p[2],
+           DBusError  *error)
+{
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  if (pipe (p) < 0)
+    {
+      dbus_set_error (error,
+                     DBUS_ERROR_SPAWN_FAILED,
+                     "Failed to create pipe for communicating with child process (%s)",
+                     _dbus_strerror (errno));
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+do_write (int fd, const void *buf, size_t count)
+{
+  size_t bytes_written;
+  int ret;
+  
+  bytes_written = 0;
+  
+ again:
+  
+  ret = write (fd, ((const char*)buf) + bytes_written, count - bytes_written);
+
+  if (ret < 0)
+    {
+      if (errno == EINTR)
+        goto again;
+      else
+        {
+          _dbus_warn ("Failed to write data to pipe!\n");
+          exit (1); /* give up, we suck */
+        }
+    }
+  else
+    bytes_written += ret;
+  
+  if (bytes_written < count)
+    goto again;
+}
+
+static void
+write_err_and_exit (int fd, int msg)
+{
+  int en = errno;
+
+  do_write (fd, &msg, sizeof (msg));
+  do_write (fd, &en, sizeof (en));
+  
+  exit (1);
+}
+
+static void
+write_pid (int fd, pid_t pid)
+{
+  int msg = CHILD_PID;
+  
+  do_write (fd, &msg, sizeof (msg));
+  do_write (fd, &pid, sizeof (pid));
+}
+
+static void
+write_status_and_exit (int fd, int status)
+{
+  int msg = CHILD_EXITED;
+  
+  do_write (fd, &msg, sizeof (msg));
+  do_write (fd, &status, sizeof (status));
+  
+  exit (0);
+}
+
+static void
+do_exec (int                       child_err_report_fd,
+        char                    **argv,
+        char                    **envp,
+        DBusSpawnChildSetupFunc   child_setup,
+        void                     *user_data)
+{
+#ifdef DBUS_BUILD_TESTS
+  int i, max_open;
+#endif
+
+  _dbus_verbose_reset ();
+  _dbus_verbose ("Child process has PID " DBUS_PID_FORMAT "\n",
+                 _dbus_getpid ());
+  
+  if (child_setup)
+    (* child_setup) (user_data);
+
+#ifdef DBUS_BUILD_TESTS
+  max_open = sysconf (_SC_OPEN_MAX);
+  
+  for (i = 3; i < max_open; i++)
+    {
+      int retval;
+
+      if (i == child_err_report_fd)
+        continue;
+      
+      retval = fcntl (i, F_GETFD);
+
+      if (retval != -1 && !(retval & FD_CLOEXEC))
+       _dbus_warn ("Fd %d did not have the close-on-exec flag set!\n", i);
+    }
+#endif
+
+  if (envp == NULL)
+    {
+      _dbus_assert (environ != NULL);
+
+      envp = environ;
+    }
+  
+  execve (argv[0], argv, envp);
+  
+  /* Exec failed */
+  write_err_and_exit (child_err_report_fd,
+                      CHILD_EXEC_FAILED);
+}
+
+static void
+check_babysit_events (pid_t grandchild_pid,
+                      int   parent_pipe,
+                      int   revents)
+{
+  pid_t ret;
+  int status;
+  
+  do
+    {
+      ret = waitpid (grandchild_pid, &status, WNOHANG);
+      /* The man page says EINTR can't happen with WNOHANG,
+       * but there are reports of it (maybe only with valgrind?)
+       */
+    }
+  while (ret < 0 && errno == EINTR);
+
+  if (ret == 0)
+    {
+      _dbus_verbose ("no child exited\n");
+      
+      ; /* no child exited */
+    }
+  else if (ret < 0)
+    {
+      /* This isn't supposed to happen. */
+      _dbus_warn ("unexpected waitpid() failure in check_babysit_events(): %s\n",
+                  _dbus_strerror (errno));
+      exit (1);
+    }
+  else if (ret == grandchild_pid)
+    {
+      /* Child exited */
+      _dbus_verbose ("reaped child pid %ld\n", (long) ret);
+      
+      write_status_and_exit (parent_pipe, status);
+    }
+  else
+    {
+      _dbus_warn ("waitpid() reaped pid %d that we've never heard of\n",
+                  (int) ret);
+      exit (1);
+    }
+
+  if (revents & _DBUS_POLLIN)
+    {
+      _dbus_verbose ("babysitter got POLLIN from parent pipe\n");
+    }
+
+  if (revents & (_DBUS_POLLERR | _DBUS_POLLHUP))
+    {
+      /* Parent is gone, so we just exit */
+      _dbus_verbose ("babysitter got POLLERR or POLLHUP from parent\n");
+      exit (0);
+    }
+}
+
+static int babysit_sigchld_pipe = -1;
+
+static void
+babysit_signal_handler (int signo)
+{
+  char b = '\0';
+ again:
+  if (write (babysit_sigchld_pipe, &b, 1) <= 0) 
+    if (errno == EINTR)
+      goto again;
+}
+
+static void
+babysit (pid_t grandchild_pid,
+         int   parent_pipe)
+{
+  int sigchld_pipe[2];
+
+  /* We don't exec, so we keep parent state, such as the pid that
+   * _dbus_verbose() uses. Reset the pid here.
+   */
+  _dbus_verbose_reset ();
+  
+  /* I thought SIGCHLD would just wake up the poll, but
+   * that didn't seem to work, so added this pipe.
+   * Probably the pipe is more likely to work on busted
+   * operating systems anyhow.
+   */
+  if (pipe (sigchld_pipe) < 0)
+    {
+      _dbus_warn ("Not enough file descriptors to create pipe in babysitter process\n");
+      exit (1);
+    }
+
+  babysit_sigchld_pipe = sigchld_pipe[WRITE_END];
+
+  _dbus_set_signal_handler (SIGCHLD, babysit_signal_handler);
+  
+  write_pid (parent_pipe, grandchild_pid);
+
+  check_babysit_events (grandchild_pid, parent_pipe, 0);
+
+  while (TRUE)
+    {
+      DBusPollFD pfds[2];
+      
+      pfds[0].fd = parent_pipe;
+      pfds[0].events = _DBUS_POLLIN;
+      pfds[0].revents = 0;
+
+      pfds[1].fd = sigchld_pipe[READ_END];
+      pfds[1].events = _DBUS_POLLIN;
+      pfds[1].revents = 0;
+      
+      if (_dbus_poll (pfds, _DBUS_N_ELEMENTS (pfds), -1) < 0 && errno != EINTR)
+        {
+          _dbus_warn ("_dbus_poll() error: %s\n", strerror (errno));
+          exit (1);
+        }
+
+      if (pfds[0].revents != 0)
+        {
+          check_babysit_events (grandchild_pid, parent_pipe, pfds[0].revents);
+        }
+      else if (pfds[1].revents & _DBUS_POLLIN)
+        {
+          char b;
+          read (sigchld_pipe[READ_END], &b, 1);
+          /* do waitpid check */
+          check_babysit_events (grandchild_pid, parent_pipe, 0);
+        }
+    }
+  
+  exit (1);
+}
+
+/**
+ * Spawns a new process. The executable name and argv[0]
+ * are the same, both are provided in argv[0]. The child_setup
+ * function is passed the given user_data and is run in the child
+ * just before calling exec().
+ *
+ * Also creates a "babysitter" which tracks the status of the
+ * child process, advising the parent if the child exits.
+ * If the spawn fails, no babysitter is created.
+ * If sitter_p is #NULL, no babysitter is kept.
+ *
+ * @param sitter_p return location for babysitter or #NULL
+ * @param argv the executable and arguments
+ * @param env the environment (not used on unix yet)
+ * @param child_setup function to call in child pre-exec()
+ * @param user_data user data for setup function
+ * @param error error object to be filled in if function fails
+ * @returns #TRUE on success, #FALSE if error is filled in
+ */
+dbus_bool_t
+_dbus_spawn_async_with_babysitter (DBusBabysitter          **sitter_p,
+                                   char                    **argv,
+                                   char                    **env,
+                                   DBusSpawnChildSetupFunc   child_setup,
+                                   void                     *user_data,
+                                   DBusError                *error)
+{
+  DBusBabysitter *sitter;
+  int child_err_report_pipe[2] = { -1, -1 };
+  int babysitter_pipe[2] = { -1, -1 };
+  pid_t pid;
+  
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  if (sitter_p != NULL)
+    *sitter_p = NULL;
+
+  sitter = NULL;
+
+  sitter = _dbus_babysitter_new ();
+  if (sitter == NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      return FALSE;
+    }
+
+  sitter->executable = _dbus_strdup (argv[0]);
+  if (sitter->executable == NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto cleanup_and_fail;
+    }
+  
+  if (!make_pipe (child_err_report_pipe, error))
+    goto cleanup_and_fail;
+
+  _dbus_fd_set_close_on_exec (child_err_report_pipe[READ_END]);
+  _dbus_fd_set_close_on_exec (child_err_report_pipe[WRITE_END]);
+
+  if (!_dbus_full_duplex_pipe (&babysitter_pipe[0], &babysitter_pipe[1], TRUE, error))
+    goto cleanup_and_fail;
+
+  _dbus_fd_set_close_on_exec (babysitter_pipe[0]);
+  _dbus_fd_set_close_on_exec (babysitter_pipe[1]);
+
+  /* Setting up the babysitter is only useful in the parent,
+   * but we don't want to run out of memory and fail
+   * after we've already forked, since then we'd leak
+   * child processes everywhere.
+   */
+  sitter->error_watch = _dbus_watch_new (child_err_report_pipe[READ_END],
+                                         DBUS_WATCH_READABLE,
+                                         TRUE, handle_watch, sitter, NULL);
+  if (sitter->error_watch == NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto cleanup_and_fail;
+    }
+        
+  if (!_dbus_watch_list_add_watch (sitter->watches,  sitter->error_watch))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto cleanup_and_fail;
+    }
+      
+  sitter->sitter_watch = _dbus_watch_new (babysitter_pipe[0],
+                                          DBUS_WATCH_READABLE,
+                                          TRUE, handle_watch, sitter, NULL);
+  if (sitter->sitter_watch == NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto cleanup_and_fail;
+    }
+      
+  if (!_dbus_watch_list_add_watch (sitter->watches,  sitter->sitter_watch))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto cleanup_and_fail;
+    }
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  pid = fork ();
+  
+  if (pid < 0)
+    {
+      dbus_set_error (error,
+                     DBUS_ERROR_SPAWN_FORK_FAILED,
+                     "Failed to fork (%s)",
+                     _dbus_strerror (errno));
+      goto cleanup_and_fail;
+    }
+  else if (pid == 0)
+    {
+      /* Immediate child, this is the babysitter process. */
+      int grandchild_pid;
+      
+      /* Be sure we crash if the parent exits
+       * and we write to the err_report_pipe
+       */
+      signal (SIGPIPE, SIG_DFL);
+
+      /* Close the parent's end of the pipes. */
+      close_and_invalidate (&child_err_report_pipe[READ_END]);
+      close_and_invalidate (&babysitter_pipe[0]);
+      
+      /* Create the child that will exec () */
+      grandchild_pid = fork ();
+      
+      if (grandchild_pid < 0)
+       {
+         write_err_and_exit (babysitter_pipe[1],
+                             CHILD_FORK_FAILED);
+          _dbus_assert_not_reached ("Got to code after write_err_and_exit()");
+       }
+      else if (grandchild_pid == 0)
+       {
+         do_exec (child_err_report_pipe[WRITE_END],
+                  argv,
+                  env,
+                  child_setup, user_data);
+          _dbus_assert_not_reached ("Got to code after exec() - should have exited on error");
+       }
+      else
+       {
+          babysit (grandchild_pid, babysitter_pipe[1]);
+          _dbus_assert_not_reached ("Got to code after babysit()");
+       }
+    }
+  else
+    {      
+      /* Close the uncared-about ends of the pipes */
+      close_and_invalidate (&child_err_report_pipe[WRITE_END]);
+      close_and_invalidate (&babysitter_pipe[1]);
+
+      sitter->socket_to_babysitter = babysitter_pipe[0];
+      babysitter_pipe[0] = -1;
+      
+      sitter->error_pipe_from_child = child_err_report_pipe[READ_END];
+      child_err_report_pipe[READ_END] = -1;
+
+      sitter->sitter_pid = pid;
+
+      if (sitter_p != NULL)
+        *sitter_p = sitter;
+      else
+        _dbus_babysitter_unref (sitter);
+
+      dbus_free_string_array (env);
+
+      _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+      
+      return TRUE;
+    }
+
+ cleanup_and_fail:
+
+  _DBUS_ASSERT_ERROR_IS_SET (error);
+  
+  close_and_invalidate (&child_err_report_pipe[READ_END]);
+  close_and_invalidate (&child_err_report_pipe[WRITE_END]);
+  close_and_invalidate (&babysitter_pipe[0]);
+  close_and_invalidate (&babysitter_pipe[1]);
+
+  if (sitter != NULL)
+    _dbus_babysitter_unref (sitter);
+  
+  return FALSE;
+}
+
+/** @} */
+
+#ifdef DBUS_BUILD_TESTS
+
+static void
+_dbus_babysitter_block_for_child_exit (DBusBabysitter *sitter)
+{
+  while (LIVE_CHILDREN (sitter))
+    babysitter_iteration (sitter, TRUE);
+}
+
+static dbus_bool_t
+check_spawn_nonexistent (void *data)
+{
+  char *argv[4] = { NULL, NULL, NULL, NULL };
+  DBusBabysitter *sitter = NULL;
+  DBusError error = DBUS_ERROR_INIT;
+
+  /*** Test launching nonexistent binary */
+  
+  argv[0] = "/this/does/not/exist/32542sdgafgafdg";
+  if (_dbus_spawn_async_with_babysitter (&sitter, argv,
+                                         NULL, NULL, NULL,
+                                         &error))
+    {
+      _dbus_babysitter_block_for_child_exit (sitter);
+      _dbus_babysitter_set_child_exit_error (sitter, &error);
+    }
+
+  if (sitter)
+    _dbus_babysitter_unref (sitter);
+
+  if (!dbus_error_is_set (&error))
+    {
+      _dbus_warn ("Did not get an error launching nonexistent executable\n");
+      return FALSE;
+    }
+
+  if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
+        dbus_error_has_name (&error, DBUS_ERROR_SPAWN_EXEC_FAILED)))
+    {
+      _dbus_warn ("Not expecting error when launching nonexistent executable: %s: %s\n",
+                  error.name, error.message);
+      dbus_error_free (&error);
+      return FALSE;
+    }
+
+  dbus_error_free (&error);
+  
+  return TRUE;
+}
+
+static dbus_bool_t
+check_spawn_segfault (void *data)
+{
+  char *argv[4] = { NULL, NULL, NULL, NULL };
+  DBusBabysitter *sitter = NULL;
+  DBusError error = DBUS_ERROR_INIT;
+
+  /*** Test launching segfault binary */
+  
+  argv[0] = TEST_SEGFAULT_BINARY;
+  if (_dbus_spawn_async_with_babysitter (&sitter, argv,
+                                         NULL, NULL, NULL,
+                                         &error))
+    {
+      _dbus_babysitter_block_for_child_exit (sitter);
+      _dbus_babysitter_set_child_exit_error (sitter, &error);
+    }
+
+  if (sitter)
+    _dbus_babysitter_unref (sitter);
+
+  if (!dbus_error_is_set (&error))
+    {
+      _dbus_warn ("Did not get an error launching segfaulting binary\n");
+      return FALSE;
+    }
+
+  if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
+        dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_SIGNALED)))
+    {
+      _dbus_warn ("Not expecting error when launching segfaulting executable: %s: %s\n",
+                  error.name, error.message);
+      dbus_error_free (&error);
+      return FALSE;
+    }
+
+  dbus_error_free (&error);
+  
+  return TRUE;
+}
+
+static dbus_bool_t
+check_spawn_exit (void *data)
+{
+  char *argv[4] = { NULL, NULL, NULL, NULL };
+  DBusBabysitter *sitter = NULL;
+  DBusError error = DBUS_ERROR_INIT;
+
+  /*** Test launching exit failure binary */
+  
+  argv[0] = TEST_EXIT_BINARY;
+  if (_dbus_spawn_async_with_babysitter (&sitter, argv,
+                                         NULL, NULL, NULL,
+                                         &error))
+    {
+      _dbus_babysitter_block_for_child_exit (sitter);
+      _dbus_babysitter_set_child_exit_error (sitter, &error);
+    }
+
+  if (sitter)
+    _dbus_babysitter_unref (sitter);
+
+  if (!dbus_error_is_set (&error))
+    {
+      _dbus_warn ("Did not get an error launching binary that exited with failure code\n");
+      return FALSE;
+    }
+
+  if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
+        dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
+    {
+      _dbus_warn ("Not expecting error when launching exiting executable: %s: %s\n",
+                  error.name, error.message);
+      dbus_error_free (&error);
+      return FALSE;
+    }
+
+  dbus_error_free (&error);
+  
+  return TRUE;
+}
+
+static dbus_bool_t
+check_spawn_and_kill (void *data)
+{
+  char *argv[4] = { NULL, NULL, NULL, NULL };
+  DBusBabysitter *sitter = NULL;
+  DBusError error = DBUS_ERROR_INIT;
+
+  /*** Test launching sleeping binary then killing it */
+
+  argv[0] = TEST_SLEEP_FOREVER_BINARY;
+  if (_dbus_spawn_async_with_babysitter (&sitter, argv,
+                                         NULL, NULL, NULL,
+                                         &error))
+    {
+      _dbus_babysitter_kill_child (sitter);
+      
+      _dbus_babysitter_block_for_child_exit (sitter);
+      
+      _dbus_babysitter_set_child_exit_error (sitter, &error);
+    }
+
+  if (sitter)
+    _dbus_babysitter_unref (sitter);
+
+  if (!dbus_error_is_set (&error))
+    {
+      _dbus_warn ("Did not get an error after killing spawned binary\n");
+      return FALSE;
+    }
+
+  if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
+        dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_SIGNALED)))
+    {
+      _dbus_warn ("Not expecting error when killing executable: %s: %s\n",
+                  error.name, error.message);
+      dbus_error_free (&error);
+      return FALSE;
+    }
+
+  dbus_error_free (&error);
+  
+  return TRUE;
+}
+
+dbus_bool_t
+_dbus_spawn_test (const char *test_data_dir)
+{
+  if (!_dbus_test_oom_handling ("spawn_nonexistent",
+                                check_spawn_nonexistent,
+                                NULL))
+    return FALSE;
+
+  if (!_dbus_test_oom_handling ("spawn_segfault",
+                                check_spawn_segfault,
+                                NULL))
+    return FALSE;
+
+  if (!_dbus_test_oom_handling ("spawn_exit",
+                                check_spawn_exit,
+                                NULL))
+    return FALSE;
+
+  if (!_dbus_test_oom_handling ("spawn_and_kill",
+                                check_spawn_and_kill,
+                                NULL))
+    return FALSE;
+  
+  return TRUE;
+}
+#endif
diff --git a/src/dbus/dbus-spawn.h b/src/dbus/dbus-spawn.h
new file mode 100644 (file)
index 0000000..7d8a67a
--- /dev/null
@@ -0,0 +1,61 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-spawn.h Wrapper around fork/exec
+ * 
+ * Copyright (C) 2002, 2003  Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_SPAWN_H
+#define DBUS_SPAWN_H
+
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-watch.h>
+
+DBUS_BEGIN_DECLS
+
+typedef void (* DBusSpawnChildSetupFunc) (void *user_data);
+
+typedef struct DBusBabysitter DBusBabysitter;
+
+dbus_bool_t _dbus_spawn_async_with_babysitter     (DBusBabysitter           **sitter_p,
+                                                   char                     **argv,
+                                                   char                     **env,
+                                                   DBusSpawnChildSetupFunc    child_setup,
+                                                   void                      *user_data,
+                                                   DBusError                 *error);
+DBusBabysitter* _dbus_babysitter_ref              (DBusBabysitter            *sitter);
+void        _dbus_babysitter_unref                (DBusBabysitter            *sitter);
+void        _dbus_babysitter_kill_child           (DBusBabysitter            *sitter);
+dbus_bool_t _dbus_babysitter_get_child_exited     (DBusBabysitter            *sitter);
+void        _dbus_babysitter_set_child_exit_error (DBusBabysitter            *sitter,
+                                                   DBusError                 *error);
+dbus_bool_t _dbus_babysitter_get_child_exit_status (DBusBabysitter           *sitter,
+                                                    int                      *status);
+dbus_bool_t _dbus_babysitter_set_watch_functions  (DBusBabysitter            *sitter,
+                                                   DBusAddWatchFunction       add_function,
+                                                   DBusRemoveWatchFunction    remove_function,
+                                                   DBusWatchToggledFunction   toggled_function,
+                                                   void                      *data,
+                                                   DBusFreeFunction           free_data_function);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_SPAWN_H */
diff --git a/src/dbus/dbus-string-private.h b/src/dbus/dbus-string-private.h
new file mode 100644 (file)
index 0000000..16d7196
--- /dev/null
@@ -0,0 +1,126 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-string-private.h String utility class (internal to D-Bus implementation)
+ * 
+ * Copyright (C) 2002, 2003  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_STRING_PRIVATE_H
+#define DBUS_STRING_PRIVATE_H
+
+#include <config.h>
+
+#include <dbus/dbus-memory.h>
+#include <dbus/dbus-types.h>
+
+#ifndef DBUS_CAN_USE_DBUS_STRING_PRIVATE
+#error "Don't go including dbus-string-private.h for no good reason"
+#endif
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @brief Internals of DBusString.
+ * 
+ * DBusString internals. DBusString is an opaque objects, it must be
+ * used via accessor functions.
+ */
+typedef struct
+{
+  unsigned char *str;            /**< String data, plus nul termination */
+  int            len;            /**< Length without nul */
+  int            allocated;      /**< Allocated size of data */
+  int            max_length;     /**< Max length of this string, without nul byte */
+  unsigned int   constant : 1;   /**< String data is not owned by DBusString */
+  unsigned int   locked : 1;     /**< DBusString has been locked and can't be changed */
+  unsigned int   invalid : 1;    /**< DBusString is invalid (e.g. already freed) */
+  unsigned int   align_offset : 3; /**< str - align_offset is the actual malloc block */
+} DBusRealString;
+
+
+/**
+ * @defgroup DBusStringInternals DBusString implementation details
+ * @ingroup  DBusInternals
+ * @brief DBusString implementation details
+ *
+ * The guts of DBusString.
+ *
+ * @{
+ */
+
+/**
+ * This is the maximum max length (and thus also the maximum length)
+ * of a DBusString
+ */
+#define _DBUS_STRING_MAX_MAX_LENGTH (_DBUS_INT32_MAX - _DBUS_STRING_ALLOCATION_PADDING)
+
+/**
+ * Checks a bunch of assertions about a string object
+ *
+ * @param real the DBusRealString
+ */
+#define DBUS_GENERIC_STRING_PREAMBLE(real) _dbus_assert ((real) != NULL); _dbus_assert (!(real)->invalid); _dbus_assert ((real)->len >= 0); _dbus_assert ((real)->allocated >= 0); _dbus_assert ((real)->max_length >= 0); _dbus_assert ((real)->len <= ((real)->allocated - _DBUS_STRING_ALLOCATION_PADDING)); _dbus_assert ((real)->len <= (real)->max_length)
+
+/**
+ * Checks assertions about a string object that needs to be
+ * modifiable - may not be locked or const. Also declares
+ * the "real" variable pointing to DBusRealString. 
+ * @param str the string
+ */
+#define DBUS_STRING_PREAMBLE(str) DBusRealString *real = (DBusRealString*) str; \
+  DBUS_GENERIC_STRING_PREAMBLE (real);                                          \
+  _dbus_assert (!(real)->constant);                                             \
+  _dbus_assert (!(real)->locked)
+
+/**
+ * Checks assertions about a string object that may be locked but
+ * can't be const. i.e. a string object that we can free.  Also
+ * declares the "real" variable pointing to DBusRealString.
+ *
+ * @param str the string
+ */
+#define DBUS_LOCKED_STRING_PREAMBLE(str) DBusRealString *real = (DBusRealString*) str; \
+  DBUS_GENERIC_STRING_PREAMBLE (real);                                                 \
+  _dbus_assert (!(real)->constant)
+
+/**
+ * Checks assertions about a string that may be const or locked.  Also
+ * declares the "real" variable pointing to DBusRealString.
+ * @param str the string.
+ */
+#define DBUS_CONST_STRING_PREAMBLE(str) const DBusRealString *real = (DBusRealString*) str; \
+  DBUS_GENERIC_STRING_PREAMBLE (real)
+
+/**
+ * Checks for ASCII blank byte
+ * @param c the byte
+ */
+#define DBUS_IS_ASCII_BLANK(c) ((c) == ' ' || (c) == '\t')
+
+/**
+ * Checks for ASCII whitespace byte
+ * @param c the byte
+ */
+#define DBUS_IS_ASCII_WHITE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r')
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_STRING_PRIVATE_H */
diff --git a/src/dbus/dbus-string-util.c b/src/dbus/dbus-string-util.c
new file mode 100644 (file)
index 0000000..aed9487
--- /dev/null
@@ -0,0 +1,878 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-string-util.c Would be in dbus-string.c, but not used in libdbus
+ * 
+ * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc.
+ * Copyright (C) 2006 Ralf Habacker <ralf.habacker@freenet.de>
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-internals.h"
+#include "dbus-string.h"
+#define DBUS_CAN_USE_DBUS_STRING_PRIVATE 1
+#include "dbus-string-private.h"
+
+/**
+ * @addtogroup DBusString
+ * @{
+ */
+
+/**
+ * Returns whether a string ends with the given suffix
+ *
+ * @todo memcmp might make this faster.
+ * 
+ * @param a the string
+ * @param c_str the C-style string
+ * @returns #TRUE if the string ends with the suffix
+ */
+dbus_bool_t
+_dbus_string_ends_with_c_str (const DBusString *a,
+                              const char       *c_str)
+{
+  const unsigned char *ap;
+  const unsigned char *bp;
+  const unsigned char *a_end;
+  unsigned long c_str_len;
+  const DBusRealString *real_a = (const DBusRealString*) a;
+  DBUS_GENERIC_STRING_PREAMBLE (real_a);
+  _dbus_assert (c_str != NULL);
+  
+  c_str_len = strlen (c_str);
+  if (((unsigned long)real_a->len) < c_str_len)
+    return FALSE;
+  
+  ap = real_a->str + (real_a->len - c_str_len);
+  bp = (const unsigned char*) c_str;
+  a_end = real_a->str + real_a->len;
+  while (ap != a_end)
+    {
+      if (*ap != *bp)
+        return FALSE;
+      
+      ++ap;
+      ++bp;
+    }
+
+  _dbus_assert (*ap == '\0');
+  _dbus_assert (*bp == '\0');
+  
+  return TRUE;
+}
+
+/**
+ * Find the given byte scanning backward from the given start.
+ * Sets *found to -1 if the byte is not found.
+ *
+ * @param str the string
+ * @param start the place to start scanning (will not find the byte at this point)
+ * @param byte the byte to find
+ * @param found return location for where it was found
+ * @returns #TRUE if found
+ */
+dbus_bool_t
+_dbus_string_find_byte_backward (const DBusString  *str,
+                                 int                start,
+                                 unsigned char      byte,
+                                 int               *found)
+{
+  int i;
+  DBUS_CONST_STRING_PREAMBLE (str);
+  _dbus_assert (start <= real->len);
+  _dbus_assert (start >= 0);
+  _dbus_assert (found != NULL);
+
+  i = start - 1;
+  while (i >= 0)
+    {
+      if (real->str[i] == byte)
+        break;
+      
+      --i;
+    }
+
+  if (found)
+    *found = i;
+
+  return i >= 0;
+}
+
+/** @} */
+
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-test.h"
+#include <stdio.h>
+
+static void
+test_max_len (DBusString *str,
+              int         max_len)
+{
+  if (max_len > 0)
+    {
+      if (!_dbus_string_set_length (str, max_len - 1))
+        _dbus_assert_not_reached ("setting len to one less than max should have worked");
+    }
+
+  if (!_dbus_string_set_length (str, max_len))
+    _dbus_assert_not_reached ("setting len to max len should have worked");
+
+  if (_dbus_string_set_length (str, max_len + 1))
+    _dbus_assert_not_reached ("setting len to one more than max len should not have worked");
+
+  if (!_dbus_string_set_length (str, 0))
+    _dbus_assert_not_reached ("setting len to zero should have worked");
+}
+
+static void
+test_hex_roundtrip (const unsigned char *data,
+                    int                  len)
+{
+  DBusString orig;
+  DBusString encoded;
+  DBusString decoded;
+  int end;
+
+  if (len < 0)
+    len = strlen (data);
+  
+  if (!_dbus_string_init (&orig))
+    _dbus_assert_not_reached ("could not init string");
+
+  if (!_dbus_string_init (&encoded))
+    _dbus_assert_not_reached ("could not init string");
+  
+  if (!_dbus_string_init (&decoded))
+    _dbus_assert_not_reached ("could not init string");
+
+  if (!_dbus_string_append_len (&orig, data, len))
+    _dbus_assert_not_reached ("couldn't append orig data");
+
+  if (!_dbus_string_hex_encode (&orig, 0, &encoded, 0))
+    _dbus_assert_not_reached ("could not encode");
+
+  if (!_dbus_string_hex_decode (&encoded, 0, &end, &decoded, 0))
+    _dbus_assert_not_reached ("could not decode");
+    
+  _dbus_assert (_dbus_string_get_length (&encoded) == end);
+
+  if (!_dbus_string_equal (&orig, &decoded))
+    {
+      const char *s;
+      
+      printf ("Original string %d bytes encoded %d bytes decoded %d bytes\n",
+              _dbus_string_get_length (&orig),
+              _dbus_string_get_length (&encoded),
+              _dbus_string_get_length (&decoded));
+      printf ("Original: %s\n", data);
+      s = _dbus_string_get_const_data (&decoded);
+      printf ("Decoded: %s\n", s);
+      _dbus_assert_not_reached ("original string not the same as string decoded from hex");
+    }
+  
+  _dbus_string_free (&orig);
+  _dbus_string_free (&encoded);
+  _dbus_string_free (&decoded);  
+}
+
+typedef void (* TestRoundtripFunc) (const unsigned char *data,
+                                    int                  len);
+static void
+test_roundtrips (TestRoundtripFunc func)
+{
+  (* func) ("Hello this is a string\n", -1);
+  (* func) ("Hello this is a string\n1", -1);
+  (* func) ("Hello this is a string\n12", -1);
+  (* func) ("Hello this is a string\n123", -1);
+  (* func) ("Hello this is a string\n1234", -1);
+  (* func) ("Hello this is a string\n12345", -1);
+  (* func) ("", 0);
+  (* func) ("1", 1);
+  (* func) ("12", 2);
+  (* func) ("123", 3);
+  (* func) ("1234", 4);
+  (* func) ("12345", 5);
+  (* func) ("", 1);
+  (* func) ("1", 2);
+  (* func) ("12", 3);
+  (* func) ("123", 4);
+  (* func) ("1234", 5);
+  (* func) ("12345", 6);
+  {
+    unsigned char buf[512];
+    int i;
+    
+    i = 0;
+    while (i < _DBUS_N_ELEMENTS (buf))
+      {
+        buf[i] = i;
+        ++i;
+      }
+    i = 0;
+    while (i < _DBUS_N_ELEMENTS (buf))
+      {
+        (* func) (buf, i);
+        ++i;
+      }
+  }
+}
+
+#ifdef DBUS_BUILD_TESTS
+/* The max length thing is sort of a historical artifact
+ * from a feature that turned out to be dumb; perhaps
+ * we should purge it entirely. The problem with
+ * the feature is that it looks like memory allocation
+ * failure, but is not a transient or resolvable failure.
+ */
+static void
+set_max_length (DBusString *str,
+                int         max_length)
+{
+  DBusRealString *real;
+  
+  real = (DBusRealString*) str;
+
+  real->max_length = max_length;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * @ingroup DBusStringInternals
+ * Unit test for DBusString.
+ *
+ * @todo Need to write tests for _dbus_string_copy() and
+ * _dbus_string_move() moving to/from each of start/middle/end of a
+ * string. Also need tests for _dbus_string_move_len ()
+ * 
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_string_test (void)
+{
+  DBusString str;
+  DBusString other;
+  int i, end;
+  long v;
+  double d;
+  int lens[] = { 0, 1, 2, 3, 4, 5, 10, 16, 17, 18, 25, 31, 32, 33, 34, 35, 63, 64, 65, 66, 67, 68, 69, 70, 71, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136 };
+  char *s;
+  dbus_unichar_t ch;
+  
+  i = 0;
+  while (i < _DBUS_N_ELEMENTS (lens))
+    {
+      if (!_dbus_string_init (&str))
+        _dbus_assert_not_reached ("failed to init string");
+
+      set_max_length (&str, lens[i]);
+      
+      test_max_len (&str, lens[i]);
+      _dbus_string_free (&str);
+
+      ++i;
+    }
+
+  /* Test shortening and setting length */
+  i = 0;
+  while (i < _DBUS_N_ELEMENTS (lens))
+    {
+      int j;
+      
+      if (!_dbus_string_init (&str))
+        _dbus_assert_not_reached ("failed to init string");
+
+      set_max_length (&str, lens[i]);
+      
+      if (!_dbus_string_set_length (&str, lens[i]))
+        _dbus_assert_not_reached ("failed to set string length");
+
+      j = lens[i];
+      while (j > 0)
+        {
+          _dbus_assert (_dbus_string_get_length (&str) == j);
+          if (j > 0)
+            {
+              _dbus_string_shorten (&str, 1);
+              _dbus_assert (_dbus_string_get_length (&str) == (j - 1));
+            }
+          --j;
+        }
+      
+      _dbus_string_free (&str);
+
+      ++i;
+    }
+
+  /* Test equality */
+  if (!_dbus_string_init (&str))
+    _dbus_assert_not_reached ("oom");
+
+  if (!_dbus_string_append (&str, "Hello World"))
+    _dbus_assert_not_reached ("oom");
+
+  _dbus_string_init_const (&other, "H");
+  _dbus_assert (_dbus_string_equal_substring (&str, 0, 1, &other, 0));
+  _dbus_assert (_dbus_string_equal_substring (&str, 1, 0, &other, 1));
+  _dbus_string_init_const (&other, "Hello");
+  _dbus_assert (_dbus_string_equal_substring (&str, 0, 5, &other, 0));
+  _dbus_assert (_dbus_string_equal_substring (&str, 1, 4, &other, 1));
+  _dbus_assert (_dbus_string_equal_substring (&str, 2, 3, &other, 2));
+  _dbus_assert (_dbus_string_equal_substring (&str, 3, 2, &other, 3));
+  _dbus_assert (_dbus_string_equal_substring (&str, 4, 1, &other, 4));
+  _dbus_assert (_dbus_string_equal_substring (&str, 5, 0, &other, 5));
+
+  _dbus_assert (_dbus_string_equal_substring (&other, 0, 5, &str, 0));
+  _dbus_assert (_dbus_string_equal_substring (&other, 1, 4, &str, 1));
+  _dbus_assert (_dbus_string_equal_substring (&other, 2, 3, &str, 2));
+  _dbus_assert (_dbus_string_equal_substring (&other, 3, 2, &str, 3));
+  _dbus_assert (_dbus_string_equal_substring (&other, 4, 1, &str, 4));
+  _dbus_assert (_dbus_string_equal_substring (&other, 5, 0, &str, 5));
+
+  
+  _dbus_string_init_const (&other, "World");
+  _dbus_assert (_dbus_string_equal_substring (&str, 6,  5, &other, 0));
+  _dbus_assert (_dbus_string_equal_substring (&str, 7,  4, &other, 1));
+  _dbus_assert (_dbus_string_equal_substring (&str, 8,  3, &other, 2));
+  _dbus_assert (_dbus_string_equal_substring (&str, 9,  2, &other, 3));
+  _dbus_assert (_dbus_string_equal_substring (&str, 10, 1, &other, 4));
+  _dbus_assert (_dbus_string_equal_substring (&str, 11, 0, &other, 5));
+
+  _dbus_assert (_dbus_string_equal_substring (&other, 0, 5, &str, 6));
+  _dbus_assert (_dbus_string_equal_substring (&other, 1, 4, &str, 7));
+  _dbus_assert (_dbus_string_equal_substring (&other, 2, 3, &str, 8));
+  _dbus_assert (_dbus_string_equal_substring (&other, 3, 2, &str, 9));
+  _dbus_assert (_dbus_string_equal_substring (&other, 4, 1, &str, 10));
+  _dbus_assert (_dbus_string_equal_substring (&other, 5, 0, &str, 11));
+  
+  _dbus_string_free (&str);
+  
+  /* Test appending data */
+  if (!_dbus_string_init (&str))
+    _dbus_assert_not_reached ("failed to init string");
+
+  i = 0;
+  while (i < 10)
+    {
+      if (!_dbus_string_append (&str, "a"))
+        _dbus_assert_not_reached ("failed to append string to string\n");
+
+      _dbus_assert (_dbus_string_get_length (&str) == i * 2 + 1);
+
+      if (!_dbus_string_append_byte (&str, 'b'))
+        _dbus_assert_not_reached ("failed to append byte to string\n");
+
+      _dbus_assert (_dbus_string_get_length (&str) == i * 2 + 2);
+                    
+      ++i;
+    }
+
+  _dbus_string_free (&str);
+
+  /* Check steal_data */
+  
+  if (!_dbus_string_init (&str))
+    _dbus_assert_not_reached ("failed to init string");
+
+  if (!_dbus_string_append (&str, "Hello World"))
+    _dbus_assert_not_reached ("could not append to string");
+
+  i = _dbus_string_get_length (&str);
+  
+  if (!_dbus_string_steal_data (&str, &s))
+    _dbus_assert_not_reached ("failed to steal data");
+
+  _dbus_assert (_dbus_string_get_length (&str) == 0);
+  _dbus_assert (((int)strlen (s)) == i);
+
+  dbus_free (s);
+
+  /* Check move */
+  
+  if (!_dbus_string_append (&str, "Hello World"))
+    _dbus_assert_not_reached ("could not append to string");
+
+  i = _dbus_string_get_length (&str);
+
+  if (!_dbus_string_init (&other))
+    _dbus_assert_not_reached ("could not init string");
+  
+  if (!_dbus_string_move (&str, 0, &other, 0))
+    _dbus_assert_not_reached ("could not move");
+
+  _dbus_assert (_dbus_string_get_length (&str) == 0);
+  _dbus_assert (_dbus_string_get_length (&other) == i);
+
+  if (!_dbus_string_append (&str, "Hello World"))
+    _dbus_assert_not_reached ("could not append to string");
+  
+  if (!_dbus_string_move (&str, 0, &other, _dbus_string_get_length (&other)))
+    _dbus_assert_not_reached ("could not move");
+
+  _dbus_assert (_dbus_string_get_length (&str) == 0);
+  _dbus_assert (_dbus_string_get_length (&other) == i * 2);
+
+    if (!_dbus_string_append (&str, "Hello World"))
+    _dbus_assert_not_reached ("could not append to string");
+  
+  if (!_dbus_string_move (&str, 0, &other, _dbus_string_get_length (&other) / 2))
+    _dbus_assert_not_reached ("could not move");
+
+  _dbus_assert (_dbus_string_get_length (&str) == 0);
+  _dbus_assert (_dbus_string_get_length (&other) == i * 3);
+  
+  _dbus_string_free (&other);
+
+  /* Check copy */
+  
+  if (!_dbus_string_append (&str, "Hello World"))
+    _dbus_assert_not_reached ("could not append to string");
+
+  i = _dbus_string_get_length (&str);
+  
+  if (!_dbus_string_init (&other))
+    _dbus_assert_not_reached ("could not init string");
+  
+  if (!_dbus_string_copy (&str, 0, &other, 0))
+    _dbus_assert_not_reached ("could not copy");
+
+  _dbus_assert (_dbus_string_get_length (&str) == i);
+  _dbus_assert (_dbus_string_get_length (&other) == i);
+
+  if (!_dbus_string_copy (&str, 0, &other, _dbus_string_get_length (&other)))
+    _dbus_assert_not_reached ("could not copy");
+
+  _dbus_assert (_dbus_string_get_length (&str) == i);
+  _dbus_assert (_dbus_string_get_length (&other) == i * 2);
+  _dbus_assert (_dbus_string_equal_c_str (&other,
+                                          "Hello WorldHello World"));
+
+  if (!_dbus_string_copy (&str, 0, &other, _dbus_string_get_length (&other) / 2))
+    _dbus_assert_not_reached ("could not copy");
+
+  _dbus_assert (_dbus_string_get_length (&str) == i);
+  _dbus_assert (_dbus_string_get_length (&other) == i * 3);
+  _dbus_assert (_dbus_string_equal_c_str (&other,
+                                          "Hello WorldHello WorldHello World"));
+  
+  _dbus_string_free (&str);
+  _dbus_string_free (&other);
+
+  /* Check replace */
+
+  if (!_dbus_string_init (&str))
+    _dbus_assert_not_reached ("failed to init string");
+  
+  if (!_dbus_string_append (&str, "Hello World"))
+    _dbus_assert_not_reached ("could not append to string");
+
+  i = _dbus_string_get_length (&str);
+  
+  if (!_dbus_string_init (&other))
+    _dbus_assert_not_reached ("could not init string");
+  
+  if (!_dbus_string_replace_len (&str, 0, _dbus_string_get_length (&str),
+                                 &other, 0, _dbus_string_get_length (&other)))
+    _dbus_assert_not_reached ("could not replace");
+
+  _dbus_assert (_dbus_string_get_length (&str) == i);
+  _dbus_assert (_dbus_string_get_length (&other) == i);
+  _dbus_assert (_dbus_string_equal_c_str (&other, "Hello World"));
+  
+  if (!_dbus_string_replace_len (&str, 0, _dbus_string_get_length (&str),
+                                 &other, 5, 1))
+    _dbus_assert_not_reached ("could not replace center space");
+
+  _dbus_assert (_dbus_string_get_length (&str) == i);
+  _dbus_assert (_dbus_string_get_length (&other) == i * 2 - 1);
+  _dbus_assert (_dbus_string_equal_c_str (&other,
+                                          "HelloHello WorldWorld"));
+
+  
+  if (!_dbus_string_replace_len (&str, 1, 1,
+                                 &other,
+                                 _dbus_string_get_length (&other) - 1,
+                                 1))
+    _dbus_assert_not_reached ("could not replace end character");
+  
+  _dbus_assert (_dbus_string_get_length (&str) == i);
+  _dbus_assert (_dbus_string_get_length (&other) == i * 2 - 1);
+  _dbus_assert (_dbus_string_equal_c_str (&other,
+                                          "HelloHello WorldWorle"));
+  
+  _dbus_string_free (&str);
+  _dbus_string_free (&other);
+  
+  /* Check append/get unichar */
+  
+  if (!_dbus_string_init (&str))
+    _dbus_assert_not_reached ("failed to init string");
+
+  ch = 0;
+  if (!_dbus_string_append_unichar (&str, 0xfffc))
+    _dbus_assert_not_reached ("failed to append unichar");
+
+  _dbus_string_get_unichar (&str, 0, &ch, &i);
+
+  _dbus_assert (ch == 0xfffc);
+  _dbus_assert (i == _dbus_string_get_length (&str));
+
+  _dbus_string_free (&str);
+
+  /* Check insert/set/get byte */
+  
+  if (!_dbus_string_init (&str))
+    _dbus_assert_not_reached ("failed to init string");
+
+  if (!_dbus_string_append (&str, "Hello"))
+    _dbus_assert_not_reached ("failed to append Hello");
+
+  _dbus_assert (_dbus_string_get_byte (&str, 0) == 'H');
+  _dbus_assert (_dbus_string_get_byte (&str, 1) == 'e');
+  _dbus_assert (_dbus_string_get_byte (&str, 2) == 'l');
+  _dbus_assert (_dbus_string_get_byte (&str, 3) == 'l');
+  _dbus_assert (_dbus_string_get_byte (&str, 4) == 'o');
+
+  _dbus_string_set_byte (&str, 1, 'q');
+  _dbus_assert (_dbus_string_get_byte (&str, 1) == 'q');
+
+  if (!_dbus_string_insert_bytes (&str, 0, 1, 255))
+    _dbus_assert_not_reached ("can't insert byte");
+
+  if (!_dbus_string_insert_bytes (&str, 2, 4, 'Z'))
+    _dbus_assert_not_reached ("can't insert byte");
+
+  if (!_dbus_string_insert_bytes (&str, _dbus_string_get_length (&str), 1, 'W'))
+    _dbus_assert_not_reached ("can't insert byte");
+  
+  _dbus_assert (_dbus_string_get_byte (&str, 0) == 255);
+  _dbus_assert (_dbus_string_get_byte (&str, 1) == 'H');
+  _dbus_assert (_dbus_string_get_byte (&str, 2) == 'Z');
+  _dbus_assert (_dbus_string_get_byte (&str, 3) == 'Z');
+  _dbus_assert (_dbus_string_get_byte (&str, 4) == 'Z');
+  _dbus_assert (_dbus_string_get_byte (&str, 5) == 'Z');
+  _dbus_assert (_dbus_string_get_byte (&str, 6) == 'q');
+  _dbus_assert (_dbus_string_get_byte (&str, 7) == 'l');
+  _dbus_assert (_dbus_string_get_byte (&str, 8) == 'l');
+  _dbus_assert (_dbus_string_get_byte (&str, 9) == 'o');
+  _dbus_assert (_dbus_string_get_byte (&str, 10) == 'W');
+
+  _dbus_string_free (&str);
+  
+  /* Check append/parse int/double */
+  
+  if (!_dbus_string_init (&str))
+    _dbus_assert_not_reached ("failed to init string");
+
+  if (!_dbus_string_append_int (&str, 27))
+    _dbus_assert_not_reached ("failed to append int");
+
+  i = _dbus_string_get_length (&str);
+
+  if (!_dbus_string_parse_int (&str, 0, &v, &end))
+    _dbus_assert_not_reached ("failed to parse int");
+
+  _dbus_assert (v == 27);
+  _dbus_assert (end == i);
+
+  _dbus_string_free (&str);
+  
+  if (!_dbus_string_init (&str))
+    _dbus_assert_not_reached ("failed to init string");
+  
+  if (!_dbus_string_append_double (&str, 50.3))
+    _dbus_assert_not_reached ("failed to append float");
+
+  i = _dbus_string_get_length (&str);
+
+  if (!_dbus_string_parse_double (&str, 0, &d, &end))
+    _dbus_assert_not_reached ("failed to parse float");
+
+  _dbus_assert (d > (50.3 - 1e-6) && d < (50.3 + 1e-6));
+  _dbus_assert (end == i);
+
+  _dbus_string_free (&str);
+
+  /* Test find */
+  if (!_dbus_string_init (&str))
+    _dbus_assert_not_reached ("failed to init string");
+
+  if (!_dbus_string_append (&str, "Hello"))
+    _dbus_assert_not_reached ("couldn't append to string");
+  
+  if (!_dbus_string_find (&str, 0, "He", &i))
+    _dbus_assert_not_reached ("didn't find 'He'");
+  _dbus_assert (i == 0);
+
+  if (!_dbus_string_find (&str, 0, "Hello", &i))
+    _dbus_assert_not_reached ("didn't find 'Hello'");
+  _dbus_assert (i == 0);
+  
+  if (!_dbus_string_find (&str, 0, "ello", &i))
+    _dbus_assert_not_reached ("didn't find 'ello'");
+  _dbus_assert (i == 1);
+
+  if (!_dbus_string_find (&str, 0, "lo", &i))
+    _dbus_assert_not_reached ("didn't find 'lo'");
+  _dbus_assert (i == 3);
+
+  if (!_dbus_string_find (&str, 2, "lo", &i))
+    _dbus_assert_not_reached ("didn't find 'lo'");
+  _dbus_assert (i == 3);
+
+  if (_dbus_string_find (&str, 4, "lo", &i))
+    _dbus_assert_not_reached ("did find 'lo'");
+  
+  if (!_dbus_string_find (&str, 0, "l", &i))
+    _dbus_assert_not_reached ("didn't find 'l'");
+  _dbus_assert (i == 2);
+
+  if (!_dbus_string_find (&str, 0, "H", &i))
+    _dbus_assert_not_reached ("didn't find 'H'");
+  _dbus_assert (i == 0);
+
+  if (!_dbus_string_find (&str, 0, "", &i))
+    _dbus_assert_not_reached ("didn't find ''");
+  _dbus_assert (i == 0);
+  
+  if (_dbus_string_find (&str, 0, "Hello!", NULL))
+    _dbus_assert_not_reached ("Did find 'Hello!'");
+
+  if (_dbus_string_find (&str, 0, "Oh, Hello", NULL))
+    _dbus_assert_not_reached ("Did find 'Oh, Hello'");
+  
+  if (_dbus_string_find (&str, 0, "ill", NULL))
+    _dbus_assert_not_reached ("Did find 'ill'");
+
+  if (_dbus_string_find (&str, 0, "q", NULL))
+    _dbus_assert_not_reached ("Did find 'q'");
+
+  if (!_dbus_string_find_to (&str, 0, 2, "He", NULL))
+    _dbus_assert_not_reached ("Didn't find 'He'");
+
+  if (_dbus_string_find_to (&str, 0, 2, "Hello", NULL))
+    _dbus_assert_not_reached ("Did find 'Hello'");
+
+  if (!_dbus_string_find_byte_backward (&str, _dbus_string_get_length (&str), 'H', &i))
+    _dbus_assert_not_reached ("Did not find 'H'");
+  _dbus_assert (i == 0);
+
+  if (!_dbus_string_find_byte_backward (&str, _dbus_string_get_length (&str), 'o', &i))
+    _dbus_assert_not_reached ("Did not find 'o'");
+  _dbus_assert (i == _dbus_string_get_length (&str) - 1);
+
+  if (_dbus_string_find_byte_backward (&str, _dbus_string_get_length (&str) - 1, 'o', &i))
+    _dbus_assert_not_reached ("Did find 'o'");
+  _dbus_assert (i == -1);
+
+  if (_dbus_string_find_byte_backward (&str, 1, 'e', &i))
+    _dbus_assert_not_reached ("Did find 'e'");
+  _dbus_assert (i == -1);
+
+  if (!_dbus_string_find_byte_backward (&str, 2, 'e', &i))
+    _dbus_assert_not_reached ("Didn't find 'e'");
+  _dbus_assert (i == 1);
+  
+  _dbus_string_free (&str);
+
+  /* Hex encoding */
+  _dbus_string_init_const (&str, "cafebabe, this is a bogus hex string");
+  if (!_dbus_string_init (&other))
+    _dbus_assert_not_reached ("could not init string");
+
+  if (!_dbus_string_hex_decode (&str, 0, &end, &other, 0))
+    _dbus_assert_not_reached ("deccoded bogus hex string with no error");
+
+  _dbus_assert (end == 8);
+
+  _dbus_string_free (&other);
+
+  test_roundtrips (test_hex_roundtrip);
+  
+  _dbus_string_free (&str);
+
+  {                                                                                           
+    int found, found_len;  
+
+    _dbus_string_init_const (&str, "012\r\n567\n90");
+    
+    if (!_dbus_string_find_eol (&str, 0, &found, &found_len) || found != 3 || found_len != 2)
+      _dbus_assert_not_reached ("Did not find '\\r\\n'");                                       
+    if (found != 3 || found_len != 2)                                                           
+      _dbus_assert_not_reached ("invalid return values");                                       
+    
+    if (!_dbus_string_find_eol (&str, 5, &found, &found_len))                                    
+      _dbus_assert_not_reached ("Did not find '\\n'");                                          
+    if (found != 8 || found_len != 1)                                                           
+      _dbus_assert_not_reached ("invalid return values");                                       
+    
+    if (_dbus_string_find_eol (&str, 9, &found, &found_len))                                     
+      _dbus_assert_not_reached ("Found not expected '\\n'");                                    
+    else if (found != 11 || found_len != 0)                                                     
+      _dbus_assert_not_reached ("invalid return values '\\n'");                                 
+
+    found = -1;
+    found_len = -1;
+    _dbus_string_init_const (&str, "");
+    if (_dbus_string_find_eol (&str, 0, &found, &found_len))
+      _dbus_assert_not_reached ("found an eol in an empty string");
+    _dbus_assert (found == 0);
+    _dbus_assert (found_len == 0);
+    
+    found = -1;
+    found_len = -1;
+    _dbus_string_init_const (&str, "foobar");
+    if (_dbus_string_find_eol (&str, 0, &found, &found_len))
+      _dbus_assert_not_reached ("found eol in string that lacks one");
+    _dbus_assert (found == 6);
+    _dbus_assert (found_len == 0);
+
+    found = -1;
+    found_len = -1;
+    _dbus_string_init_const (&str, "foobar\n");
+    if (!_dbus_string_find_eol (&str, 0, &found, &found_len))
+      _dbus_assert_not_reached ("did not find eol in string that has one at end");
+    _dbus_assert (found == 6);
+    _dbus_assert (found_len == 1);
+  }
+
+  {
+    DBusString line;
+
+#define FIRST_LINE "this is a line"
+#define SECOND_LINE "this is a second line"
+    /* third line is empty */
+#define THIRD_LINE ""
+#define FOURTH_LINE "this is a fourth line"
+    
+    if (!_dbus_string_init (&str))
+      _dbus_assert_not_reached ("no memory");
+
+    if (!_dbus_string_append (&str, FIRST_LINE "\n" SECOND_LINE "\r\n" THIRD_LINE "\n" FOURTH_LINE))
+      _dbus_assert_not_reached ("no memory");
+    
+    if (!_dbus_string_init (&line))
+      _dbus_assert_not_reached ("no memory");
+    
+    if (!_dbus_string_pop_line (&str, &line))
+      _dbus_assert_not_reached ("failed to pop first line");
+
+    _dbus_assert (_dbus_string_equal_c_str (&line, FIRST_LINE));
+    
+    if (!_dbus_string_pop_line (&str, &line))
+      _dbus_assert_not_reached ("failed to pop second line");
+
+    _dbus_assert (_dbus_string_equal_c_str (&line, SECOND_LINE));
+    
+    if (!_dbus_string_pop_line (&str, &line))
+      _dbus_assert_not_reached ("failed to pop third line");
+
+    _dbus_assert (_dbus_string_equal_c_str (&line, THIRD_LINE));
+    
+    if (!_dbus_string_pop_line (&str, &line))
+      _dbus_assert_not_reached ("failed to pop fourth line");
+
+    _dbus_assert (_dbus_string_equal_c_str (&line, FOURTH_LINE));
+    
+    _dbus_string_free (&str);
+    _dbus_string_free (&line);
+  }
+
+  {
+    if (!_dbus_string_init (&str))
+      _dbus_assert_not_reached ("no memory");
+
+    for (i = 0; i < 10000; i++)
+      if (!_dbus_string_append (&str, "abcdefghijklmnopqrstuvwxyz"))
+        _dbus_assert_not_reached ("no memory");
+
+    if (!_dbus_string_set_length (&str, 10))
+      _dbus_assert_not_reached ("failed to set length");
+
+    /* actually compact */
+    if (!_dbus_string_compact (&str, 2048))
+      _dbus_assert_not_reached ("failed to compact after set_length");
+
+    /* peek inside to make sure it worked */
+    if (((DBusRealString *)&str)->allocated > 30)
+      _dbus_assert_not_reached ("compacting string didn't do anything");
+
+    if (!_dbus_string_equal_c_str (&str, "abcdefghij"))
+      _dbus_assert_not_reached ("unexpected content after compact");
+
+    /* compact nothing */
+    if (!_dbus_string_compact (&str, 2048))
+      _dbus_assert_not_reached ("failed to compact 2nd time");
+
+    if (!_dbus_string_equal_c_str (&str, "abcdefghij"))
+      _dbus_assert_not_reached ("unexpected content after 2nd compact");
+
+    /* and make sure it still works...*/
+    if (!_dbus_string_append (&str, "123456"))
+      _dbus_assert_not_reached ("failed to append after compact");
+
+    if (!_dbus_string_equal_c_str (&str, "abcdefghij123456"))
+      _dbus_assert_not_reached ("unexpected content after append");
+
+    /* after growing automatically, this should do nothing */
+    if (!_dbus_string_compact (&str, 20000))
+      _dbus_assert_not_reached ("failed to compact after grow");
+
+    /* but this one will do something */
+    if (!_dbus_string_compact (&str, 0))
+      _dbus_assert_not_reached ("failed to compact after grow");
+
+    if (!_dbus_string_equal_c_str (&str, "abcdefghij123456"))
+      _dbus_assert_not_reached ("unexpected content");
+
+    if (!_dbus_string_append (&str, "!@#$%"))
+      _dbus_assert_not_reached ("failed to append after compact");
+
+    if (!_dbus_string_equal_c_str (&str, "abcdefghij123456!@#$%"))
+      _dbus_assert_not_reached ("unexpected content");
+
+    _dbus_string_free (&str);
+  }
+
+  {
+    const char two_strings[] = "one\ttwo";
+
+    if (!_dbus_string_init (&str))
+      _dbus_assert_not_reached ("no memory");
+
+    if (!_dbus_string_init (&other))
+      _dbus_assert_not_reached ("no memory");
+
+    if (!_dbus_string_append (&str, two_strings))
+      _dbus_assert_not_reached ("no memory");
+
+    if (!_dbus_string_split_on_byte (&str, '\t', &other))
+      _dbus_assert_not_reached ("no memory or delimiter not found");
+
+    if (strcmp (_dbus_string_get_data (&str), "one") != 0)
+      _dbus_assert_not_reached ("left side after split on tab is wrong");
+
+    if (strcmp (_dbus_string_get_data (&other), "two") != 0)
+      _dbus_assert_not_reached ("right side after split on tab is wrong");
+
+    _dbus_string_free (&str);
+    _dbus_string_free (&other);
+  }
+  
+  return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-string.c b/src/dbus/dbus-string.c
new file mode 100644 (file)
index 0000000..6b9b2bf
--- /dev/null
@@ -0,0 +1,2907 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-string.c String utility class (internal to D-Bus implementation)
+ * 
+ * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc.
+ * Copyright (C) 2006 Ralf Habacker <ralf.habacker@freenet.de>
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-internals.h"
+#include "dbus-string.h"
+/* we allow a system header here, for speed/convenience */
+#include <string.h>
+/* for vsnprintf */
+#include <stdio.h>
+#define DBUS_CAN_USE_DBUS_STRING_PRIVATE 1
+#include "dbus-string-private.h"
+#include "dbus-marshal-basic.h" /* probably should be removed by moving the usage of DBUS_TYPE
+                                 * into the marshaling-related files
+                                 */
+/* for DBUS_VA_COPY */
+#include "dbus-sysdeps.h"
+
+/**
+ * @defgroup DBusString DBusString class
+ * @ingroup  DBusInternals
+ * @brief DBusString data structure for safer string handling
+ *
+ * Types and functions related to DBusString. DBusString is intended
+ * to be a string class that makes it hard to mess up security issues
+ * (and just in general harder to write buggy code).  It should be
+ * used (or extended and then used) rather than the libc stuff in
+ * string.h.  The string class is a bit inconvenient at spots because
+ * it handles out-of-memory failures and tries to be extra-robust.
+ * 
+ * A DBusString has a maximum length set at initialization time; this
+ * can be used to ensure that a buffer doesn't get too big.  The
+ * _dbus_string_lengthen() method checks for overflow, and for max
+ * length being exceeded.
+ * 
+ * Try to avoid conversion to a plain C string, i.e. add methods on
+ * the string object instead, only convert to C string when passing
+ * things out to the public API. In particular, no sprintf, strcpy,
+ * strcat, any of that should be used. The GString feature of
+ * accepting negative numbers for "length of string" is also absent,
+ * because it could keep us from detecting bogus huge lengths. i.e. if
+ * we passed in some bogus huge length it would be taken to mean
+ * "current length of string" instead of "broken crack"
+ *
+ * @todo #DBusString needs a lot of cleaning up; some of the
+ * API is no longer used, and the API is pretty inconsistent.
+ * In particular all the "append" APIs, especially those involving
+ * alignment but probably lots of them, are no longer used by the
+ * marshaling code which always does "inserts" now.
+ */
+
+/**
+ * @addtogroup DBusString
+ * @{
+ */
+
+static void
+fixup_alignment (DBusRealString *real)
+{
+  unsigned char *aligned;
+  unsigned char *real_block;
+  unsigned int old_align_offset;
+
+  /* we have to have extra space in real->allocated for the align offset and nul byte */
+  _dbus_assert (real->len <= real->allocated - _DBUS_STRING_ALLOCATION_PADDING);
+  
+  old_align_offset = real->align_offset;
+  real_block = real->str - old_align_offset;
+  
+  aligned = _DBUS_ALIGN_ADDRESS (real_block, 8);
+
+  real->align_offset = aligned - real_block;
+  real->str = aligned;
+  
+  if (old_align_offset != real->align_offset)
+    {
+      /* Here comes the suck */
+      memmove (real_block + real->align_offset,
+               real_block + old_align_offset,
+               real->len + 1);
+    }
+
+  _dbus_assert (real->align_offset < 8);
+  _dbus_assert (_DBUS_ALIGN_ADDRESS (real->str, 8) == real->str);
+}
+
+static void
+undo_alignment (DBusRealString *real)
+{
+  if (real->align_offset != 0)
+    {
+      memmove (real->str - real->align_offset,
+               real->str,
+               real->len + 1);
+
+      real->str = real->str - real->align_offset;
+      real->align_offset = 0;
+    }
+}
+
+/**
+ * Initializes a string that can be up to the given allocation size
+ * before it has to realloc. The string starts life with zero length.
+ * The string must eventually be freed with _dbus_string_free().
+ * 
+ * @param str memory to hold the string
+ * @param allocate_size amount to preallocate
+ * @returns #TRUE on success, #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_init_preallocated (DBusString *str,
+                                int         allocate_size)
+{
+  DBusRealString *real;
+  
+  _dbus_assert (str != NULL);
+
+  _dbus_assert (sizeof (DBusString) == sizeof (DBusRealString));
+  
+  real = (DBusRealString*) str;
+
+  /* It's very important not to touch anything
+   * other than real->str if we're going to fail,
+   * since we also use this function to reset
+   * an existing string, e.g. in _dbus_string_steal_data()
+   */
+  
+  real->str = dbus_malloc (_DBUS_STRING_ALLOCATION_PADDING + allocate_size);
+  if (real->str == NULL)
+    return FALSE;  
+  
+  real->allocated = _DBUS_STRING_ALLOCATION_PADDING + allocate_size;
+  real->len = 0;
+  real->str[real->len] = '\0';
+  
+  real->max_length = _DBUS_STRING_MAX_MAX_LENGTH;
+  real->constant = FALSE;
+  real->locked = FALSE;
+  real->invalid = FALSE;
+  real->align_offset = 0;
+  
+  fixup_alignment (real);
+  
+  return TRUE;
+}
+
+/**
+ * Initializes a string. The string starts life with zero length.  The
+ * string must eventually be freed with _dbus_string_free().
+ * 
+ * @param str memory to hold the string
+ * @returns #TRUE on success, #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_init (DBusString *str)
+{
+  return _dbus_string_init_preallocated (str, 0);
+}
+
+#ifdef DBUS_BUILD_TESTS
+/* The max length thing is sort of a historical artifact
+ * from a feature that turned out to be dumb; perhaps
+ * we should purge it entirely. The problem with
+ * the feature is that it looks like memory allocation
+ * failure, but is not a transient or resolvable failure.
+ */
+static void
+set_max_length (DBusString *str,
+                int         max_length)
+{
+  DBusRealString *real;
+  
+  real = (DBusRealString*) str;
+
+  real->max_length = max_length;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * Initializes a constant string. The value parameter is not copied
+ * (should be static), and the string may never be modified.
+ * It is safe but not necessary to call _dbus_string_free()
+ * on a const string. The string has a length limit of MAXINT - 8.
+ * 
+ * @param str memory to use for the string
+ * @param value a string to be stored in str (not copied!!!)
+ */
+void
+_dbus_string_init_const (DBusString *str,
+                         const char *value)
+{
+  _dbus_assert (value != NULL);
+  
+  _dbus_string_init_const_len (str, value,
+                               strlen (value));
+}
+
+/**
+ * Initializes a constant string with a length. The value parameter is
+ * not copied (should be static), and the string may never be
+ * modified.  It is safe but not necessary to call _dbus_string_free()
+ * on a const string.
+ * 
+ * @param str memory to use for the string
+ * @param value a string to be stored in str (not copied!!!)
+ * @param len the length to use
+ */
+void
+_dbus_string_init_const_len (DBusString *str,
+                             const char *value,
+                             int         len)
+{
+  DBusRealString *real;
+  
+  _dbus_assert (str != NULL);
+  _dbus_assert (len == 0 || value != NULL);
+  _dbus_assert (len <= _DBUS_STRING_MAX_MAX_LENGTH);
+  _dbus_assert (len >= 0);
+  
+  real = (DBusRealString*) str;
+  
+  real->str = (unsigned char*) value;
+  real->len = len;
+  real->allocated = real->len + _DBUS_STRING_ALLOCATION_PADDING; /* a lie, just to avoid special-case assertions... */
+  real->max_length = real->len + 1;
+  real->constant = TRUE;
+  real->locked = TRUE;
+  real->invalid = FALSE;
+  real->align_offset = 0;
+
+  /* We don't require const strings to be 8-byte aligned as the
+   * memory is coming from elsewhere.
+   */
+}
+
+/**
+ * Frees a string created by _dbus_string_init().
+ *
+ * @param str memory where the string is stored.
+ */
+void
+_dbus_string_free (DBusString *str)
+{
+  DBusRealString *real = (DBusRealString*) str;
+  DBUS_GENERIC_STRING_PREAMBLE (real);
+  
+  if (real->constant)
+    return;
+  dbus_free (real->str - real->align_offset);
+
+  real->invalid = TRUE;
+}
+
+static dbus_bool_t
+compact (DBusRealString *real,
+         int             max_waste)
+{
+  unsigned char *new_str;
+  int new_allocated;
+  int waste;
+
+  waste = real->allocated - (real->len + _DBUS_STRING_ALLOCATION_PADDING);
+
+  if (waste <= max_waste)
+    return TRUE;
+
+  new_allocated = real->len + _DBUS_STRING_ALLOCATION_PADDING;
+
+  new_str = dbus_realloc (real->str - real->align_offset, new_allocated);
+  if (_DBUS_UNLIKELY (new_str == NULL))
+    return FALSE;
+
+  real->str = new_str + real->align_offset;
+  real->allocated = new_allocated;
+  fixup_alignment (real);
+
+  return TRUE;
+}
+
+#ifdef DBUS_BUILD_TESTS
+/* Not using this feature at the moment,
+ * so marked DBUS_BUILD_TESTS-only
+ */
+/**
+ * Locks a string such that any attempts to change the string will
+ * result in aborting the program. Also, if the string is wasting a
+ * lot of memory (allocation is sufficiently larger than what the
+ * string is really using), _dbus_string_lock() will realloc the
+ * string's data to "compact" it.
+ *
+ * @param str the string to lock.
+ */
+void
+_dbus_string_lock (DBusString *str)
+{  
+  DBUS_LOCKED_STRING_PREAMBLE (str); /* can lock multiple times */
+
+  real->locked = TRUE;
+
+  /* Try to realloc to avoid excess memory usage, since
+   * we know we won't change the string further
+   */
+#define MAX_WASTE 48
+  compact (real, MAX_WASTE);
+}
+#endif /* DBUS_BUILD_TESTS */
+
+static dbus_bool_t
+reallocate_for_length (DBusRealString *real,
+                       int             new_length)
+{
+  int new_allocated;
+  unsigned char *new_str;
+
+  /* at least double our old allocation to avoid O(n), avoiding
+   * overflow
+   */
+  if (real->allocated > (_DBUS_STRING_MAX_MAX_LENGTH + _DBUS_STRING_ALLOCATION_PADDING) / 2)
+    new_allocated = _DBUS_STRING_MAX_MAX_LENGTH + _DBUS_STRING_ALLOCATION_PADDING;
+  else
+    new_allocated = real->allocated * 2;
+
+  /* if you change the code just above here, run the tests without
+   * the following assert-only hack before you commit
+   */
+  /* This is keyed off asserts in addition to tests so when you
+   * disable asserts to profile, you don't get this destroyer
+   * of profiles.
+   */
+#ifdef DBUS_DISABLE_ASSERT
+#else
+#ifdef DBUS_BUILD_TESTS
+  new_allocated = 0; /* ensure a realloc every time so that we go
+                      * through all malloc failure codepaths
+                      */
+#endif /* DBUS_BUILD_TESTS */
+#endif /* !DBUS_DISABLE_ASSERT */
+
+  /* But be sure we always alloc at least space for the new length */
+  new_allocated = MAX (new_allocated,
+                       new_length + _DBUS_STRING_ALLOCATION_PADDING);
+
+  _dbus_assert (new_allocated >= real->allocated); /* code relies on this */
+  new_str = dbus_realloc (real->str - real->align_offset, new_allocated);
+  if (_DBUS_UNLIKELY (new_str == NULL))
+    return FALSE;
+
+  real->str = new_str + real->align_offset;
+  real->allocated = new_allocated;
+  fixup_alignment (real);
+
+  return TRUE;
+}
+
+/**
+ * Compacts the string to avoid wasted memory.  Wasted memory is
+ * memory that is allocated but not actually required to store the
+ * current length of the string.  The compact is only done if more
+ * than the given amount of memory is being wasted (otherwise the
+ * waste is ignored and the call does nothing).
+ *
+ * @param str the string
+ * @param max_waste the maximum amount of waste to ignore
+ * @returns #FALSE if the compact failed due to realloc failure
+ */
+dbus_bool_t
+_dbus_string_compact (DBusString *str,
+                      int         max_waste)
+{
+  DBUS_STRING_PREAMBLE (str);
+
+  return compact (real, max_waste);
+}
+
+static dbus_bool_t
+set_length (DBusRealString *real,
+            int             new_length)
+{
+  /* Note, we are setting the length not including nul termination */
+
+  /* exceeding max length is the same as failure to allocate memory */
+  if (_DBUS_UNLIKELY (new_length > real->max_length))
+    return FALSE;
+  else if (new_length > (real->allocated - _DBUS_STRING_ALLOCATION_PADDING) &&
+           _DBUS_UNLIKELY (!reallocate_for_length (real, new_length)))
+    return FALSE;
+  else
+    {
+      real->len = new_length;
+      real->str[new_length] = '\0';
+      return TRUE;
+    }
+}
+
+static dbus_bool_t
+open_gap (int             len,
+          DBusRealString *dest,
+          int             insert_at)
+{
+  if (len == 0)
+    return TRUE;
+
+  if (len > dest->max_length - dest->len)
+    return FALSE; /* detected overflow of dest->len + len below */
+  
+  if (!set_length (dest, dest->len + len))
+    return FALSE;
+
+  memmove (dest->str + insert_at + len, 
+           dest->str + insert_at,
+           dest->len - len - insert_at);
+
+  return TRUE;
+}
+
+#ifndef _dbus_string_get_data
+/**
+ * Gets the raw character buffer from the string.  The returned buffer
+ * will be nul-terminated, but note that strings may contain binary
+ * data so there may be extra nul characters prior to the termination.
+ * This function should be little-used, extend DBusString or add
+ * stuff to dbus-sysdeps.c instead. It's an error to use this
+ * function on a const string.
+ *
+ * @param str the string
+ * @returns the data
+ */
+char*
+_dbus_string_get_data (DBusString *str)
+{
+  DBUS_STRING_PREAMBLE (str);
+  
+  return (char*) real->str;
+}
+#endif /* _dbus_string_get_data */
+
+/* only do the function if we don't have the macro */
+#ifndef _dbus_string_get_const_data
+/**
+ * Gets the raw character buffer from a const string.
+ *
+ * @param str the string
+ * @returns the string data
+ */
+const char*
+_dbus_string_get_const_data (const DBusString  *str)
+{
+  DBUS_CONST_STRING_PREAMBLE (str);
+  
+  return (const char*) real->str;
+}
+#endif /* _dbus_string_get_const_data */
+
+/**
+ * Gets a sub-portion of the raw character buffer from the
+ * string. The "len" field is required simply for error
+ * checking, to be sure you don't try to use more
+ * string than exists. The nul termination of the
+ * returned buffer remains at the end of the entire
+ * string, not at start + len.
+ *
+ * @param str the string
+ * @param start byte offset to return
+ * @param len length of segment to return
+ * @returns the string data
+ */
+char*
+_dbus_string_get_data_len (DBusString *str,
+                           int         start,
+                           int         len)
+{
+  DBUS_STRING_PREAMBLE (str);
+  _dbus_assert (start >= 0);
+  _dbus_assert (len >= 0);
+  _dbus_assert (start <= real->len);
+  _dbus_assert (len <= real->len - start);
+  
+  return (char*) real->str + start;
+}
+
+/* only do the function if we don't have the macro */
+#ifndef _dbus_string_get_const_data_len
+/**
+ * const version of _dbus_string_get_data_len().
+ *
+ * @param str the string
+ * @param start byte offset to return
+ * @param len length of segment to return
+ * @returns the string data
+ */
+const char*
+_dbus_string_get_const_data_len (const DBusString  *str,
+                                 int                start,
+                                 int                len)
+{
+  DBUS_CONST_STRING_PREAMBLE (str);
+  _dbus_assert (start >= 0);
+  _dbus_assert (len >= 0);
+  _dbus_assert (start <= real->len);
+  _dbus_assert (len <= real->len - start);
+  
+  return (const char*) real->str + start;
+}
+#endif /* _dbus_string_get_const_data_len */
+
+/* only do the function if we don't have the macro */
+#ifndef _dbus_string_set_byte
+/**
+ * Sets the value of the byte at the given position.
+ *
+ * @param str the string
+ * @param i the position
+ * @param byte the new value
+ */
+void
+_dbus_string_set_byte (DBusString    *str,
+                       int            i,
+                       unsigned char  byte)
+{
+  DBUS_STRING_PREAMBLE (str);
+  _dbus_assert (i < real->len);
+  _dbus_assert (i >= 0);
+  
+  real->str[i] = byte;
+}
+#endif /* _dbus_string_set_byte */
+
+/* only have the function if we didn't create a macro */
+#ifndef _dbus_string_get_byte
+/**
+ * Gets the byte at the given position. It is
+ * allowed to ask for the nul byte at the end of
+ * the string.
+ *
+ * @param str the string
+ * @param start the position
+ * @returns the byte at that position
+ */
+unsigned char
+_dbus_string_get_byte (const DBusString  *str,
+                       int                start)
+{
+  DBUS_CONST_STRING_PREAMBLE (str);
+  _dbus_assert (start <= real->len);
+  _dbus_assert (start >= 0);
+  
+  return real->str[start];
+}
+#endif /* _dbus_string_get_byte */
+
+/**
+ * Inserts a number of bytes of a given value at the
+ * given position.
+ *
+ * @param str the string
+ * @param i the position
+ * @param n_bytes number of bytes
+ * @param byte the value to insert
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_string_insert_bytes (DBusString   *str,
+                          int           i,
+                          int           n_bytes,
+                          unsigned char byte)
+{
+  DBUS_STRING_PREAMBLE (str);
+  _dbus_assert (i <= real->len);
+  _dbus_assert (i >= 0);
+  _dbus_assert (n_bytes >= 0);
+
+  if (n_bytes == 0)
+    return TRUE;
+  
+  if (!open_gap (n_bytes, real, i))
+    return FALSE;
+  
+  memset (real->str + i, byte, n_bytes);
+
+  return TRUE;
+}
+
+/**
+ * Inserts a single byte at the given position.
+ *
+ * @param str the string
+ * @param i the position
+ * @param byte the value to insert
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_string_insert_byte (DBusString   *str,
+                          int           i,
+                          unsigned char byte)
+{
+  DBUS_STRING_PREAMBLE (str);
+  _dbus_assert (i <= real->len);
+  _dbus_assert (i >= 0);
+  
+  if (!open_gap (1, real, i))
+    return FALSE;
+
+  real->str[i] = byte;
+
+  return TRUE;
+}
+
+/**
+ * Like _dbus_string_get_data(), but removes the
+ * gotten data from the original string. The caller
+ * must free the data returned. This function may
+ * fail due to lack of memory, and return #FALSE.
+ *
+ * @param str the string
+ * @param data_return location to return the buffer
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_string_steal_data (DBusString        *str,
+                         char             **data_return)
+{
+  int old_max_length;
+  DBUS_STRING_PREAMBLE (str);
+  _dbus_assert (data_return != NULL);
+
+  undo_alignment (real);
+  
+  *data_return = (char*) real->str;
+
+  old_max_length = real->max_length;
+  
+  /* reset the string */
+  if (!_dbus_string_init (str))
+    {
+      /* hrm, put it back then */
+      real->str = (unsigned char*) *data_return;
+      *data_return = NULL;
+      fixup_alignment (real);
+      return FALSE;
+    }
+
+  real->max_length = old_max_length;
+
+  return TRUE;
+}
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Like _dbus_string_get_data_len(), but removes the gotten data from
+ * the original string. The caller must free the data returned. This
+ * function may fail due to lack of memory, and return #FALSE.
+ * The returned string is nul-terminated and has length len.
+ *
+ * @todo this function is broken because on failure it
+ * may corrupt the source string.
+ * 
+ * @param str the string
+ * @param data_return location to return the buffer
+ * @param start the start of segment to steal
+ * @param len the length of segment to steal
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_string_steal_data_len (DBusString        *str,
+                             char             **data_return,
+                             int                start,
+                             int                len)
+{
+  DBusString dest;
+  DBUS_STRING_PREAMBLE (str);
+  _dbus_assert (data_return != NULL);
+  _dbus_assert (start >= 0);
+  _dbus_assert (len >= 0);
+  _dbus_assert (start <= real->len);
+  _dbus_assert (len <= real->len - start);
+
+  if (!_dbus_string_init (&dest))
+    return FALSE;
+
+  set_max_length (&dest, real->max_length);
+  
+  if (!_dbus_string_move_len (str, start, len, &dest, 0))
+    {
+      _dbus_string_free (&dest);
+      return FALSE;
+    }
+
+  _dbus_warn ("Broken code in _dbus_string_steal_data_len(), see @todo, FIXME\n");
+  if (!_dbus_string_steal_data (&dest, data_return))
+    {
+      _dbus_string_free (&dest);
+      return FALSE;
+    }
+
+  _dbus_string_free (&dest);
+  return TRUE;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * Copies the data from the string into a char*
+ *
+ * @param str the string
+ * @param data_return place to return the data
+ * @returns #TRUE on success, #FALSE on no memory
+ */
+dbus_bool_t
+_dbus_string_copy_data (const DBusString  *str,
+                        char             **data_return)
+{
+  DBUS_CONST_STRING_PREAMBLE (str);
+  _dbus_assert (data_return != NULL);
+  
+  *data_return = dbus_malloc (real->len + 1);
+  if (*data_return == NULL)
+    return FALSE;
+
+  memcpy (*data_return, real->str, real->len + 1);
+
+  return TRUE;
+}
+
+/**
+ * Copies the contents of a DBusString into a different buffer. It is
+ * a bug if avail_len is too short to hold the string contents. nul
+ * termination is not copied, just the supplied bytes.
+ * 
+ * @param str a string
+ * @param buffer a C buffer to copy data to
+ * @param avail_len maximum length of C buffer
+ */
+void
+_dbus_string_copy_to_buffer (const DBusString  *str,
+                            char              *buffer,
+                            int                avail_len)
+{
+  DBUS_CONST_STRING_PREAMBLE (str);
+
+  _dbus_assert (avail_len >= 0);
+  _dbus_assert (avail_len >= real->len);
+  
+  memcpy (buffer, real->str, real->len);
+}
+
+/**
+ * Copies the contents of a DBusString into a different buffer. It is
+ * a bug if avail_len is too short to hold the string contents plus a
+ * nul byte. 
+ * 
+ * @param str a string
+ * @param buffer a C buffer to copy data to
+ * @param avail_len maximum length of C buffer
+ */
+void
+_dbus_string_copy_to_buffer_with_nul (const DBusString  *str,
+                                      char              *buffer,
+                                      int                avail_len)
+{
+  DBUS_CONST_STRING_PREAMBLE (str);
+
+  _dbus_assert (avail_len >= 0);
+  _dbus_assert (avail_len > real->len);
+  
+  memcpy (buffer, real->str, real->len+1);
+}
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Copies a segment of the string into a char*
+ *
+ * @param str the string
+ * @param data_return place to return the data
+ * @param start start index
+ * @param len length to copy
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_copy_data_len (const DBusString  *str,
+                            char             **data_return,
+                            int                start,
+                            int                len)
+{
+  DBusString dest;
+
+  DBUS_CONST_STRING_PREAMBLE (str);
+  _dbus_assert (data_return != NULL);
+  _dbus_assert (start >= 0);
+  _dbus_assert (len >= 0);
+  _dbus_assert (start <= real->len);
+  _dbus_assert (len <= real->len - start);
+
+  if (!_dbus_string_init (&dest))
+    return FALSE;
+
+  set_max_length (&dest, real->max_length);
+
+  if (!_dbus_string_copy_len (str, start, len, &dest, 0))
+    {
+      _dbus_string_free (&dest);
+      return FALSE;
+    }
+
+  if (!_dbus_string_steal_data (&dest, data_return))
+    {
+      _dbus_string_free (&dest);
+      return FALSE;
+    }
+
+  _dbus_string_free (&dest);
+  return TRUE;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/* Only have the function if we don't have the macro */
+#ifndef _dbus_string_get_length
+/**
+ * Gets the length of a string (not including nul termination).
+ *
+ * @returns the length.
+ */
+int
+_dbus_string_get_length (const DBusString  *str)
+{
+  DBUS_CONST_STRING_PREAMBLE (str);
+  
+  return real->len;
+}
+#endif /* !_dbus_string_get_length */
+
+/**
+ * Makes a string longer by the given number of bytes.  Checks whether
+ * adding additional_length to the current length would overflow an
+ * integer, and checks for exceeding a string's max length.
+ * The new bytes are not initialized, other than nul-terminating
+ * the end of the string. The uninitialized bytes may contain
+ * nul bytes or other junk.
+ *
+ * @param str a string
+ * @param additional_length length to add to the string.
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_string_lengthen (DBusString *str,
+                       int         additional_length)
+{
+  DBUS_STRING_PREAMBLE (str);  
+  _dbus_assert (additional_length >= 0);
+
+  if (_DBUS_UNLIKELY (additional_length > real->max_length - real->len))
+    return FALSE; /* would overflow */
+  
+  return set_length (real,
+                     real->len + additional_length);
+}
+
+/**
+ * Makes a string shorter by the given number of bytes.
+ *
+ * @param str a string
+ * @param length_to_remove length to remove from the string.
+ */
+void
+_dbus_string_shorten (DBusString *str,
+                      int         length_to_remove)
+{
+  DBUS_STRING_PREAMBLE (str);
+  _dbus_assert (length_to_remove >= 0);
+  _dbus_assert (length_to_remove <= real->len);
+
+  set_length (real,
+              real->len - length_to_remove);
+}
+
+/**
+ * Sets the length of a string. Can be used to truncate or lengthen
+ * the string. If the string is lengthened, the function may fail and
+ * return #FALSE. Newly-added bytes are not initialized, as with
+ * _dbus_string_lengthen().
+ *
+ * @param str a string
+ * @param length new length of the string.
+ * @returns #FALSE on failure.
+ */
+dbus_bool_t
+_dbus_string_set_length (DBusString *str,
+                         int         length)
+{
+  DBUS_STRING_PREAMBLE (str);
+  _dbus_assert (length >= 0);
+
+  return set_length (real, length);
+}
+
+static dbus_bool_t
+align_insert_point_then_open_gap (DBusString *str,
+                                  int        *insert_at_p,
+                                  int         alignment,
+                                  int         gap_size)
+{
+  unsigned long new_len; /* ulong to avoid _DBUS_ALIGN_VALUE overflow */
+  unsigned long gap_pos;
+  int insert_at;
+  int delta;
+  DBUS_STRING_PREAMBLE (str);
+  _dbus_assert (alignment >= 1);
+  _dbus_assert (alignment <= 8); /* it has to be a bug if > 8 */
+
+  insert_at = *insert_at_p;
+
+  _dbus_assert (insert_at <= real->len);
+  
+  gap_pos = _DBUS_ALIGN_VALUE (insert_at, alignment);
+  new_len = real->len + (gap_pos - insert_at) + gap_size;
+  
+  if (_DBUS_UNLIKELY (new_len > (unsigned long) real->max_length))
+    return FALSE;
+  
+  delta = new_len - real->len;
+  _dbus_assert (delta >= 0);
+
+  if (delta == 0) /* only happens if gap_size == 0 and insert_at is aligned already */
+    {
+      _dbus_assert (((unsigned long) *insert_at_p) == gap_pos);
+      return TRUE;
+    }
+
+  if (_DBUS_UNLIKELY (!open_gap (new_len - real->len,
+                                 real, insert_at)))
+    return FALSE;
+
+  /* nul the padding if we had to add any padding */
+  if (gap_size < delta)
+    {
+      memset (&real->str[insert_at], '\0',
+              gap_pos - insert_at);
+    }
+
+  *insert_at_p = gap_pos;
+  
+  return TRUE;
+}
+
+static dbus_bool_t
+align_length_then_lengthen (DBusString *str,
+                            int         alignment,
+                            int         then_lengthen_by)
+{
+  int insert_at;
+
+  insert_at = _dbus_string_get_length (str);
+  
+  return align_insert_point_then_open_gap (str,
+                                           &insert_at,
+                                           alignment, then_lengthen_by);
+}
+
+/**
+ * Align the length of a string to a specific alignment (typically 4 or 8)
+ * by appending nul bytes to the string.
+ *
+ * @param str a string
+ * @param alignment the alignment
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_align_length (DBusString *str,
+                           int         alignment)
+{
+  return align_length_then_lengthen (str, alignment, 0);
+}
+
+/**
+ * Preallocate extra_bytes such that a future lengthening of the
+ * string by extra_bytes is guaranteed to succeed without an out of
+ * memory error.
+ *
+ * @param str a string
+ * @param extra_bytes bytes to alloc
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_alloc_space (DBusString        *str,
+                          int                extra_bytes)
+{
+  if (!_dbus_string_lengthen (str, extra_bytes))
+    return FALSE;
+  _dbus_string_shorten (str, extra_bytes);
+
+  return TRUE;
+}
+
+static dbus_bool_t
+append (DBusRealString *real,
+        const char     *buffer,
+        int             buffer_len)
+{
+  if (buffer_len == 0)
+    return TRUE;
+
+  if (!_dbus_string_lengthen ((DBusString*)real, buffer_len))
+    return FALSE;
+
+  memcpy (real->str + (real->len - buffer_len),
+          buffer,
+          buffer_len);
+
+  return TRUE;
+}
+
+/**
+ * Appends a nul-terminated C-style string to a DBusString.
+ *
+ * @param str the DBusString
+ * @param buffer the nul-terminated characters to append
+ * @returns #FALSE if not enough memory.
+ */
+dbus_bool_t
+_dbus_string_append (DBusString *str,
+                     const char *buffer)
+{
+  unsigned long buffer_len;
+  
+  DBUS_STRING_PREAMBLE (str);
+  _dbus_assert (buffer != NULL);
+  
+  buffer_len = strlen (buffer);
+  if (buffer_len > (unsigned long) real->max_length)
+    return FALSE;
+  
+  return append (real, buffer, buffer_len);
+}
+
+/** assign 2 bytes from one string to another */
+#define ASSIGN_2_OCTETS(p, octets) \
+  *((dbus_uint16_t*)(p)) = *((dbus_uint16_t*)(octets));
+
+/** assign 4 bytes from one string to another */
+#define ASSIGN_4_OCTETS(p, octets) \
+  *((dbus_uint32_t*)(p)) = *((dbus_uint32_t*)(octets));
+
+#ifdef DBUS_HAVE_INT64
+/** assign 8 bytes from one string to another */
+#define ASSIGN_8_OCTETS(p, octets) \
+  *((dbus_uint64_t*)(p)) = *((dbus_uint64_t*)(octets));
+#else
+/** assign 8 bytes from one string to another */
+#define ASSIGN_8_OCTETS(p, octets)              \
+do {                                            \
+  unsigned char *b;                             \
+                                                \
+  b = p;                                        \
+                                                \
+  *b++ = octets[0];                             \
+  *b++ = octets[1];                             \
+  *b++ = octets[2];                             \
+  *b++ = octets[3];                             \
+  *b++ = octets[4];                             \
+  *b++ = octets[5];                             \
+  *b++ = octets[6];                             \
+  *b++ = octets[7];                             \
+  _dbus_assert (b == p + 8);                    \
+} while (0)
+#endif /* DBUS_HAVE_INT64 */
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Appends 4 bytes aligned on a 4 byte boundary
+ * with any alignment padding initialized to 0.
+ *
+ * @param str the DBusString
+ * @param octets 4 bytes to append
+ * @returns #FALSE if not enough memory.
+ */
+dbus_bool_t
+_dbus_string_append_4_aligned (DBusString         *str,
+                               const unsigned char octets[4])
+{
+  DBUS_STRING_PREAMBLE (str);
+  
+  if (!align_length_then_lengthen (str, 4, 4))
+    return FALSE;
+
+  ASSIGN_4_OCTETS (real->str + (real->len - 4), octets);
+
+  return TRUE;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Appends 8 bytes aligned on an 8 byte boundary
+ * with any alignment padding initialized to 0.
+ *
+ * @param str the DBusString
+ * @param octets 8 bytes to append
+ * @returns #FALSE if not enough memory.
+ */
+dbus_bool_t
+_dbus_string_append_8_aligned (DBusString         *str,
+                               const unsigned char octets[8])
+{
+  DBUS_STRING_PREAMBLE (str);
+  
+  if (!align_length_then_lengthen (str, 8, 8))
+    return FALSE;
+
+  ASSIGN_8_OCTETS (real->str + (real->len - 8), octets);
+
+  return TRUE;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * Inserts 2 bytes aligned on a 2 byte boundary
+ * with any alignment padding initialized to 0.
+ *
+ * @param str the DBusString
+ * @param insert_at where to insert
+ * @param octets 2 bytes to insert
+ * @returns #FALSE if not enough memory.
+ */
+dbus_bool_t
+_dbus_string_insert_2_aligned (DBusString         *str,
+                               int                 insert_at,
+                               const unsigned char octets[4])
+{
+  DBUS_STRING_PREAMBLE (str);
+  
+  if (!align_insert_point_then_open_gap (str, &insert_at, 2, 2))
+    return FALSE;
+
+  ASSIGN_2_OCTETS (real->str + insert_at, octets);
+
+  return TRUE;
+}
+
+/**
+ * Inserts 4 bytes aligned on a 4 byte boundary
+ * with any alignment padding initialized to 0.
+ *
+ * @param str the DBusString
+ * @param insert_at where to insert
+ * @param octets 4 bytes to insert
+ * @returns #FALSE if not enough memory.
+ */
+dbus_bool_t
+_dbus_string_insert_4_aligned (DBusString         *str,
+                               int                 insert_at,
+                               const unsigned char octets[4])
+{
+  DBUS_STRING_PREAMBLE (str);
+  
+  if (!align_insert_point_then_open_gap (str, &insert_at, 4, 4))
+    return FALSE;
+
+  ASSIGN_4_OCTETS (real->str + insert_at, octets);
+
+  return TRUE;
+}
+
+/**
+ * Inserts 8 bytes aligned on an 8 byte boundary
+ * with any alignment padding initialized to 0.
+ *
+ * @param str the DBusString
+ * @param insert_at where to insert
+ * @param octets 8 bytes to insert
+ * @returns #FALSE if not enough memory.
+ */
+dbus_bool_t
+_dbus_string_insert_8_aligned (DBusString         *str,
+                               int                 insert_at,
+                               const unsigned char octets[8])
+{
+  DBUS_STRING_PREAMBLE (str);
+  
+  if (!align_insert_point_then_open_gap (str, &insert_at, 8, 8))
+    return FALSE;
+
+  _dbus_assert (_DBUS_ALIGN_VALUE (insert_at, 8) == (unsigned) insert_at);
+  
+  ASSIGN_8_OCTETS (real->str + insert_at, octets);
+
+  return TRUE;
+}
+
+
+/**
+ * Inserts padding at *insert_at such to align it to the given
+ * boundary. Initializes the padding to nul bytes. Sets *insert_at
+ * to the aligned position.
+ *
+ * @param str the DBusString
+ * @param insert_at location to be aligned
+ * @param alignment alignment boundary (1, 2, 4, or 8)
+ * @returns #FALSE if not enough memory.
+ */
+dbus_bool_t
+_dbus_string_insert_alignment (DBusString        *str,
+                               int               *insert_at,
+                               int                alignment)
+{
+  DBUS_STRING_PREAMBLE (str);
+  
+  if (!align_insert_point_then_open_gap (str, insert_at, alignment, 0))
+    return FALSE;
+
+  _dbus_assert (_DBUS_ALIGN_VALUE (*insert_at, alignment) == (unsigned) *insert_at);
+
+  return TRUE;
+}
+
+/**
+ * Appends a printf-style formatted string
+ * to the #DBusString.
+ *
+ * @param str the string
+ * @param format printf format
+ * @param args variable argument list
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_append_printf_valist  (DBusString        *str,
+                                    const char        *format,
+                                    va_list            args)
+{
+  int len;
+  va_list args_copy;
+
+  DBUS_STRING_PREAMBLE (str);
+
+  DBUS_VA_COPY (args_copy, args);
+
+  /* Measure the message length without terminating nul */
+  len = _dbus_printf_string_upper_bound (format, args);
+
+  if (!_dbus_string_lengthen (str, len))
+    {
+      /* don't leak the copy */
+      va_end (args_copy);
+      return FALSE;
+    }
+  
+  vsprintf ((char*) (real->str + (real->len - len)),
+            format, args_copy);
+
+  va_end (args_copy);
+
+  return TRUE;
+}
+
+/**
+ * Appends a printf-style formatted string
+ * to the #DBusString.
+ *
+ * @param str the string
+ * @param format printf format
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_append_printf (DBusString        *str,
+                            const char        *format,
+                            ...)
+{
+  va_list args;
+  dbus_bool_t retval;
+  
+  va_start (args, format);
+  retval = _dbus_string_append_printf_valist (str, format, args);
+  va_end (args);
+
+  return retval;
+}
+
+/**
+ * Appends block of bytes with the given length to a DBusString.
+ *
+ * @param str the DBusString
+ * @param buffer the bytes to append
+ * @param len the number of bytes to append
+ * @returns #FALSE if not enough memory.
+ */
+dbus_bool_t
+_dbus_string_append_len (DBusString *str,
+                         const char *buffer,
+                         int         len)
+{
+  DBUS_STRING_PREAMBLE (str);
+  _dbus_assert (buffer != NULL);
+  _dbus_assert (len >= 0);
+
+  return append (real, buffer, len);
+}
+
+/**
+ * Appends a single byte to the string, returning #FALSE
+ * if not enough memory.
+ *
+ * @param str the string
+ * @param byte the byte to append
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_string_append_byte (DBusString    *str,
+                          unsigned char  byte)
+{
+  DBUS_STRING_PREAMBLE (str);
+
+  if (!set_length (real, real->len + 1))
+    return FALSE;
+
+  real->str[real->len-1] = byte;
+
+  return TRUE;
+}
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Appends a single Unicode character, encoding the character
+ * in UTF-8 format.
+ *
+ * @param str the string
+ * @param ch the Unicode character
+ */
+dbus_bool_t
+_dbus_string_append_unichar (DBusString    *str,
+                             dbus_unichar_t ch)
+{
+  int len;
+  int first;
+  int i;
+  unsigned char *out;
+  
+  DBUS_STRING_PREAMBLE (str);
+
+  /* this code is from GLib but is pretty standard I think */
+  
+  len = 0;
+  
+  if (ch < 0x80)
+    {
+      first = 0;
+      len = 1;
+    }
+  else if (ch < 0x800)
+    {
+      first = 0xc0;
+      len = 2;
+    }
+  else if (ch < 0x10000)
+    {
+      first = 0xe0;
+      len = 3;
+    }
+   else if (ch < 0x200000)
+    {
+      first = 0xf0;
+      len = 4;
+    }
+  else if (ch < 0x4000000)
+    {
+      first = 0xf8;
+      len = 5;
+    }
+  else
+    {
+      first = 0xfc;
+      len = 6;
+    }
+
+  if (len > (real->max_length - real->len))
+    return FALSE; /* real->len + len would overflow */
+  
+  if (!set_length (real, real->len + len))
+    return FALSE;
+
+  out = real->str + (real->len - len);
+  
+  for (i = len - 1; i > 0; --i)
+    {
+      out[i] = (ch & 0x3f) | 0x80;
+      ch >>= 6;
+    }
+  out[0] = ch | first;
+
+  return TRUE;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+static void
+delete (DBusRealString *real,
+        int             start,
+        int             len)
+{
+  if (len == 0)
+    return;
+  
+  memmove (real->str + start, real->str + start + len, real->len - (start + len));
+  real->len -= len;
+  real->str[real->len] = '\0';
+}
+
+/**
+ * Deletes a segment of a DBusString with length len starting at
+ * start. (Hint: to clear an entire string, setting length to 0
+ * with _dbus_string_set_length() is easier.)
+ *
+ * @param str the DBusString
+ * @param start where to start deleting
+ * @param len the number of bytes to delete
+ */
+void
+_dbus_string_delete (DBusString       *str,
+                     int               start,
+                     int               len)
+{
+  DBUS_STRING_PREAMBLE (str);
+  _dbus_assert (start >= 0);
+  _dbus_assert (len >= 0);
+  _dbus_assert (start <= real->len);
+  _dbus_assert (len <= real->len - start);
+  
+  delete (real, start, len);
+}
+
+static dbus_bool_t
+copy (DBusRealString *source,
+      int             start,
+      int             len,
+      DBusRealString *dest,
+      int             insert_at)
+{
+  if (len == 0)
+    return TRUE;
+
+  if (!open_gap (len, dest, insert_at))
+    return FALSE;
+  
+  memmove (dest->str + insert_at,
+           source->str + start,
+           len);
+
+  return TRUE;
+}
+
+/**
+ * Checks assertions for two strings we're copying a segment between,
+ * and declares real_source/real_dest variables.
+ *
+ * @param source the source string
+ * @param start the starting offset
+ * @param dest the dest string
+ * @param insert_at where the copied segment is inserted
+ */
+#define DBUS_STRING_COPY_PREAMBLE(source, start, dest, insert_at)       \
+  DBusRealString *real_source = (DBusRealString*) source;               \
+  DBusRealString *real_dest = (DBusRealString*) dest;                   \
+  _dbus_assert ((source) != (dest));                                    \
+  DBUS_GENERIC_STRING_PREAMBLE (real_source);                           \
+  DBUS_GENERIC_STRING_PREAMBLE (real_dest);                             \
+  _dbus_assert (!real_dest->constant);                                  \
+  _dbus_assert (!real_dest->locked);                                    \
+  _dbus_assert ((start) >= 0);                                          \
+  _dbus_assert ((start) <= real_source->len);                           \
+  _dbus_assert ((insert_at) >= 0);                                      \
+  _dbus_assert ((insert_at) <= real_dest->len)
+
+/**
+ * Moves the end of one string into another string. Both strings
+ * must be initialized, valid strings.
+ *
+ * @param source the source string
+ * @param start where to chop off the source string
+ * @param dest the destination string
+ * @param insert_at where to move the chopped-off part of source string
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_string_move (DBusString       *source,
+                   int               start,
+                   DBusString       *dest,
+                   int               insert_at)
+{
+  DBusRealString *real_source = (DBusRealString*) source;
+  _dbus_assert (start <= real_source->len);
+  
+  return _dbus_string_move_len (source, start,
+                                real_source->len - start,
+                                dest, insert_at);
+}
+
+/**
+ * Like _dbus_string_move(), but does not delete the section
+ * of the source string that's copied to the dest string.
+ *
+ * @param source the source string
+ * @param start where to start copying the source string
+ * @param dest the destination string
+ * @param insert_at where to place the copied part of source string
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_string_copy (const DBusString *source,
+                   int               start,
+                   DBusString       *dest,
+                   int               insert_at)
+{
+  DBUS_STRING_COPY_PREAMBLE (source, start, dest, insert_at);
+
+  return copy (real_source, start,
+               real_source->len - start,
+               real_dest,
+               insert_at);
+}
+
+/**
+ * Like _dbus_string_move(), but can move a segment from
+ * the middle of the source string.
+ *
+ * @todo this doesn't do anything with max_length field.
+ * we should probably just kill the max_length field though.
+ * 
+ * @param source the source string
+ * @param start first byte of source string to move
+ * @param len length of segment to move
+ * @param dest the destination string
+ * @param insert_at where to move the bytes from the source string
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_string_move_len (DBusString       *source,
+                       int               start,
+                       int               len,
+                       DBusString       *dest,
+                       int               insert_at)
+
+{
+  DBUS_STRING_COPY_PREAMBLE (source, start, dest, insert_at);
+  _dbus_assert (len >= 0);
+  _dbus_assert ((start + len) <= real_source->len);
+
+
+  if (len == 0)
+    {
+      return TRUE;
+    }
+  else if (start == 0 &&
+           len == real_source->len &&
+           real_dest->len == 0)
+    {
+      /* Short-circuit moving an entire existing string to an empty string
+       * by just swapping the buffers.
+       */
+      /* we assume ->constant doesn't matter as you can't have
+       * a constant string involved in a move.
+       */
+#define ASSIGN_DATA(a, b) do {                  \
+        (a)->str = (b)->str;                    \
+        (a)->len = (b)->len;                    \
+        (a)->allocated = (b)->allocated;        \
+        (a)->align_offset = (b)->align_offset;  \
+      } while (0)
+      
+      DBusRealString tmp;
+
+      ASSIGN_DATA (&tmp, real_source);
+      ASSIGN_DATA (real_source, real_dest);
+      ASSIGN_DATA (real_dest, &tmp);
+
+      return TRUE;
+    }
+  else
+    {
+      if (!copy (real_source, start, len,
+                 real_dest,
+                 insert_at))
+        return FALSE;
+      
+      delete (real_source, start,
+              len);
+      
+      return TRUE;
+    }
+}
+
+/**
+ * Like _dbus_string_copy(), but can copy a segment from the middle of
+ * the source string.
+ *
+ * @param source the source string
+ * @param start where to start copying the source string
+ * @param len length of segment to copy
+ * @param dest the destination string
+ * @param insert_at where to place the copied segment of source string
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_string_copy_len (const DBusString *source,
+                       int               start,
+                       int               len,
+                       DBusString       *dest,
+                       int               insert_at)
+{
+  DBUS_STRING_COPY_PREAMBLE (source, start, dest, insert_at);
+  _dbus_assert (len >= 0);
+  _dbus_assert (start <= real_source->len);
+  _dbus_assert (len <= real_source->len - start);
+  
+  return copy (real_source, start, len,
+               real_dest,
+               insert_at);
+}
+
+/**
+ * Replaces a segment of dest string with a segment of source string.
+ *
+ * @todo optimize the case where the two lengths are the same, and
+ * avoid memmoving the data in the trailing part of the string twice.
+ *
+ * @todo avoid inserting the source into dest, then deleting
+ * the replaced chunk of dest (which creates a potentially large
+ * intermediate string). Instead, extend the replaced chunk
+ * of dest with padding to the same size as the source chunk,
+ * then copy in the source bytes.
+ * 
+ * @param source the source string
+ * @param start where to start copying the source string
+ * @param len length of segment to copy
+ * @param dest the destination string
+ * @param replace_at start of segment of dest string to replace
+ * @param replace_len length of segment of dest string to replace
+ * @returns #FALSE if not enough memory
+ *
+ */
+dbus_bool_t
+_dbus_string_replace_len (const DBusString *source,
+                          int               start,
+                          int               len,
+                          DBusString       *dest,
+                          int               replace_at,
+                          int               replace_len)
+{
+  DBUS_STRING_COPY_PREAMBLE (source, start, dest, replace_at);
+  _dbus_assert (len >= 0);
+  _dbus_assert (start <= real_source->len);
+  _dbus_assert (len <= real_source->len - start);
+  _dbus_assert (replace_at >= 0);
+  _dbus_assert (replace_at <= real_dest->len);
+  _dbus_assert (replace_len <= real_dest->len - replace_at);
+
+  if (!copy (real_source, start, len,
+             real_dest, replace_at))
+    return FALSE;
+
+  delete (real_dest, replace_at + len, replace_len);
+
+  return TRUE;
+}
+
+/**
+ * Looks for the first occurance of a byte, deletes that byte,
+ * and moves everything after the byte to the beginning of a
+ * separate string.  Both strings must be initialized, valid
+ * strings.
+ *
+ * @param source the source string
+ * @param byte the byte to remove and split the string at
+ * @param tail the split off string
+ * @returns #FALSE if not enough memory or if byte could not be found
+ *
+ */
+dbus_bool_t
+_dbus_string_split_on_byte (DBusString        *source,
+                            unsigned char      byte,
+                            DBusString        *tail)
+{
+  int byte_position;
+  char byte_string[2] = "";
+  int head_length;
+  int tail_length;
+
+  byte_string[0] = (char) byte;
+
+  if (!_dbus_string_find (source, 0, byte_string, &byte_position))
+    return FALSE;
+
+  head_length = byte_position;
+  tail_length = _dbus_string_get_length (source) - head_length - 1;
+
+  if (!_dbus_string_move_len (source, byte_position + 1, tail_length,
+                              tail, 0))
+    return FALSE;
+
+  /* remove the trailing delimiter byte from the head now.
+   */
+  if (!_dbus_string_set_length (source, head_length))
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Unicode macros and utf8_validate() from GLib Owen Taylor, Havoc
+ * Pennington, and Tom Tromey are the authors and authorized relicense.
+ */
+
+/** computes length and mask of a unicode character
+ * @param Char the char
+ * @param Mask the mask variable to assign to
+ * @param Len the length variable to assign to
+ */
+#define UTF8_COMPUTE(Char, Mask, Len)                                        \
+  if (Char < 128)                                                            \
+    {                                                                        \
+      Len = 1;                                                               \
+      Mask = 0x7f;                                                           \
+    }                                                                        \
+  else if ((Char & 0xe0) == 0xc0)                                            \
+    {                                                                        \
+      Len = 2;                                                               \
+      Mask = 0x1f;                                                           \
+    }                                                                        \
+  else if ((Char & 0xf0) == 0xe0)                                            \
+    {                                                                        \
+      Len = 3;                                                               \
+      Mask = 0x0f;                                                           \
+    }                                                                        \
+  else if ((Char & 0xf8) == 0xf0)                                            \
+    {                                                                        \
+      Len = 4;                                                               \
+      Mask = 0x07;                                                           \
+    }                                                                        \
+  else if ((Char & 0xfc) == 0xf8)                                            \
+    {                                                                        \
+      Len = 5;                                                               \
+      Mask = 0x03;                                                           \
+    }                                                                        \
+  else if ((Char & 0xfe) == 0xfc)                                            \
+    {                                                                        \
+      Len = 6;                                                               \
+      Mask = 0x01;                                                           \
+    }                                                                        \
+  else                                                                        \
+    {                                                                         \
+      Len = 0;                                                               \
+      Mask = 0;                                                               \
+    }
+
+/**
+ * computes length of a unicode character in UTF-8
+ * @param Char the char
+ */
+#define UTF8_LENGTH(Char)              \
+  ((Char) < 0x80 ? 1 :                 \
+   ((Char) < 0x800 ? 2 :               \
+    ((Char) < 0x10000 ? 3 :            \
+     ((Char) < 0x200000 ? 4 :          \
+      ((Char) < 0x4000000 ? 5 : 6)))))
+   
+/**
+ * Gets a UTF-8 value.
+ *
+ * @param Result variable for extracted unicode char.
+ * @param Chars the bytes to decode
+ * @param Count counter variable
+ * @param Mask mask for this char
+ * @param Len length for this char in bytes
+ */
+#define UTF8_GET(Result, Chars, Count, Mask, Len)                            \
+  (Result) = (Chars)[0] & (Mask);                                            \
+  for ((Count) = 1; (Count) < (Len); ++(Count))                                      \
+    {                                                                        \
+      if (((Chars)[(Count)] & 0xc0) != 0x80)                                 \
+       {                                                                     \
+         (Result) = -1;                                                      \
+         break;                                                              \
+       }                                                                     \
+      (Result) <<= 6;                                                        \
+      (Result) |= ((Chars)[(Count)] & 0x3f);                                 \
+    }
+
+/**
+ * Check whether a unicode char is in a valid range.
+ *
+ * @param Char the character
+ */
+#define UNICODE_VALID(Char)                   \
+    ((Char) < 0x110000 &&                     \
+     (((Char) & 0xFFFFF800) != 0xD800) &&     \
+     ((Char) < 0xFDD0 || (Char) > 0xFDEF) &&  \
+     ((Char) & 0xFFFF) != 0xFFFF)
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Gets a unicode character from a UTF-8 string. Does no validation;
+ * you must verify that the string is valid UTF-8 in advance and must
+ * pass in the start of a character.
+ *
+ * @param str the string
+ * @param start the start of the UTF-8 character.
+ * @param ch_return location to return the character
+ * @param end_return location to return the byte index of next character
+ */
+void
+_dbus_string_get_unichar (const DBusString *str,
+                          int               start,
+                          dbus_unichar_t   *ch_return,
+                          int              *end_return)
+{
+  int i, mask, len;
+  dbus_unichar_t result;
+  unsigned char c;
+  unsigned char *p;
+  DBUS_CONST_STRING_PREAMBLE (str);
+  _dbus_assert (start >= 0);
+  _dbus_assert (start <= real->len);
+  
+  if (ch_return)
+    *ch_return = 0;
+  if (end_return)
+    *end_return = real->len;
+  
+  mask = 0;
+  p = real->str + start;
+  c = *p;
+  
+  UTF8_COMPUTE (c, mask, len);
+  if (len == 0)
+    return;
+  UTF8_GET (result, p, i, mask, len);
+
+  if (result == (dbus_unichar_t)-1)
+    return;
+
+  if (ch_return)
+    *ch_return = result;
+  if (end_return)
+    *end_return = start + len;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * Finds the given substring in the string,
+ * returning #TRUE and filling in the byte index
+ * where the substring was found, if it was found.
+ * Returns #FALSE if the substring wasn't found.
+ * Sets *start to the length of the string if the substring
+ * is not found.
+ *
+ * @param str the string
+ * @param start where to start looking
+ * @param substr the substring
+ * @param found return location for where it was found, or #NULL
+ * @returns #TRUE if found
+ */
+dbus_bool_t
+_dbus_string_find (const DBusString *str,
+                   int               start,
+                   const char       *substr,
+                   int              *found)
+{
+  return _dbus_string_find_to (str, start,
+                               ((const DBusRealString*)str)->len,
+                               substr, found);
+}
+
+/**
+ * Finds end of line ("\r\n" or "\n") in the string,
+ * returning #TRUE and filling in the byte index
+ * where the eol string was found, if it was found.
+ * Returns #FALSE if eol wasn't found.
+ *
+ * @param str the string
+ * @param start where to start looking
+ * @param found return location for where eol was found or string length otherwise
+ * @param found_len return length of found eol string or zero otherwise
+ * @returns #TRUE if found
+ */
+dbus_bool_t
+_dbus_string_find_eol (const DBusString *str,
+                       int               start,
+                       int              *found,
+                       int              *found_len)
+{
+  int i;
+
+  DBUS_CONST_STRING_PREAMBLE (str);
+  _dbus_assert (start <= real->len);
+  _dbus_assert (start >= 0);
+  
+  i = start;
+  while (i < real->len)
+    {
+      if (real->str[i] == '\r') 
+        {
+          if ((i+1) < real->len && real->str[i+1] == '\n') /* "\r\n" */
+            {
+              if (found) 
+                *found = i;
+              if (found_len)
+                *found_len = 2;
+              return TRUE;
+            } 
+          else /* only "\r" */
+            {
+              if (found) 
+                *found = i;
+              if (found_len)
+                *found_len = 1;
+              return TRUE;
+            }
+        } 
+      else if (real->str[i] == '\n')  /* only "\n" */
+        {
+          if (found) 
+            *found = i;
+          if (found_len)
+            *found_len = 1;
+          return TRUE;
+        }
+      ++i;
+    }
+
+  if (found)
+    *found = real->len;
+
+  if (found_len)
+    *found_len = 0;
+  
+  return FALSE;
+}
+
+/**
+ * Finds the given substring in the string,
+ * up to a certain position,
+ * returning #TRUE and filling in the byte index
+ * where the substring was found, if it was found.
+ * Returns #FALSE if the substring wasn't found.
+ * Sets *start to the length of the string if the substring
+ * is not found.
+ *
+ * @param str the string
+ * @param start where to start looking
+ * @param end where to stop looking
+ * @param substr the substring
+ * @param found return location for where it was found, or #NULL
+ * @returns #TRUE if found
+ */
+dbus_bool_t
+_dbus_string_find_to (const DBusString *str,
+                     int               start,
+                     int               end,
+                     const char       *substr,
+                     int              *found)
+{
+  int i;
+  DBUS_CONST_STRING_PREAMBLE (str);
+  _dbus_assert (substr != NULL);
+  _dbus_assert (start <= real->len);
+  _dbus_assert (start >= 0);
+  _dbus_assert (substr != NULL);
+  _dbus_assert (end <= real->len);
+  _dbus_assert (start <= end);
+
+  /* we always "find" an empty string */
+  if (*substr == '\0')
+    {
+      if (found)
+        *found = start;
+      return TRUE;
+    }
+
+  i = start;
+  while (i < end)
+    {
+      if (real->str[i] == substr[0])
+        {
+          int j = i + 1;
+          
+          while (j < end)
+            {
+              if (substr[j - i] == '\0')
+                break;
+              else if (real->str[j] != substr[j - i])
+                break;
+              
+              ++j;
+            }
+
+          if (substr[j - i] == '\0')
+            {
+              if (found)
+                *found = i;
+              return TRUE;
+            }
+        }
+      
+      ++i;
+    }
+
+  if (found)
+    *found = end;
+  
+  return FALSE;  
+}
+
+/**
+ * Finds a blank (space or tab) in the string. Returns #TRUE
+ * if found, #FALSE otherwise. If a blank is not found sets
+ * *found to the length of the string.
+ *
+ * @param str the string
+ * @param start byte index to start looking
+ * @param found place to store the location of the first blank
+ * @returns #TRUE if a blank was found
+ */
+dbus_bool_t
+_dbus_string_find_blank (const DBusString *str,
+                         int               start,
+                         int              *found)
+{
+  int i;
+  DBUS_CONST_STRING_PREAMBLE (str);
+  _dbus_assert (start <= real->len);
+  _dbus_assert (start >= 0);
+  
+  i = start;
+  while (i < real->len)
+    {
+      if (real->str[i] == ' ' ||
+          real->str[i] == '\t')
+        {
+          if (found)
+            *found = i;
+          return TRUE;
+        }
+      
+      ++i;
+    }
+
+  if (found)
+    *found = real->len;
+  
+  return FALSE;
+}
+
+/**
+ * Skips blanks from start, storing the first non-blank in *end
+ * (blank is space or tab).
+ *
+ * @param str the string
+ * @param start where to start
+ * @param end where to store the first non-blank byte index
+ */
+void
+_dbus_string_skip_blank (const DBusString *str,
+                         int               start,
+                         int              *end)
+{
+  int i;
+  DBUS_CONST_STRING_PREAMBLE (str);
+  _dbus_assert (start <= real->len);
+  _dbus_assert (start >= 0);
+  
+  i = start;
+  while (i < real->len)
+    {
+      if (!DBUS_IS_ASCII_BLANK (real->str[i]))
+        break;
+      
+      ++i;
+    }
+
+  _dbus_assert (i == real->len || !DBUS_IS_ASCII_WHITE (real->str[i]));
+  
+  if (end)
+    *end = i;
+}
+
+
+/**
+ * Skips whitespace from start, storing the first non-whitespace in *end.
+ * (whitespace is space, tab, newline, CR).
+ *
+ * @param str the string
+ * @param start where to start
+ * @param end where to store the first non-whitespace byte index
+ */
+void
+_dbus_string_skip_white (const DBusString *str,
+                         int               start,
+                         int              *end)
+{
+  int i;
+  DBUS_CONST_STRING_PREAMBLE (str);
+  _dbus_assert (start <= real->len);
+  _dbus_assert (start >= 0);
+  
+  i = start;
+  while (i < real->len)
+    {
+      if (!DBUS_IS_ASCII_WHITE (real->str[i]))
+        break;
+      
+      ++i;
+    }
+
+  _dbus_assert (i == real->len || !(DBUS_IS_ASCII_WHITE (real->str[i])));
+  
+  if (end)
+    *end = i;
+}
+
+/**
+ * Skips whitespace from end, storing the start index of the trailing
+ * whitespace in *start. (whitespace is space, tab, newline, CR).
+ *
+ * @param str the string
+ * @param end where to start scanning backward
+ * @param start where to store the start of whitespace chars
+ */
+void
+_dbus_string_skip_white_reverse (const DBusString *str,
+                                 int               end,
+                                 int              *start)
+{
+  int i;
+  DBUS_CONST_STRING_PREAMBLE (str);
+  _dbus_assert (end <= real->len);
+  _dbus_assert (end >= 0);
+  
+  i = end;
+  while (i > 0)
+    {
+      if (!DBUS_IS_ASCII_WHITE (real->str[i-1]))
+        break;
+      --i;
+    }
+
+  _dbus_assert (i >= 0 && (i == 0 || !(DBUS_IS_ASCII_WHITE (real->str[i-1]))));
+  
+  if (start)
+    *start = i;
+}
+
+/**
+ * Assigns a newline-terminated or \\r\\n-terminated line from the front
+ * of the string to the given dest string. The dest string's previous
+ * contents are deleted. If the source string contains no newline,
+ * moves the entire source string to the dest string.
+ *
+ * @todo owen correctly notes that this is a stupid function (it was
+ * written purely for test code,
+ * e.g. dbus-message-builder.c). Probably should be enforced as test
+ * code only with ifdef DBUS_BUILD_TESTS
+ * 
+ * @param source the source string
+ * @param dest the destination string (contents are replaced)
+ * @returns #FALSE if no memory, or source has length 0
+ */
+dbus_bool_t
+_dbus_string_pop_line (DBusString *source,
+                       DBusString *dest)
+{
+  int eol, eol_len;
+  
+  _dbus_string_set_length (dest, 0);
+  
+  eol = 0;
+  eol_len = 0;
+  if (!_dbus_string_find_eol (source, 0, &eol, &eol_len))
+    {
+      _dbus_assert (eol == _dbus_string_get_length (source));
+      if (eol == 0)
+        {
+          /* If there's no newline and source has zero length, we're done */
+          return FALSE;
+        }
+      /* otherwise, the last line of the file has no eol characters */
+    }
+
+  /* remember eol can be 0 if it's an empty line, but eol_len should not be zero also
+   * since find_eol returned TRUE
+   */
+  
+  if (!_dbus_string_move_len (source, 0, eol + eol_len, dest, 0))
+    return FALSE;
+  
+  /* remove line ending */
+  if (!_dbus_string_set_length (dest, eol))
+    {
+      _dbus_assert_not_reached ("out of memory when shortening a string");
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Deletes up to and including the first blank space
+ * in the string.
+ *
+ * @param str the string
+ */
+void
+_dbus_string_delete_first_word (DBusString *str)
+{
+  int i;
+  
+  if (_dbus_string_find_blank (str, 0, &i))
+    _dbus_string_skip_blank (str, i, &i);
+
+  _dbus_string_delete (str, 0, i);
+}
+#endif
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Deletes any leading blanks in the string
+ *
+ * @param str the string
+ */
+void
+_dbus_string_delete_leading_blanks (DBusString *str)
+{
+  int i;
+  
+  _dbus_string_skip_blank (str, 0, &i);
+
+  if (i > 0)
+    _dbus_string_delete (str, 0, i);
+}
+#endif
+
+/**
+ * Deletes leading and trailing whitespace
+ * 
+ * @param str the string
+ */
+void
+_dbus_string_chop_white(DBusString *str)
+{
+  int i;
+  
+  _dbus_string_skip_white (str, 0, &i);
+
+  if (i > 0)
+    _dbus_string_delete (str, 0, i);
+  
+  _dbus_string_skip_white_reverse (str, _dbus_string_get_length (str), &i);
+
+  _dbus_string_set_length (str, i);
+}
+
+/**
+ * Tests two DBusString for equality.
+ *
+ * @todo memcmp is probably faster
+ *
+ * @param a first string
+ * @param b second string
+ * @returns #TRUE if equal
+ */
+dbus_bool_t
+_dbus_string_equal (const DBusString *a,
+                    const DBusString *b)
+{
+  const unsigned char *ap;
+  const unsigned char *bp;
+  const unsigned char *a_end;
+  const DBusRealString *real_a = (const DBusRealString*) a;
+  const DBusRealString *real_b = (const DBusRealString*) b;
+  DBUS_GENERIC_STRING_PREAMBLE (real_a);
+  DBUS_GENERIC_STRING_PREAMBLE (real_b);
+
+  if (real_a->len != real_b->len)
+    return FALSE;
+
+  ap = real_a->str;
+  bp = real_b->str;
+  a_end = real_a->str + real_a->len;
+  while (ap != a_end)
+    {
+      if (*ap != *bp)
+        return FALSE;
+      
+      ++ap;
+      ++bp;
+    }
+
+  return TRUE;
+}
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Tests two DBusString for equality up to the given length.
+ * The strings may be shorter than the given length.
+ *
+ * @todo write a unit test
+ *
+ * @todo memcmp is probably faster
+ *
+ * @param a first string
+ * @param b second string
+ * @param len the maximum length to look at
+ * @returns #TRUE if equal for the given number of bytes
+ */
+dbus_bool_t
+_dbus_string_equal_len (const DBusString *a,
+                        const DBusString *b,
+                        int               len)
+{
+  const unsigned char *ap;
+  const unsigned char *bp;
+  const unsigned char *a_end;
+  const DBusRealString *real_a = (const DBusRealString*) a;
+  const DBusRealString *real_b = (const DBusRealString*) b;
+  DBUS_GENERIC_STRING_PREAMBLE (real_a);
+  DBUS_GENERIC_STRING_PREAMBLE (real_b);
+
+  if (real_a->len != real_b->len &&
+      (real_a->len < len || real_b->len < len))
+    return FALSE;
+
+  ap = real_a->str;
+  bp = real_b->str;
+  a_end = real_a->str + MIN (real_a->len, len);
+  while (ap != a_end)
+    {
+      if (*ap != *bp)
+        return FALSE;
+      
+      ++ap;
+      ++bp;
+    }
+
+  return TRUE;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * Tests two sub-parts of two DBusString for equality.  The specified
+ * range of the first string must exist; the specified start position
+ * of the second string must exist.
+ *
+ * @todo write a unit test
+ *
+ * @todo memcmp is probably faster
+ *
+ * @param a first string
+ * @param a_start where to start substring in first string
+ * @param a_len length of substring in first string
+ * @param b second string
+ * @param b_start where to start substring in second string
+ * @returns #TRUE if the two substrings are equal
+ */
+dbus_bool_t
+_dbus_string_equal_substring (const DBusString  *a,
+                              int                a_start,
+                              int                a_len,
+                              const DBusString  *b,
+                              int                b_start)
+{
+  const unsigned char *ap;
+  const unsigned char *bp;
+  const unsigned char *a_end;
+  const DBusRealString *real_a = (const DBusRealString*) a;
+  const DBusRealString *real_b = (const DBusRealString*) b;
+  DBUS_GENERIC_STRING_PREAMBLE (real_a);
+  DBUS_GENERIC_STRING_PREAMBLE (real_b);
+  _dbus_assert (a_start >= 0);
+  _dbus_assert (a_len >= 0);
+  _dbus_assert (a_start <= real_a->len);
+  _dbus_assert (a_len <= real_a->len - a_start);
+  _dbus_assert (b_start >= 0);
+  _dbus_assert (b_start <= real_b->len);
+  
+  if (a_len > real_b->len - b_start)
+    return FALSE;
+
+  ap = real_a->str + a_start;
+  bp = real_b->str + b_start;
+  a_end = ap + a_len;
+  while (ap != a_end)
+    {
+      if (*ap != *bp)
+        return FALSE;
+      
+      ++ap;
+      ++bp;
+    }
+
+  _dbus_assert (bp <= (real_b->str + real_b->len));
+  
+  return TRUE;
+}
+
+/**
+ * Checks whether a string is equal to a C string.
+ *
+ * @param a the string
+ * @param c_str the C string
+ * @returns #TRUE if equal
+ */
+dbus_bool_t
+_dbus_string_equal_c_str (const DBusString *a,
+                          const char       *c_str)
+{
+  const unsigned char *ap;
+  const unsigned char *bp;
+  const unsigned char *a_end;
+  const DBusRealString *real_a = (const DBusRealString*) a;
+  DBUS_GENERIC_STRING_PREAMBLE (real_a);
+  _dbus_assert (c_str != NULL);
+  
+  ap = real_a->str;
+  bp = (const unsigned char*) c_str;
+  a_end = real_a->str + real_a->len;
+  while (ap != a_end && *bp)
+    {
+      if (*ap != *bp)
+        return FALSE;
+      
+      ++ap;
+      ++bp;
+    }
+
+  if (ap != a_end || *bp)
+    return FALSE;
+  
+  return TRUE;
+}
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Checks whether a string starts with the given C string.
+ *
+ * @param a the string
+ * @param c_str the C string
+ * @returns #TRUE if string starts with it
+ */
+dbus_bool_t
+_dbus_string_starts_with_c_str (const DBusString *a,
+                                const char       *c_str)
+{
+  const unsigned char *ap;
+  const unsigned char *bp;
+  const unsigned char *a_end;
+  const DBusRealString *real_a = (const DBusRealString*) a;
+  DBUS_GENERIC_STRING_PREAMBLE (real_a);
+  _dbus_assert (c_str != NULL);
+  
+  ap = real_a->str;
+  bp = (const unsigned char*) c_str;
+  a_end = real_a->str + real_a->len;
+  while (ap != a_end && *bp)
+    {
+      if (*ap != *bp)
+        return FALSE;
+      
+      ++ap;
+      ++bp;
+    }
+
+  if (*bp == '\0')
+    return TRUE;
+  else
+    return FALSE;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * Appends a two-character hex digit to a string, where the hex digit
+ * has the value of the given byte.
+ *
+ * @param str the string
+ * @param byte the byte
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_append_byte_as_hex (DBusString *str,
+                                 int         byte)
+{
+  const char hexdigits[16] = {
+    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+    'a', 'b', 'c', 'd', 'e', 'f'
+  };
+
+  if (!_dbus_string_append_byte (str,
+                                 hexdigits[(byte >> 4)]))
+    return FALSE;
+  
+  if (!_dbus_string_append_byte (str,
+                                 hexdigits[(byte & 0x0f)]))
+    {
+      _dbus_string_set_length (str,
+                               _dbus_string_get_length (str) - 1);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+/**
+ * Encodes a string in hex, the way MD5 and SHA-1 are usually
+ * encoded. (Each byte is two hex digits.)
+ *
+ * @param source the string to encode
+ * @param start byte index to start encoding
+ * @param dest string where encoded data should be placed
+ * @param insert_at where to place encoded data
+ * @returns #TRUE if encoding was successful, #FALSE if no memory etc.
+ */
+dbus_bool_t
+_dbus_string_hex_encode (const DBusString *source,
+                         int               start,
+                         DBusString       *dest,
+                         int               insert_at)
+{
+  DBusString result;
+  const unsigned char *p;
+  const unsigned char *end;
+  dbus_bool_t retval;
+  
+  _dbus_assert (start <= _dbus_string_get_length (source));
+
+  if (!_dbus_string_init (&result))
+    return FALSE;
+
+  retval = FALSE;
+  
+  p = (const unsigned char*) _dbus_string_get_const_data (source);
+  end = p + _dbus_string_get_length (source);
+  p += start;
+  
+  while (p != end)
+    {
+      if (!_dbus_string_append_byte_as_hex (&result, *p))
+        goto out;
+      
+      ++p;
+    }
+
+  if (!_dbus_string_move (&result, 0, dest, insert_at))
+    goto out;
+
+  retval = TRUE;
+
+ out:
+  _dbus_string_free (&result);
+  return retval;
+}
+
+/**
+ * Decodes a string from hex encoding.
+ *
+ * @param source the string to decode
+ * @param start byte index to start decode
+ * @param end_return return location of the end of the hex data, or #NULL
+ * @param dest string where decoded data should be placed
+ * @param insert_at where to place decoded data
+ * @returns #TRUE if decoding was successful, #FALSE if no memory.
+ */
+dbus_bool_t
+_dbus_string_hex_decode (const DBusString *source,
+                         int               start,
+                        int              *end_return,
+                         DBusString       *dest,
+                         int               insert_at)
+{
+  DBusString result;
+  const unsigned char *p;
+  const unsigned char *end;
+  dbus_bool_t retval;
+  dbus_bool_t high_bits;
+  
+  _dbus_assert (start <= _dbus_string_get_length (source));
+
+  if (!_dbus_string_init (&result))
+    return FALSE;
+
+  retval = FALSE;
+
+  high_bits = TRUE;
+  p = (const unsigned char*) _dbus_string_get_const_data (source);
+  end = p + _dbus_string_get_length (source);
+  p += start;
+  
+  while (p != end)
+    {
+      unsigned int val;
+
+      switch (*p)
+        {
+        case '0':
+          val = 0;
+          break;
+        case '1':
+          val = 1;
+          break;
+        case '2':
+          val = 2;
+          break;
+        case '3':
+          val = 3;
+          break;
+        case '4':
+          val = 4;
+          break;
+        case '5':
+          val = 5;
+          break;
+        case '6':
+          val = 6;
+          break;
+        case '7':
+          val = 7;
+          break;
+        case '8':
+          val = 8;
+          break;
+        case '9':
+          val = 9;
+          break;
+        case 'a':
+        case 'A':
+          val = 10;
+          break;
+        case 'b':
+        case 'B':
+          val = 11;
+          break;
+        case 'c':
+        case 'C':
+          val = 12;
+          break;
+        case 'd':
+        case 'D':
+          val = 13;
+          break;
+        case 'e':
+        case 'E':
+          val = 14;
+          break;
+        case 'f':
+        case 'F':
+          val = 15;
+          break;
+        default:
+          goto done;
+        }
+
+      if (high_bits)
+        {
+          if (!_dbus_string_append_byte (&result,
+                                         val << 4))
+           goto out;
+        }
+      else
+        {
+          int len;
+          unsigned char b;
+
+          len = _dbus_string_get_length (&result);
+          
+          b = _dbus_string_get_byte (&result, len - 1);
+
+          b |= val;
+
+          _dbus_string_set_byte (&result, len - 1, b);
+        }
+
+      high_bits = !high_bits;
+
+      ++p;
+    }
+
+ done:
+  if (!_dbus_string_move (&result, 0, dest, insert_at))
+    goto out;
+
+  if (end_return)
+    *end_return = p - (const unsigned char*) _dbus_string_get_const_data (source);
+
+  retval = TRUE;
+  
+ out:
+  _dbus_string_free (&result);  
+  return retval;
+}
+
+/**
+ * Checks that the given range of the string is valid ASCII with no
+ * nul bytes. If the given range is not entirely contained in the
+ * string, returns #FALSE.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ * 
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is all valid ASCII
+ */
+dbus_bool_t
+_dbus_string_validate_ascii (const DBusString *str,
+                             int               start,
+                             int               len)
+{
+  const unsigned char *s;
+  const unsigned char *end;
+  DBUS_CONST_STRING_PREAMBLE (str);
+  _dbus_assert (start >= 0);
+  _dbus_assert (start <= real->len);
+  _dbus_assert (len >= 0);
+  
+  if (len > real->len - start)
+    return FALSE;
+  
+  s = real->str + start;
+  end = s + len;
+  while (s != end)
+    {
+      if (_DBUS_UNLIKELY (!_DBUS_ISASCII (*s)))
+        return FALSE;
+        
+      ++s;
+    }
+  
+  return TRUE;
+}
+
+/**
+ * Checks that the given range of the string is valid UTF-8. If the
+ * given range is not entirely contained in the string, returns
+ * #FALSE. If the string contains any nul bytes in the given range,
+ * returns #FALSE. If the start and start+len are not on character
+ * boundaries, returns #FALSE.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ * 
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is all valid UTF-8
+ */
+dbus_bool_t
+_dbus_string_validate_utf8  (const DBusString *str,
+                             int               start,
+                             int               len)
+{
+  const unsigned char *p;
+  const unsigned char *end;
+  DBUS_CONST_STRING_PREAMBLE (str);
+  _dbus_assert (start >= 0);
+  _dbus_assert (start <= real->len);
+  _dbus_assert (len >= 0);
+
+  /* we are doing _DBUS_UNLIKELY() here which might be
+   * dubious in a generic library like GLib, but in D-Bus
+   * we know we're validating messages and that it would
+   * only be evil/broken apps that would have invalid
+   * UTF-8. Also, this function seems to be a performance
+   * bottleneck in profiles.
+   */
+  
+  if (_DBUS_UNLIKELY (len > real->len - start))
+    return FALSE;
+  
+  p = real->str + start;
+  end = p + len;
+  
+  while (p < end)
+    {
+      int i, mask, char_len;
+      dbus_unichar_t result;
+
+      /* nul bytes considered invalid */
+      if (*p == '\0')
+        break;
+      
+      /* Special-case ASCII; this makes us go a lot faster in
+       * D-Bus profiles where we are typically validating
+       * function names and such. We have to know that
+       * all following checks will pass for ASCII though,
+       * comments follow ...
+       */      
+      if (*p < 128)
+        {
+          ++p;
+          continue;
+        }
+      
+      UTF8_COMPUTE (*p, mask, char_len);
+
+      if (_DBUS_UNLIKELY (char_len == 0))  /* ASCII: char_len == 1 */
+        break;
+
+      /* check that the expected number of bytes exists in the remaining length */
+      if (_DBUS_UNLIKELY ((end - p) < char_len)) /* ASCII: p < end and char_len == 1 */
+        break;
+        
+      UTF8_GET (result, p, i, mask, char_len);
+
+      /* Check for overlong UTF-8 */
+      if (_DBUS_UNLIKELY (UTF8_LENGTH (result) != char_len)) /* ASCII: UTF8_LENGTH == 1 */
+        break;
+#if 0
+      /* The UNICODE_VALID check below will catch this */
+      if (_DBUS_UNLIKELY (result == (dbus_unichar_t)-1)) /* ASCII: result = ascii value */
+        break;
+#endif
+
+      if (_DBUS_UNLIKELY (!UNICODE_VALID (result))) /* ASCII: always valid */
+        break;
+
+      /* UNICODE_VALID should have caught it */
+      _dbus_assert (result != (dbus_unichar_t)-1);
+      
+      p += char_len;
+    }
+
+  /* See that we covered the entire length if a length was
+   * passed in
+   */
+  if (_DBUS_UNLIKELY (p != end))
+    return FALSE;
+  else
+    return TRUE;
+}
+
+/**
+ * Checks that the given range of the string is all nul bytes. If the
+ * given range is not entirely contained in the string, returns
+ * #FALSE.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ * 
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is all nul bytes
+ */
+dbus_bool_t
+_dbus_string_validate_nul (const DBusString *str,
+                           int               start,
+                           int               len)
+{
+  const unsigned char *s;
+  const unsigned char *end;
+  DBUS_CONST_STRING_PREAMBLE (str);
+  _dbus_assert (start >= 0);
+  _dbus_assert (len >= 0);
+  _dbus_assert (start <= real->len);
+  
+  if (len > real->len - start)
+    return FALSE;
+  
+  s = real->str + start;
+  end = s + len;
+  while (s != end)
+    {
+      if (_DBUS_UNLIKELY (*s != '\0'))
+        return FALSE;
+      ++s;
+    }
+  
+  return TRUE;
+}
+
+/**
+ * Clears all allocated bytes in the string to zero.
+ *
+ * @param str the string
+ */
+void
+_dbus_string_zero (DBusString *str)
+{
+  DBUS_STRING_PREAMBLE (str);
+
+  memset (real->str - real->align_offset, '\0', real->allocated);
+}
+/** @} */
+
+/* tests are in dbus-string-util.c */
diff --git a/src/dbus/dbus-string.h b/src/dbus/dbus-string.h
new file mode 100644 (file)
index 0000000..374f0a8
--- /dev/null
@@ -0,0 +1,321 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-string.h String utility class (internal to D-Bus implementation)
+ * 
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ * Copyright (C) 2006 Ralf Habacker <ralf.habacker@freenet.de>
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_STRING_H
+#define DBUS_STRING_H
+
+#include <config.h>
+
+#include <dbus/dbus-memory.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-sysdeps.h>
+
+#include <stdarg.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * DBusString object
+ */
+struct DBusString
+{
+  const void *dummy1; /**< placeholder */
+  int   dummy2;       /**< placeholder */
+  int   dummy3;       /**< placeholder */
+  int   dummy4;       /**< placeholder */
+  unsigned int dummy5 : 1; /**< placeholder */
+  unsigned int dummy6 : 1; /**< placeholder */
+  unsigned int dummy7 : 1; /**< placeholder */
+  unsigned int dummy8 : 3; /**< placeholder */
+};
+
+#ifdef DBUS_DISABLE_ASSERT
+/* Some simple inlining hacks; the current linker is not smart enough
+ * to inline non-exported symbols across files in the library.
+ * Note that these break type safety (due to the casts)
+ */
+#define _dbus_string_get_data(s) ((char*)(((DBusString*)(s))->dummy1))
+#define _dbus_string_get_length(s) (((DBusString*)(s))->dummy2)
+#define _dbus_string_set_byte(s, i, b) ((((unsigned char*)(((DBusString*)(s))->dummy1))[(i)]) = (unsigned char) (b))
+#define _dbus_string_get_byte(s, i) (((const unsigned char*)(((DBusString*)(s))->dummy1))[(i)])
+#define _dbus_string_get_const_data(s) ((const char*)(((DBusString*)(s))->dummy1))
+#define _dbus_string_get_const_data_len(s,start,len) (((const char*)(((DBusString*)(s))->dummy1)) + (start))
+#endif
+
+dbus_bool_t   _dbus_string_init                  (DBusString        *str);
+void          _dbus_string_init_const            (DBusString        *str,
+                                                  const char        *value);
+void          _dbus_string_init_const_len        (DBusString        *str,
+                                                  const char        *value,
+                                                  int                len);
+dbus_bool_t   _dbus_string_init_preallocated     (DBusString        *str,
+                                                  int                allocate_size);
+void          _dbus_string_free                  (DBusString        *str);
+void          _dbus_string_lock                  (DBusString        *str);
+dbus_bool_t   _dbus_string_compact               (DBusString        *str,
+                                                  int                max_waste);
+#ifndef _dbus_string_get_data
+char*         _dbus_string_get_data              (DBusString        *str);
+#endif /* _dbus_string_get_data */
+#ifndef _dbus_string_get_const_data
+const char*   _dbus_string_get_const_data        (const DBusString  *str);
+#endif /* _dbus_string_get_const_data */
+char*         _dbus_string_get_data_len          (DBusString        *str,
+                                                  int                start,
+                                                  int                len);
+#ifndef _dbus_string_get_const_data_len
+const char*   _dbus_string_get_const_data_len    (const DBusString  *str,
+                                                  int                start,
+                                                  int                len);
+#endif
+#ifndef _dbus_string_set_byte
+void          _dbus_string_set_byte              (DBusString        *str,
+                                                  int                i,
+                                                  unsigned char      byte);
+#endif
+#ifndef _dbus_string_get_byte
+unsigned char _dbus_string_get_byte              (const DBusString  *str,
+                                                  int                start);
+#endif /* _dbus_string_get_byte */
+dbus_bool_t   _dbus_string_insert_bytes          (DBusString        *str,
+                                                  int                i,
+                                                 int                n_bytes,
+                                                  unsigned char      byte);
+dbus_bool_t   _dbus_string_insert_byte           (DBusString        *str,
+                                                  int                i,
+                                                  unsigned char      byte);
+dbus_bool_t   _dbus_string_steal_data            (DBusString        *str,
+                                                  char             **data_return);
+dbus_bool_t   _dbus_string_steal_data_len        (DBusString        *str,
+                                                  char             **data_return,
+                                                  int                start,
+                                                  int                len);
+dbus_bool_t   _dbus_string_copy_data             (const DBusString  *str,
+                                                  char             **data_return);
+dbus_bool_t   _dbus_string_copy_data_len         (const DBusString  *str,
+                                                  char             **data_return,
+                                                  int                start,
+                                                  int                len);
+void          _dbus_string_copy_to_buffer        (const DBusString  *str,
+                                                  char              *buffer,
+                                                 int                len);
+void          _dbus_string_copy_to_buffer_with_nul (const DBusString  *str,
+                                                    char              *buffer,
+                                                    int                avail_len);
+#ifndef _dbus_string_get_length
+int           _dbus_string_get_length            (const DBusString  *str);
+#endif /* !_dbus_string_get_length */
+
+dbus_bool_t   _dbus_string_lengthen              (DBusString        *str,
+                                                  int                additional_length);
+void          _dbus_string_shorten               (DBusString        *str,
+                                                  int                length_to_remove);
+dbus_bool_t   _dbus_string_set_length            (DBusString        *str,
+                                                  int                length);
+dbus_bool_t   _dbus_string_align_length          (DBusString        *str,
+                                                  int                alignment);
+dbus_bool_t   _dbus_string_alloc_space           (DBusString        *str,
+                                                  int                extra_bytes);
+dbus_bool_t   _dbus_string_append                (DBusString        *str,
+                                                  const char        *buffer);
+dbus_bool_t   _dbus_string_append_len            (DBusString        *str,
+                                                  const char        *buffer,
+                                                  int                len);
+dbus_bool_t   _dbus_string_append_int            (DBusString        *str,
+                                                  long               value);
+dbus_bool_t   _dbus_string_append_uint           (DBusString        *str,
+                                                  unsigned long      value);
+dbus_bool_t   _dbus_string_append_double         (DBusString        *str,
+                                                  double             value);
+dbus_bool_t   _dbus_string_append_byte           (DBusString        *str,
+                                                  unsigned char      byte);
+dbus_bool_t   _dbus_string_append_unichar        (DBusString        *str,
+                                                  dbus_unichar_t     ch);
+dbus_bool_t   _dbus_string_append_4_aligned      (DBusString        *str,
+                                                  const unsigned char octets[4]);
+dbus_bool_t   _dbus_string_append_8_aligned      (DBusString        *str,
+                                                  const unsigned char octets[8]);
+dbus_bool_t   _dbus_string_append_printf         (DBusString        *str,
+                                                  const char        *format,
+                                                  ...) _DBUS_GNUC_PRINTF (2, 3);
+dbus_bool_t   _dbus_string_append_printf_valist  (DBusString        *str,
+                                                  const char        *format,
+                                                  va_list            args);
+dbus_bool_t   _dbus_string_insert_2_aligned      (DBusString        *str,
+                                                  int                insert_at,
+                                                  const unsigned char octets[2]);
+dbus_bool_t   _dbus_string_insert_4_aligned      (DBusString        *str,
+                                                  int                insert_at,
+                                                  const unsigned char octets[4]);
+dbus_bool_t   _dbus_string_insert_8_aligned      (DBusString        *str,
+                                                  int                insert_at,
+                                                  const unsigned char octets[8]);
+dbus_bool_t   _dbus_string_insert_alignment      (DBusString        *str,
+                                                  int               *insert_at,
+                                                  int                alignment);
+void          _dbus_string_delete                (DBusString        *str,
+                                                  int                start,
+                                                  int                len);
+dbus_bool_t   _dbus_string_move                  (DBusString        *source,
+                                                  int                start,
+                                                  DBusString        *dest,
+                                                  int                insert_at);
+dbus_bool_t   _dbus_string_copy                  (const DBusString  *source,
+                                                  int                start,
+                                                  DBusString        *dest,
+                                                  int                insert_at);
+dbus_bool_t   _dbus_string_move_len              (DBusString        *source,
+                                                  int                start,
+                                                  int                len,
+                                                  DBusString        *dest,
+                                                  int                insert_at);
+dbus_bool_t   _dbus_string_copy_len              (const DBusString  *source,
+                                                  int                start,
+                                                  int                len,
+                                                  DBusString        *dest,
+                                                  int                insert_at);
+dbus_bool_t   _dbus_string_replace_len           (const DBusString  *source,
+                                                  int                start,
+                                                  int                len,
+                                                  DBusString        *dest,
+                                                  int                replace_at,
+                                                  int                replace_len);
+dbus_bool_t   _dbus_string_split_on_byte         (DBusString        *source,
+                                                  unsigned char      byte,
+                                                  DBusString        *tail);
+void          _dbus_string_get_unichar           (const DBusString  *str,
+                                                  int                start,
+                                                  dbus_unichar_t    *ch_return,
+                                                  int               *end_return);
+dbus_bool_t   _dbus_string_parse_int             (const DBusString  *str,
+                                                  int                start,
+                                                  long              *value_return,
+                                                  int               *end_return);
+dbus_bool_t   _dbus_string_parse_uint            (const DBusString  *str,
+                                                  int                start,
+                                                  unsigned long     *value_return,
+                                                  int               *end_return);
+dbus_bool_t   _dbus_string_parse_double          (const DBusString  *str,
+                                                  int                start,
+                                                  double            *value,
+                                                  int               *end_return);
+dbus_bool_t   _dbus_string_find                  (const DBusString  *str,
+                                                  int                start,
+                                                  const char        *substr,
+                                                  int               *found);
+dbus_bool_t   _dbus_string_find_eol               (const DBusString *str,
+                                                  int               start,
+                                                  int               *found,
+                                                  int               *found_len);
+dbus_bool_t   _dbus_string_find_to               (const DBusString  *str,
+                                                  int                start,
+                                                  int                end,
+                                                  const char        *substr,
+                                                  int               *found);
+dbus_bool_t   _dbus_string_find_byte_backward    (const DBusString  *str,
+                                                  int                start,
+                                                  unsigned char      byte,
+                                                  int               *found);
+dbus_bool_t   _dbus_string_find_blank            (const DBusString  *str,
+                                                  int                start,
+                                                  int               *found);
+void          _dbus_string_skip_blank            (const DBusString  *str,
+                                                  int                start,
+                                                  int               *end);
+void          _dbus_string_skip_white            (const DBusString  *str,
+                                                  int                start,
+                                                  int               *end);
+void          _dbus_string_skip_white_reverse    (const DBusString  *str,
+                                                  int                end,
+                                                  int               *start);
+dbus_bool_t   _dbus_string_equal                 (const DBusString  *a,
+                                                  const DBusString  *b);
+dbus_bool_t   _dbus_string_equal_c_str           (const DBusString  *a,
+                                                  const char        *c_str);
+dbus_bool_t   _dbus_string_equal_len             (const DBusString  *a,
+                                                  const DBusString  *b,
+                                                  int                len);
+dbus_bool_t   _dbus_string_equal_substring       (const DBusString  *a,
+                                                  int                a_start,
+                                                  int                a_len,
+                                                  const DBusString  *b,
+                                                  int                b_start);
+dbus_bool_t   _dbus_string_starts_with_c_str     (const DBusString  *a,
+                                                  const char        *c_str);
+dbus_bool_t   _dbus_string_ends_with_c_str       (const DBusString  *a,
+                                                  const char        *c_str);
+dbus_bool_t   _dbus_string_pop_line              (DBusString        *source,
+                                                  DBusString        *dest);
+void          _dbus_string_delete_first_word     (DBusString        *str);
+void          _dbus_string_delete_leading_blanks (DBusString        *str);
+void          _dbus_string_chop_white            (DBusString        *str); 
+dbus_bool_t   _dbus_string_append_byte_as_hex    (DBusString        *str,
+                                                  int                byte);
+dbus_bool_t   _dbus_string_hex_encode            (const DBusString  *source,
+                                                  int                start,
+                                                  DBusString        *dest,
+                                                  int                insert_at);
+dbus_bool_t   _dbus_string_hex_decode            (const DBusString  *source,
+                                                  int                start,
+                                                 int               *end_return,
+                                                  DBusString        *dest,
+                                                  int                insert_at);
+dbus_bool_t   _dbus_string_validate_ascii        (const DBusString  *str,
+                                                  int                start,
+                                                  int                len);
+dbus_bool_t   _dbus_string_validate_utf8         (const DBusString  *str,
+                                                  int                start,
+                                                  int                len);
+dbus_bool_t   _dbus_string_validate_nul          (const DBusString  *str,
+                                                  int                start,
+                                                  int                len);
+void          _dbus_string_zero                  (DBusString        *str);
+
+
+/**
+ * We allocate 1 byte for nul termination, plus 7 bytes for possible
+ * align_offset, so we always need 8 bytes on top of the string's
+ * length to be in the allocated block.
+ */
+#define _DBUS_STRING_ALLOCATION_PADDING 8
+
+/**
+ * Defines a static const variable with type #DBusString called "name"
+ * containing the given string literal.
+ *
+ * @param name the name of the variable
+ * @param str the string value
+ */
+#define _DBUS_STRING_DEFINE_STATIC(name, str)                           \
+  static const char _dbus_static_string_##name[] = str;                 \
+  static const DBusString name = { _dbus_static_string_##name,          \
+                                   sizeof(_dbus_static_string_##name),  \
+                                   sizeof(_dbus_static_string_##name) + \
+                                   _DBUS_STRING_ALLOCATION_PADDING,     \
+                                   sizeof(_dbus_static_string_##name),  \
+                                   TRUE, TRUE, FALSE, 0 }
+
+DBUS_END_DECLS
+
+#endif /* DBUS_STRING_H */
diff --git a/src/dbus/dbus-sysdeps-pthread.c b/src/dbus/dbus-sysdeps-pthread.c
new file mode 100644 (file)
index 0000000..121ee12
--- /dev/null
@@ -0,0 +1,324 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sysdeps-pthread.c Implements threads using pthreads (internal to libdbus)
+ * 
+ * Copyright (C) 2002, 2003, 2006  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-internals.h"
+#include "dbus-sysdeps.h"
+#include "dbus-threads.h"
+
+#include <sys/time.h>
+#include <pthread.h>
+#include <string.h>
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+typedef struct {
+  pthread_mutex_t lock; /**< lock protecting count field */
+  volatile int count;   /**< count of how many times lock holder has recursively locked */
+  volatile pthread_t holder; /**< holder of the lock if count >0,
+                                valid but arbitrary thread if count
+                                has ever been >0, uninitialized memory
+                                if count has never been >0 */
+} DBusMutexPThread;
+
+typedef struct {
+  pthread_cond_t cond; /**< the condition */
+} DBusCondVarPThread;
+
+#define DBUS_MUTEX(m)         ((DBusMutex*) m)
+#define DBUS_MUTEX_PTHREAD(m) ((DBusMutexPThread*) m)
+
+#define DBUS_COND_VAR(c)         ((DBusCondVar*) c)
+#define DBUS_COND_VAR_PTHREAD(c) ((DBusCondVarPThread*) c)
+
+
+#ifdef DBUS_DISABLE_ASSERT
+/* (tmp != 0) is a no-op usage to silence compiler */
+#define PTHREAD_CHECK(func_name, result_or_call)    \
+    do { int tmp = (result_or_call); if (tmp != 0) {;} } while (0)
+#else
+#define PTHREAD_CHECK(func_name, result_or_call) do {                                  \
+    int tmp = (result_or_call);                                                        \
+    if (tmp != 0) {                                                                    \
+      _dbus_warn_check_failed ("pthread function %s failed with %d %s in %s\n",        \
+                               func_name, tmp, strerror(tmp), _DBUS_FUNCTION_NAME);    \
+    }                                                                                  \
+} while (0)
+#endif /* !DBUS_DISABLE_ASSERT */
+
+static DBusMutex*
+_dbus_pthread_mutex_new (void)
+{
+  DBusMutexPThread *pmutex;
+  int result;
+  
+  pmutex = dbus_new (DBusMutexPThread, 1);
+  if (pmutex == NULL)
+    return NULL;
+
+  result = pthread_mutex_init (&pmutex->lock, NULL);
+
+  if (result == ENOMEM || result == EAGAIN)
+    {
+      dbus_free (pmutex);
+      return NULL;
+    }
+  else
+    {
+      PTHREAD_CHECK ("pthread_mutex_init", result);
+    }
+
+  /* Only written */
+  pmutex->count = 0;
+
+  /* There's no portable way to have a "null" pthread afaik so we
+   * can't set pmutex->holder to anything sensible.  We only access it
+   * once the lock is held (which means we've set it).
+   */
+  
+  return DBUS_MUTEX (pmutex);
+}
+
+static void
+_dbus_pthread_mutex_free (DBusMutex *mutex)
+{
+  DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex);
+
+  _dbus_assert (pmutex->count == 0);
+  
+  PTHREAD_CHECK ("pthread_mutex_destroy", pthread_mutex_destroy (&pmutex->lock));
+
+  dbus_free (pmutex);
+}
+
+static void
+_dbus_pthread_mutex_lock (DBusMutex *mutex)
+{
+  DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex);
+  pthread_t self = pthread_self ();
+
+  /* If the count is > 0 then someone had the lock, maybe us. If it is
+   * 0, then it might immediately change right after we read it,
+   * but it will be changed by another thread; i.e. if we read 0,
+   * we assume that this thread doesn't have the lock.
+   *
+   * Not 100% sure this is safe, but ... seems like it should be.
+   */
+  if (pmutex->count == 0)
+    {
+      /* We know we don't have the lock; someone may have the lock. */
+      
+      PTHREAD_CHECK ("pthread_mutex_lock", pthread_mutex_lock (&pmutex->lock));
+
+      /* We now have the lock. Count must be 0 since it must be 0 when
+       * the lock is released by another thread, and we just now got
+       * the lock.
+       */
+      _dbus_assert (pmutex->count == 0);
+      
+      pmutex->holder = self;
+      pmutex->count = 1;
+    }
+  else
+    {
+      /* We know someone had the lock, possibly us. Thus
+       * pmutex->holder is not pointing to junk, though it may not be
+       * the lock holder anymore if the lock holder is not us.  If the
+       * lock holder is us, then we definitely have the lock.
+       */
+
+      if (pthread_equal (pmutex->holder, self))
+        {
+          /* We already have the lock. */
+          _dbus_assert (pmutex->count > 0);
+        }
+      else
+        {
+          /* Wait for the lock */
+          PTHREAD_CHECK ("pthread_mutex_lock", pthread_mutex_lock (&pmutex->lock));
+         pmutex->holder = self;
+          _dbus_assert (pmutex->count == 0);
+        }
+
+      pmutex->count += 1;
+    }
+}
+
+static void
+_dbus_pthread_mutex_unlock (DBusMutex *mutex)
+{
+  DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex);
+
+  _dbus_assert (pmutex->count > 0);
+  
+  pmutex->count -= 1;
+
+  if (pmutex->count == 0)
+    PTHREAD_CHECK ("pthread_mutex_unlock", pthread_mutex_unlock (&pmutex->lock));
+  
+  /* We leave pmutex->holder set to ourselves, its content is undefined if count is 0 */
+}
+
+static DBusCondVar *
+_dbus_pthread_condvar_new (void)
+{
+  DBusCondVarPThread *pcond;
+  int result;
+  
+  pcond = dbus_new (DBusCondVarPThread, 1);
+  if (pcond == NULL)
+    return NULL;
+
+  result = pthread_cond_init (&pcond->cond, NULL);
+
+  if (result == EAGAIN || result == ENOMEM)
+    {
+      dbus_free (pcond);
+      return NULL;
+    }
+  else
+    {
+      PTHREAD_CHECK ("pthread_cond_init", result);
+    }
+  
+  return DBUS_COND_VAR (pcond);
+}
+
+static void
+_dbus_pthread_condvar_free (DBusCondVar *cond)
+{  
+  DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond);
+  
+  PTHREAD_CHECK ("pthread_cond_destroy", pthread_cond_destroy (&pcond->cond));
+
+  dbus_free (pcond);
+}
+
+static void
+_dbus_pthread_condvar_wait (DBusCondVar *cond,
+                            DBusMutex   *mutex)
+{
+  DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex);
+  DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond);
+  int old_count;
+  
+  _dbus_assert (pmutex->count > 0);
+  _dbus_assert (pthread_equal (pmutex->holder, pthread_self ()));
+
+  old_count = pmutex->count;
+  pmutex->count = 0;           /* allow other threads to lock */
+  PTHREAD_CHECK ("pthread_cond_wait", pthread_cond_wait (&pcond->cond, &pmutex->lock));
+  _dbus_assert (pmutex->count == 0);
+  pmutex->count = old_count;
+  pmutex->holder = pthread_self(); /* other threads may have locked the mutex in the meantime */
+}
+
+static dbus_bool_t
+_dbus_pthread_condvar_wait_timeout (DBusCondVar               *cond,
+                                    DBusMutex                 *mutex,
+                                    int                        timeout_milliseconds)
+{
+  DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex);
+  DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond);
+  struct timeval time_now;
+  struct timespec end_time;
+  int result;
+  int old_count;
+  
+  _dbus_assert (pmutex->count > 0);
+  _dbus_assert (pthread_equal (pmutex->holder, pthread_self ()));  
+  
+  gettimeofday (&time_now, NULL);
+  
+  end_time.tv_sec = time_now.tv_sec + timeout_milliseconds / 1000;
+  end_time.tv_nsec = (time_now.tv_usec + (timeout_milliseconds % 1000) * 1000) * 1000;
+  if (end_time.tv_nsec > 1000*1000*1000)
+    {
+      end_time.tv_sec += 1;
+      end_time.tv_nsec -= 1000*1000*1000;
+    }
+
+  old_count = pmutex->count;
+  pmutex->count = 0;
+  result = pthread_cond_timedwait (&pcond->cond, &pmutex->lock, &end_time);
+  
+  if (result != ETIMEDOUT)
+    {
+      PTHREAD_CHECK ("pthread_cond_timedwait", result);
+    }
+
+  _dbus_assert (pmutex->count == 0);
+  pmutex->count = old_count;
+  pmutex->holder = pthread_self(); /* other threads may have locked the mutex in the meantime */
+  
+  /* return true if we did not time out */
+  return result != ETIMEDOUT;
+}
+
+static void
+_dbus_pthread_condvar_wake_one (DBusCondVar *cond)
+{
+  DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond);
+
+  PTHREAD_CHECK ("pthread_cond_signal", pthread_cond_signal (&pcond->cond));
+}
+
+static void
+_dbus_pthread_condvar_wake_all (DBusCondVar *cond)
+{
+  DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond);
+  
+  PTHREAD_CHECK ("pthread_cond_broadcast", pthread_cond_broadcast (&pcond->cond));
+}
+
+static const DBusThreadFunctions pthread_functions =
+{
+  DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_NEW_MASK |
+  DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_FREE_MASK |
+  DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_LOCK_MASK |
+  DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_UNLOCK_MASK |
+  DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK |
+  DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK |
+  DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK |
+  DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK |
+  DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK|
+  DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK,
+  NULL, NULL, NULL, NULL,
+  _dbus_pthread_condvar_new,
+  _dbus_pthread_condvar_free,
+  _dbus_pthread_condvar_wait,
+  _dbus_pthread_condvar_wait_timeout,
+  _dbus_pthread_condvar_wake_one,
+  _dbus_pthread_condvar_wake_all,
+  _dbus_pthread_mutex_new,
+  _dbus_pthread_mutex_free,
+  _dbus_pthread_mutex_lock,
+  _dbus_pthread_mutex_unlock
+};
+
+dbus_bool_t
+_dbus_threads_init_platform_specific (void)
+{
+  return dbus_threads_init (&pthread_functions);
+}
diff --git a/src/dbus/dbus-sysdeps-unix.c b/src/dbus/dbus-sysdeps-unix.c
new file mode 100644 (file)
index 0000000..ccb8483
--- /dev/null
@@ -0,0 +1,3356 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sysdeps-unix.c Wrappers around UNIX system/libc features (internal to D-Bus implementation)
+ * 
+ * Copyright (C) 2002, 2003, 2006  Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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
+ *
+ */
+
+#define _GNU_SOURCE 
+
+#include "dbus-internals.h"
+#include "dbus-sysdeps.h"
+#include "dbus-sysdeps-unix.h"
+#include "dbus-threads.h"
+#include "dbus-protocol.h"
+#include "dbus-transport.h"
+#include "dbus-string.h"
+#include "dbus-userdb.h"
+#include "dbus-list.h"
+#include "dbus-credentials.h"
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <dirent.h>
+#include <sys/un.h>
+#include <pwd.h>
+#include <time.h>
+#include <locale.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <grp.h>
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_WRITEV
+#include <sys/uio.h>
+#endif
+#ifdef HAVE_POLL
+#include <sys/poll.h>
+#endif
+#ifdef HAVE_BACKTRACE
+#include <execinfo.h>
+#endif
+#ifdef HAVE_GETPEERUCRED
+#include <ucred.h>
+#endif
+
+#ifdef HAVE_ADT
+#include <bsm/adt.h>
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#ifndef AI_ADDRCONFIG
+#define AI_ADDRCONFIG 0
+#endif
+
+#ifndef HAVE_SOCKLEN_T
+#define socklen_t int
+#endif
+
+static dbus_bool_t
+_dbus_open_socket (int              *fd_p,
+                   int               domain,
+                   int               type,
+                   int               protocol,
+                   DBusError        *error)
+{
+  *fd_p = socket (domain, type, protocol);
+  if (*fd_p >= 0)
+    {
+      _dbus_verbose ("socket fd %d opened\n", *fd_p);
+      return TRUE;
+    }
+  else
+    {
+      dbus_set_error(error,
+                     _dbus_error_from_errno (errno),
+                     "Failed to open socket: %s",
+                     _dbus_strerror (errno));
+      return FALSE;
+    }
+}
+
+dbus_bool_t
+_dbus_open_tcp_socket (int              *fd,
+                       DBusError        *error)
+{
+  return _dbus_open_socket(fd, AF_INET, SOCK_STREAM, 0, error);
+}
+
+/**
+ * Opens a UNIX domain socket (as in the socket() call).
+ * Does not bind the socket.
+ * @param fd return location for socket descriptor
+ * @param error return location for an error
+ * @returns #FALSE if error is set
+ */
+dbus_bool_t
+_dbus_open_unix_socket (int              *fd,
+                        DBusError        *error)
+{
+  return _dbus_open_socket(fd, PF_UNIX, SOCK_STREAM, 0, error);
+}
+
+/**
+ * Closes a socket. Should not be used on non-socket
+ * file descriptors or handles.
+ *
+ * @param fd the socket
+ * @param error return location for an error
+ * @returns #FALSE if error is set
+ */
+dbus_bool_t 
+_dbus_close_socket (int               fd,
+                    DBusError        *error)
+{
+  return _dbus_close (fd, error);
+}
+
+/**
+ * Like _dbus_read(), but only works on sockets so is
+ * available on Windows.
+ *
+ * @param fd the socket
+ * @param buffer string to append data to
+ * @param count max amount of data to read
+ * @returns number of bytes appended to the string
+ */
+int
+_dbus_read_socket (int               fd,
+                   DBusString       *buffer,
+                   int               count)
+{
+  return _dbus_read (fd, buffer, count);
+}
+
+/**
+ * Like _dbus_write(), but only supports sockets
+ * and is thus available on Windows.
+ *
+ * @param fd the file descriptor to write
+ * @param buffer the buffer to write data from
+ * @param start the first byte in the buffer to write
+ * @param len the number of bytes to try to write
+ * @returns the number of bytes written or -1 on error
+ */
+int
+_dbus_write_socket (int               fd,
+                    const DBusString *buffer,
+                    int               start,
+                    int               len)
+{
+  return _dbus_write (fd, buffer, start, len);
+}
+
+/**
+ * write data to a pipe.
+ *
+ * @param pipe the pipe instance
+ * @param buffer the buffer to write data from
+ * @param start the first byte in the buffer to write
+ * @param len the number of bytes to try to write
+ * @param error error return
+ * @returns the number of bytes written or -1 on error
+ */
+int
+_dbus_pipe_write (DBusPipe         *pipe,
+                  const DBusString *buffer,
+                  int               start,
+                  int               len,
+                  DBusError        *error)
+{
+  int written;
+  
+  written = _dbus_write (pipe->fd_or_handle, buffer, start, len);
+  if (written < 0)
+    {
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                      "Writing to pipe: %s\n",
+                      _dbus_strerror (errno));
+    }
+  return written;
+}
+
+/**
+ * close a pipe.
+ *
+ * @param pipe the pipe instance
+ * @param error return location for an error
+ * @returns #FALSE if error is set
+ */
+int
+_dbus_pipe_close  (DBusPipe         *pipe,
+                   DBusError        *error)
+{
+  if (_dbus_close (pipe->fd_or_handle, error) < 0)
+    {
+      return -1;
+    }
+  else
+    {
+      _dbus_pipe_invalidate (pipe);
+      return 0;
+    }
+}
+
+/**
+ * Like _dbus_write_two() but only works on sockets and is thus
+ * available on Windows.
+ * 
+ * @param fd the file descriptor
+ * @param buffer1 first buffer
+ * @param start1 first byte to write in first buffer
+ * @param len1 number of bytes to write from first buffer
+ * @param buffer2 second buffer, or #NULL
+ * @param start2 first byte to write in second buffer
+ * @param len2 number of bytes to write in second buffer
+ * @returns total bytes written from both buffers, or -1 on error
+ */
+int
+_dbus_write_socket_two (int               fd,
+                        const DBusString *buffer1,
+                        int               start1,
+                        int               len1,
+                        const DBusString *buffer2,
+                        int               start2,
+                        int               len2)
+{
+  return _dbus_write_two (fd, buffer1, start1, len1,
+                          buffer2, start2, len2);
+}
+
+
+/**
+ * Thin wrapper around the read() system call that appends
+ * the data it reads to the DBusString buffer. It appends
+ * up to the given count, and returns the same value
+ * and same errno as read(). The only exception is that
+ * _dbus_read() handles EINTR for you. Also, _dbus_read() can
+ * return ENOMEM, even though regular UNIX read doesn't.
+ *
+ * Unlike _dbus_read_socket(), _dbus_read() is not available
+ * on Windows.
+ * 
+ * @param fd the file descriptor to read from
+ * @param buffer the buffer to append data to
+ * @param count the amount of data to read
+ * @returns the number of bytes read or -1
+ */
+int
+_dbus_read (int               fd,
+            DBusString       *buffer,
+            int               count)
+{
+  int bytes_read;
+  int start;
+  char *data;
+
+  _dbus_assert (count >= 0);
+  
+  start = _dbus_string_get_length (buffer);
+
+  if (!_dbus_string_lengthen (buffer, count))
+    {
+      errno = ENOMEM;
+      return -1;
+    }
+
+  data = _dbus_string_get_data_len (buffer, start, count);
+
+ again:
+  
+  bytes_read = read (fd, data, count);
+
+  if (bytes_read < 0)
+    {
+      if (errno == EINTR)
+        goto again;
+      else
+        {
+          /* put length back (note that this doesn't actually realloc anything) */
+          _dbus_string_set_length (buffer, start);
+          return -1;
+        }
+    }
+  else
+    {
+      /* put length back (doesn't actually realloc) */
+      _dbus_string_set_length (buffer, start + bytes_read);
+
+#if 0
+      if (bytes_read > 0)
+        _dbus_verbose_bytes_of_string (buffer, start, bytes_read);
+#endif
+      
+      return bytes_read;
+    }
+}
+
+/**
+ * Thin wrapper around the write() system call that writes a part of a
+ * DBusString and handles EINTR for you.
+ * 
+ * @param fd the file descriptor to write
+ * @param buffer the buffer to write data from
+ * @param start the first byte in the buffer to write
+ * @param len the number of bytes to try to write
+ * @returns the number of bytes written or -1 on error
+ */
+int
+_dbus_write (int               fd,
+             const DBusString *buffer,
+             int               start,
+             int               len)
+{
+  const char *data;
+  int bytes_written;
+  
+  data = _dbus_string_get_const_data_len (buffer, start, len);
+  
+ again:
+
+  bytes_written = write (fd, data, len);
+
+  if (bytes_written < 0 && errno == EINTR)
+    goto again;
+
+#if 0
+  if (bytes_written > 0)
+    _dbus_verbose_bytes_of_string (buffer, start, bytes_written);
+#endif
+  
+  return bytes_written;
+}
+
+/**
+ * Like _dbus_write() but will use writev() if possible
+ * to write both buffers in sequence. The return value
+ * is the number of bytes written in the first buffer,
+ * plus the number written in the second. If the first
+ * buffer is written successfully and an error occurs
+ * writing the second, the number of bytes in the first
+ * is returned (i.e. the error is ignored), on systems that
+ * don't have writev. Handles EINTR for you.
+ * The second buffer may be #NULL.
+ *
+ * @param fd the file descriptor
+ * @param buffer1 first buffer
+ * @param start1 first byte to write in first buffer
+ * @param len1 number of bytes to write from first buffer
+ * @param buffer2 second buffer, or #NULL
+ * @param start2 first byte to write in second buffer
+ * @param len2 number of bytes to write in second buffer
+ * @returns total bytes written from both buffers, or -1 on error
+ */
+int
+_dbus_write_two (int               fd,
+                 const DBusString *buffer1,
+                 int               start1,
+                 int               len1,
+                 const DBusString *buffer2,
+                 int               start2,
+                 int               len2)
+{
+  _dbus_assert (buffer1 != NULL);
+  _dbus_assert (start1 >= 0);
+  _dbus_assert (start2 >= 0);
+  _dbus_assert (len1 >= 0);
+  _dbus_assert (len2 >= 0);
+  
+#ifdef HAVE_WRITEV
+  {
+    struct iovec vectors[2];
+    const char *data1;
+    const char *data2;
+    int bytes_written;
+
+    data1 = _dbus_string_get_const_data_len (buffer1, start1, len1);
+
+    if (buffer2 != NULL)
+      data2 = _dbus_string_get_const_data_len (buffer2, start2, len2);
+    else
+      {
+        data2 = NULL;
+        start2 = 0;
+        len2 = 0;
+      }
+   
+    vectors[0].iov_base = (char*) data1;
+    vectors[0].iov_len = len1;
+    vectors[1].iov_base = (char*) data2;
+    vectors[1].iov_len = len2;
+
+  again:
+   
+    bytes_written = writev (fd,
+                            vectors,
+                            data2 ? 2 : 1);
+
+    if (bytes_written < 0 && errno == EINTR)
+      goto again;
+   
+    return bytes_written;
+  }
+#else /* HAVE_WRITEV */
+  {
+    int ret1;
+    
+    ret1 = _dbus_write (fd, buffer1, start1, len1);
+    if (ret1 == len1 && buffer2 != NULL)
+      {
+        ret2 = _dbus_write (fd, buffer2, start2, len2);
+        if (ret2 < 0)
+          ret2 = 0; /* we can't report an error as the first write was OK */
+       
+        return ret1 + ret2;
+      }
+    else
+      return ret1;
+  }
+#endif /* !HAVE_WRITEV */   
+}
+
+#define _DBUS_MAX_SUN_PATH_LENGTH 99
+
+/**
+ * @def _DBUS_MAX_SUN_PATH_LENGTH
+ *
+ * Maximum length of the path to a UNIX domain socket,
+ * sockaddr_un::sun_path member. POSIX requires that all systems
+ * support at least 100 bytes here, including the nul termination.
+ * We use 99 for the max value to allow for the nul.
+ *
+ * We could probably also do sizeof (addr.sun_path)
+ * but this way we are the same on all platforms
+ * which is probably a good idea.
+ */
+
+/**
+ * Creates a socket and connects it to the UNIX domain socket at the
+ * given path.  The connection fd is returned, and is set up as
+ * nonblocking.
+ * 
+ * Uses abstract sockets instead of filesystem-linked sockets if
+ * requested (it's possible only on Linux; see "man 7 unix" on Linux).
+ * On non-Linux abstract socket usage always fails.
+ *
+ * @param path the path to UNIX domain socket
+ * @param abstract #TRUE to use abstract namespace
+ * @param error return location for error code
+ * @returns connection file descriptor or -1 on error
+ */
+int
+_dbus_connect_unix_socket (const char     *path,
+                           dbus_bool_t     abstract,
+                           DBusError      *error)
+{
+  int fd;
+  size_t path_len;
+  struct sockaddr_un addr;  
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  _dbus_verbose ("connecting to unix socket %s abstract=%d\n",
+                 path, abstract);
+  
+  
+  if (!_dbus_open_unix_socket (&fd, error))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET(error);
+      return -1;
+    }
+  _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+
+  _DBUS_ZERO (addr);
+  addr.sun_family = AF_UNIX;
+  path_len = strlen (path);
+
+  if (abstract)
+    {
+#ifdef HAVE_ABSTRACT_SOCKETS
+      addr.sun_path[0] = '\0'; /* this is what says "use abstract" */
+      path_len++; /* Account for the extra nul byte added to the start of sun_path */
+
+      if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
+        {
+          dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+                      "Abstract socket name too long\n");
+          _dbus_close (fd, NULL);
+          return -1;
+       }
+       
+      strncpy (&addr.sun_path[1], path, path_len);
+      /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */
+#else /* HAVE_ABSTRACT_SOCKETS */
+      dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED,
+                      "Operating system does not support abstract socket namespace\n");
+      _dbus_close (fd, NULL);
+      return -1;
+#endif /* ! HAVE_ABSTRACT_SOCKETS */
+    }
+  else
+    {
+      if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
+        {
+          dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+                      "Socket name too long\n");
+          _dbus_close (fd, NULL);
+          return -1;
+       }
+
+      strncpy (addr.sun_path, path, path_len);
+    }
+  
+  if (connect (fd, (struct sockaddr*) &addr, _DBUS_STRUCT_OFFSET (struct sockaddr_un, sun_path) + path_len) < 0)
+    {      
+      dbus_set_error (error,
+                      _dbus_error_from_errno (errno),
+                      "Failed to connect to socket %s: %s",
+                      path, _dbus_strerror (errno));
+
+      _dbus_close (fd, NULL);
+      fd = -1;
+      
+      return -1;
+    }
+
+  if (!_dbus_set_fd_nonblocking (fd, error))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      
+      _dbus_close (fd, NULL);
+      fd = -1;
+
+      return -1;
+    }
+
+  return fd;
+}
+
+/**
+ * Enables or disables the reception of credentials on the given socket during
+ * the next message transmission.  This is only effective if the #LOCAL_CREDS
+ * system feature exists, in which case the other side of the connection does
+ * not have to do anything special to send the credentials.
+ *
+ * @param fd socket on which to change the #LOCAL_CREDS flag.
+ * @param on whether to enable or disable the #LOCAL_CREDS flag.
+ */
+static dbus_bool_t
+_dbus_set_local_creds (int fd, dbus_bool_t on)
+{
+  dbus_bool_t retval = TRUE;
+
+#if defined(HAVE_CMSGCRED)
+  /* NOOP just to make sure only one codepath is used 
+   *      and to prefer CMSGCRED
+   */
+#elif defined(LOCAL_CREDS) 
+  int val = on ? 1 : 0;
+  if (setsockopt (fd, 0, LOCAL_CREDS, &val, sizeof (val)) < 0)
+    {
+      _dbus_verbose ("Unable to set LOCAL_CREDS socket option on fd %d\n", fd);
+      retval = FALSE;
+    }
+  else
+    _dbus_verbose ("LOCAL_CREDS %s for further messages on fd %d\n",
+                   on ? "enabled" : "disabled", fd);
+#endif
+
+  return retval;
+}
+
+/**
+ * Creates a socket and binds it to the given path,
+ * then listens on the socket. The socket is
+ * set to be nonblocking.
+ *
+ * Uses abstract sockets instead of filesystem-linked
+ * sockets if requested (it's possible only on Linux;
+ * see "man 7 unix" on Linux).
+ * On non-Linux abstract socket usage always fails.
+ *
+ * @param path the socket name
+ * @param abstract #TRUE to use abstract namespace
+ * @param error return location for errors
+ * @returns the listening file descriptor or -1 on error
+ */
+int
+_dbus_listen_unix_socket (const char     *path,
+                          dbus_bool_t     abstract,
+                          DBusError      *error)
+{
+  int listen_fd;
+  struct sockaddr_un addr;
+  size_t path_len;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  _dbus_verbose ("listening on unix socket %s abstract=%d\n",
+                 path, abstract);
+  
+  if (!_dbus_open_unix_socket (&listen_fd, error))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET(error);
+      return -1;
+    }
+  _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+
+  _DBUS_ZERO (addr);
+  addr.sun_family = AF_UNIX;
+  path_len = strlen (path);
+  
+  if (abstract)
+    {
+#ifdef HAVE_ABSTRACT_SOCKETS
+      /* remember that abstract names aren't nul-terminated so we rely
+       * on sun_path being filled in with zeroes above.
+       */
+      addr.sun_path[0] = '\0'; /* this is what says "use abstract" */
+      path_len++; /* Account for the extra nul byte added to the start of sun_path */
+
+      if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
+        {
+          dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+                      "Abstract socket name too long\n");
+          _dbus_close (listen_fd, NULL);
+          return -1;
+       }
+      
+      strncpy (&addr.sun_path[1], path, path_len);
+      /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */
+#else /* HAVE_ABSTRACT_SOCKETS */
+      dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED,
+                      "Operating system does not support abstract socket namespace\n");
+      _dbus_close (listen_fd, NULL);
+      return -1;
+#endif /* ! HAVE_ABSTRACT_SOCKETS */
+    }
+  else
+    {
+      /* Discussed security implications of this with Nalin,
+       * and we couldn't think of where it would kick our ass, but
+       * it still seems a bit sucky. It also has non-security suckage;
+       * really we'd prefer to exit if the socket is already in use.
+       * But there doesn't seem to be a good way to do this.
+       *
+       * Just to be extra careful, I threw in the stat() - clearly
+       * the stat() can't *fix* any security issue, but it at least
+       * avoids inadvertent/accidental data loss.
+       */
+      {
+        struct stat sb;
+
+        if (stat (path, &sb) == 0 &&
+            S_ISSOCK (sb.st_mode))
+          unlink (path);
+      }
+
+      if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
+        {
+          dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+                      "Abstract socket name too long\n");
+          _dbus_close (listen_fd, NULL);
+          return -1;
+       }
+       
+      strncpy (addr.sun_path, path, path_len);
+    }
+  
+  if (bind (listen_fd, (struct sockaddr*) &addr, _DBUS_STRUCT_OFFSET (struct sockaddr_un, sun_path) + path_len) < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to bind socket \"%s\": %s",
+                      path, _dbus_strerror (errno));
+      _dbus_close (listen_fd, NULL);
+      return -1;
+    }
+
+  if (listen (listen_fd, 30 /* backlog */) < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to listen on socket \"%s\": %s",
+                      path, _dbus_strerror (errno));
+      _dbus_close (listen_fd, NULL);
+      return -1;
+    }
+
+  if (!_dbus_set_local_creds (listen_fd, TRUE))
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to enable LOCAL_CREDS on socket \"%s\": %s",
+                      path, _dbus_strerror (errno));
+      close (listen_fd);
+      return -1;
+    }
+
+  if (!_dbus_set_fd_nonblocking (listen_fd, error))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      _dbus_close (listen_fd, NULL);
+      return -1;
+    }
+  
+  /* Try opening up the permissions, but if we can't, just go ahead
+   * and continue, maybe it will be good enough.
+   */
+  if (!abstract && chmod (path, 0777) < 0)
+    _dbus_warn ("Could not set mode 0777 on socket %s\n",
+                path);
+  
+  return listen_fd;
+}
+
+/**
+ * Creates a socket and connects to a socket at the given host 
+ * and port. The connection fd is returned, and is set up as
+ * nonblocking.
+ *
+ * @param host the host name to connect to
+ * @param port the port to connect to
+ * @param family the address family to listen on, NULL for all
+ * @param error return location for error code
+ * @returns connection file descriptor or -1 on error
+ */
+int
+_dbus_connect_tcp_socket (const char     *host,
+                          const char     *port,
+                          const char     *family,
+                          DBusError      *error)
+{
+  int fd = -1, res;
+  struct addrinfo hints;
+  struct addrinfo *ai, *tmp;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  if (!_dbus_open_tcp_socket (&fd, error))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET(error);
+      return -1;
+    }
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+
+  _DBUS_ZERO (hints);
+
+  if (!family)
+    hints.ai_family = AF_UNSPEC;
+  else if (!strcmp(family, "ipv4"))
+    hints.ai_family = AF_INET;
+  else if (!strcmp(family, "ipv6"))
+    hints.ai_family = AF_INET6;
+  else
+    {
+      dbus_set_error (error,
+                      _dbus_error_from_errno (errno),
+                      "Unknown address family %s", family);
+      return -1;
+    }
+  hints.ai_protocol = IPPROTO_TCP;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags = AI_ADDRCONFIG;
+
+  if ((res = getaddrinfo(host, port, &hints, &ai)) != 0)
+    {
+      dbus_set_error (error,
+                      _dbus_error_from_errno (errno),
+                      "Failed to lookup host/port: \"%s:%s\": %s (%d)",
+                      host, port, gai_strerror(res), res);
+      _dbus_close (fd, NULL);
+      return -1;
+    }
+
+  tmp = ai;
+  while (tmp)
+    {
+      if (!_dbus_open_socket (&fd, tmp->ai_family, SOCK_STREAM, 0, error))
+        {
+          freeaddrinfo(ai);
+          _DBUS_ASSERT_ERROR_IS_SET(error);
+          return -1;
+        }
+      _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+
+      if (connect (fd, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) < 0)
+        {
+          _dbus_close(fd, NULL);
+          fd = -1;
+          tmp = tmp->ai_next;
+          continue;
+        }
+
+      break;
+    }
+  freeaddrinfo(ai);
+
+  if (fd == -1)
+    {
+      dbus_set_error (error,
+                      _dbus_error_from_errno (errno),
+                      "Failed to connect to socket \"%s:%s\" %s",
+                      host, port, _dbus_strerror(errno));
+      return -1;
+    }
+
+
+  if (!_dbus_set_fd_nonblocking (fd, error))
+    {
+      _dbus_close (fd, NULL);
+      fd = -1;
+
+      return -1;
+    }
+
+  return fd;
+}
+
+/**
+ * Creates a socket and binds it to the given path, then listens on
+ * the socket. The socket is set to be nonblocking.  In case of port=0
+ * a random free port is used and returned in the port parameter.
+ * If inaddr_any is specified, the hostname is ignored.
+ *
+ * @param host the host name to listen on
+ * @param port the port to listen on, if zero a free port will be used
+ * @param family the address family to listen on, NULL for all
+ * @param retport string to return the actual port listened on
+ * @param fds_p location to store returned file descriptors
+ * @param error return location for errors
+ * @returns the number of listening file descriptors or -1 on error
+ */
+int
+_dbus_listen_tcp_socket (const char     *host,
+                         const char     *port,
+                         const char     *family,
+                         DBusString     *retport,
+                         int           **fds_p,
+                         DBusError      *error)
+{
+  int nlisten_fd = 0, *listen_fd = NULL, res, i;
+  struct addrinfo hints;
+  struct addrinfo *ai, *tmp;
+
+  *fds_p = NULL;
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  _DBUS_ZERO (hints);
+
+  if (!family)
+    hints.ai_family = AF_UNSPEC;
+  else if (!strcmp(family, "ipv4"))
+    hints.ai_family = AF_INET;
+  else if (!strcmp(family, "ipv6"))
+    hints.ai_family = AF_INET6;
+  else
+    {
+      dbus_set_error (error,
+                      _dbus_error_from_errno (errno),
+                      "Unknown address family %s", family);
+      return -1;
+    }
+
+  hints.ai_protocol = IPPROTO_TCP;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE;
+
+ redo_lookup_with_port:
+  if ((res = getaddrinfo(host, port, &hints, &ai)) != 0 || !ai)
+    {
+      dbus_set_error (error,
+                      _dbus_error_from_errno (errno),
+                      "Failed to lookup host/port: \"%s:%s\": %s (%d)",
+                      host ? host : "*", port, gai_strerror(res), res);
+      return -1;
+    }
+
+  tmp = ai;
+  while (tmp)
+    {
+      int fd = -1, *newlisten_fd;
+      if (!_dbus_open_socket (&fd, tmp->ai_family, SOCK_STREAM, 0, error))
+        {
+          _DBUS_ASSERT_ERROR_IS_SET(error);
+          goto failed;
+        }
+      _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+
+      if (bind (fd, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) < 0)
+        {
+          _dbus_close(fd, NULL);
+          if (errno == EADDRINUSE)
+            {
+              /* Depending on kernel policy, it may or may not
+                 be neccessary to bind to both IPv4 & 6 addresses
+                 so ignore EADDRINUSE here */
+              tmp = tmp->ai_next;
+              continue;
+            }
+          dbus_set_error (error, _dbus_error_from_errno (errno),
+                          "Failed to bind socket \"%s:%s\": %s",
+                          host ? host : "*", port, _dbus_strerror (errno));
+          goto failed;
+        }
+
+      if (listen (fd, 30 /* backlog */) < 0)
+        {
+          _dbus_close (fd, NULL);
+          dbus_set_error (error, _dbus_error_from_errno (errno),
+                          "Failed to listen on socket \"%s:%s\": %s",
+                          host ? host : "*", port, _dbus_strerror (errno));
+          goto failed;
+        }
+
+      newlisten_fd = dbus_realloc(listen_fd, sizeof(int)*(nlisten_fd+1));
+      if (!newlisten_fd)
+        {
+          _dbus_close (fd, NULL);
+          dbus_set_error (error, _dbus_error_from_errno (errno),
+                          "Failed to allocate file handle array: %s",
+                          _dbus_strerror (errno));
+          goto failed;
+        }
+      listen_fd = newlisten_fd;
+      listen_fd[nlisten_fd] = fd;
+      nlisten_fd++;
+
+      if (!_dbus_string_get_length(retport))
+        {
+          /* If the user didn't specify a port, or used 0, then
+             the kernel chooses a port. After the first address
+             is bound to, we need to force all remaining addresses
+             to use the same port */
+          if (!port || !strcmp(port, "0"))
+            {
+              struct sockaddr_storage addr;
+              socklen_t addrlen;
+              char portbuf[50];
+
+              addrlen = sizeof(addr);
+              getsockname(fd, (struct sockaddr*) &addr, &addrlen);
+
+              if ((res = getnameinfo((struct sockaddr*)&addr, addrlen, NULL, 0,
+                                     portbuf, sizeof(portbuf),
+                                     NI_NUMERICHOST)) != 0)
+                {
+                  dbus_set_error (error, _dbus_error_from_errno (errno),
+                                  "Failed to resolve port \"%s:%s\": %s (%s)",
+                                  host ? host : "*", port, gai_strerror(res), res);
+                  goto failed;
+                }
+              if (!_dbus_string_append(retport, portbuf))
+                {
+                  dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+                  goto failed;
+                }
+
+              /* Release current address list & redo lookup */
+              port = _dbus_string_get_const_data(retport);
+              freeaddrinfo(ai);
+              goto redo_lookup_with_port;
+            }
+          else
+            {
+              if (!_dbus_string_append(retport, port))
+                {
+                    dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+                    goto failed;
+                }
+            }
+        }
+
+      tmp = tmp->ai_next;
+    }
+  freeaddrinfo(ai);
+  ai = NULL;
+
+  if (!nlisten_fd)
+    {
+      errno = EADDRINUSE;
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to bind socket \"%s:%s\": %s",
+                      host ? host : "*", port, _dbus_strerror (errno));
+      return -1;
+    }
+
+  for (i = 0 ; i < nlisten_fd ; i++)
+    {
+      if (!_dbus_set_fd_nonblocking (listen_fd[i], error))
+        {
+          goto failed;
+        }
+    }
+
+  *fds_p = listen_fd;
+
+  return nlisten_fd;
+
+ failed:
+  if (ai)
+    freeaddrinfo(ai);
+  for (i = 0 ; i < nlisten_fd ; i++)
+    _dbus_close(listen_fd[i], NULL);
+  dbus_free(listen_fd);
+  return -1;
+}
+
+static dbus_bool_t
+write_credentials_byte (int             server_fd,
+                        DBusError      *error)
+{
+  int bytes_written;
+  char buf[1] = { '\0' };
+#if defined(HAVE_CMSGCRED) 
+  struct {
+         struct cmsghdr hdr;
+         struct cmsgcred cred;
+  } cmsg;
+  struct iovec iov;
+  struct msghdr msg;
+  iov.iov_base = buf;
+  iov.iov_len = 1;
+
+  memset (&msg, 0, sizeof (msg));
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+
+  msg.msg_control = &cmsg;
+  msg.msg_controllen = sizeof (cmsg);
+  memset (&cmsg, 0, sizeof (cmsg));
+  cmsg.hdr.cmsg_len = sizeof (cmsg);
+  cmsg.hdr.cmsg_level = SOL_SOCKET;
+  cmsg.hdr.cmsg_type = SCM_CREDS;
+#endif
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+ again:
+
+#if defined(HAVE_CMSGCRED) 
+  bytes_written = sendmsg (server_fd, &msg, 0);
+#else
+  bytes_written = write (server_fd, buf, 1);
+#endif
+
+  if (bytes_written < 0 && errno == EINTR)
+    goto again;
+
+  if (bytes_written < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to write credentials byte: %s",
+                     _dbus_strerror (errno));
+      return FALSE;
+    }
+  else if (bytes_written == 0)
+    {
+      dbus_set_error (error, DBUS_ERROR_IO_ERROR,
+                      "wrote zero bytes writing credentials byte");
+      return FALSE;
+    }
+  else
+    {
+      _dbus_assert (bytes_written == 1);
+      _dbus_verbose ("wrote credentials byte\n");
+      return TRUE;
+    }
+}
+
+/**
+ * Reads a single byte which must be nul (an error occurs otherwise),
+ * and reads unix credentials if available. Clears the credentials
+ * object, then adds pid/uid if available, so any previous credentials
+ * stored in the object are lost.
+ *
+ * Return value indicates whether a byte was read, not whether
+ * we got valid credentials. On some systems, such as Linux,
+ * reading/writing the byte isn't actually required, but we do it
+ * anyway just to avoid multiple codepaths.
+ * 
+ * Fails if no byte is available, so you must select() first.
+ *
+ * The point of the byte is that on some systems we have to
+ * use sendmsg()/recvmsg() to transmit credentials.
+ *
+ * @param client_fd the client file descriptor
+ * @param credentials object to add client credentials to
+ * @param error location to store error code
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_read_credentials_socket  (int              client_fd,
+                                DBusCredentials *credentials,
+                                DBusError       *error)
+{
+  struct msghdr msg;
+  struct iovec iov;
+  char buf;
+  dbus_uid_t uid_read;
+  dbus_pid_t pid_read;
+  int bytes_read;
+  
+  uid_read = DBUS_UID_UNSET;
+  pid_read = DBUS_PID_UNSET;
+  
+#ifdef HAVE_CMSGCRED 
+  struct {
+    struct cmsghdr hdr;
+    struct cmsgcred cred;
+  } cmsg;
+
+#elif defined(LOCAL_CREDS)
+  struct {
+    struct cmsghdr hdr;
+    struct sockcred cred;
+  } cmsg;
+#endif
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  /* The POSIX spec certainly doesn't promise this, but
+   * we need these assertions to fail as soon as we're wrong about
+   * it so we can do the porting fixups
+   */
+  _dbus_assert (sizeof (pid_t) <= sizeof (dbus_pid_t));
+  _dbus_assert (sizeof (uid_t) <= sizeof (dbus_uid_t));
+  _dbus_assert (sizeof (gid_t) <= sizeof (dbus_gid_t));
+
+  _dbus_credentials_clear (credentials);
+
+  /* Systems supporting LOCAL_CREDS are configured to have this feature
+   * enabled (if it does not conflict with HAVE_CMSGCRED) prior accepting
+   * the connection.  Therefore, the received message must carry the
+   * credentials information without doing anything special.
+   */
+
+  iov.iov_base = &buf;
+  iov.iov_len = 1;
+
+  memset (&msg, 0, sizeof (msg));
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+
+#if defined(HAVE_CMSGCRED) || defined(LOCAL_CREDS)
+  memset (&cmsg, 0, sizeof (cmsg));
+  msg.msg_control = &cmsg;
+  msg.msg_controllen = sizeof (cmsg);
+#endif
+
+ again:
+  bytes_read = recvmsg (client_fd, &msg, 0);
+
+  if (bytes_read < 0)
+    {
+      if (errno == EINTR)
+       goto again;
+
+      /* EAGAIN or EWOULDBLOCK would be unexpected here since we would
+       * normally only call read_credentials if the socket was ready
+       * for reading
+       */
+      
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to read credentials byte: %s",
+                      _dbus_strerror (errno));
+      return FALSE;
+    }
+  else if (bytes_read == 0)
+    {
+      /* this should not happen unless we are using recvmsg wrong,
+       * so is essentially here for paranoia
+       */
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                      "Failed to read credentials byte (zero-length read)");
+      return FALSE;
+    }
+  else if (buf != '\0')
+    {
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                      "Credentials byte was not nul");
+      return FALSE;
+    }
+
+#if defined(HAVE_CMSGCRED) || defined(LOCAL_CREDS)
+  if (cmsg.hdr.cmsg_len < sizeof (cmsg) || cmsg.hdr.cmsg_type != SCM_CREDS)
+    {
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                      "Message from recvmsg() was not SCM_CREDS");
+      return FALSE;
+    }
+#endif
+
+  _dbus_verbose ("read credentials byte\n");
+
+  {
+#ifdef SO_PEERCRED
+    struct ucred cr;   
+    int cr_len = sizeof (cr);
+    
+    if (getsockopt (client_fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 &&
+       cr_len == sizeof (cr))
+      {
+       pid_read = cr.pid;
+       uid_read = cr.uid;
+      }
+    else
+      {
+       _dbus_verbose ("Failed to getsockopt() credentials, returned len %d/%d: %s\n",
+                      cr_len, (int) sizeof (cr), _dbus_strerror (errno));
+      }
+#elif defined(HAVE_CMSGCRED)
+    pid_read = cmsg.cred.cmcred_pid;
+    uid_read = cmsg.cred.cmcred_euid;
+#elif defined(LOCAL_CREDS)
+    pid_read = DBUS_PID_UNSET;
+    uid_read = cmsg.cred.sc_uid;
+    /* Since we have already got the credentials from this socket, we can
+     * disable its LOCAL_CREDS flag if it was ever set. */
+    _dbus_set_local_creds (client_fd, FALSE);
+#elif defined(HAVE_GETPEEREID)
+    uid_t euid;
+    gid_t egid;
+    if (getpeereid (client_fd, &euid, &egid) == 0)
+      {
+        uid_read = euid;
+      }
+    else
+      {
+        _dbus_verbose ("Failed to getpeereid() credentials: %s\n", _dbus_strerror (errno));
+      }
+#elif defined(HAVE_GETPEERUCRED)
+    ucred_t * ucred = NULL;
+    if (getpeerucred (client_fd, &ucred) == 0)
+      {
+        pid_read = ucred_getpid (ucred);
+        uid_read = ucred_geteuid (ucred);
+#ifdef HAVE_ADT
+        /* generate audit session data based on socket ucred */
+        adt_session_data_t *adth = NULL;
+        adt_export_data_t *data = NULL;
+        size_t size = 0;
+        if (adt_start_session (&adth, NULL, 0) || (adth == NULL))
+          {
+            _dbus_verbose ("Failed to adt_start_session(): %s\n", _dbus_strerror (errno));
+          }
+        else 
+          {
+            if (adt_set_from_ucred (adth, ucred, ADT_NEW)) 
+              {
+                _dbus_verbose ("Failed to adt_set_from_ucred(): %s\n", _dbus_strerror (errno));
+              }
+            else
+              {
+                size = adt_export_session_data (adth, &data);
+                if (size <= 0)
+                  {
+                    _dbus_verbose ("Failed to adt_export_session_data(): %s\n", _dbus_strerror (errno));
+                  }
+                else
+                  {
+                    _dbus_credentials_add_adt_audit_data (credentials, data, size);
+                    free (data);
+                  }
+              }
+            (void) adt_end_session (adth);
+          }
+#endif /* HAVE_ADT */
+      }
+    else
+      {
+        _dbus_verbose ("Failed to getpeerucred() credentials: %s\n", _dbus_strerror (errno));
+      }
+    if (ucred != NULL)
+      ucred_free (ucred);
+#else /* !SO_PEERCRED && !HAVE_CMSGCRED && !HAVE_GETPEEREID && !HAVE_GETPEERUCRED */
+    _dbus_verbose ("Socket credentials not supported on this OS\n");
+#endif
+  }
+
+  _dbus_verbose ("Credentials:"
+                 "  pid "DBUS_PID_FORMAT
+                 "  uid "DBUS_UID_FORMAT
+                 "\n",
+                pid_read,
+                uid_read);
+
+  if (pid_read != DBUS_PID_UNSET)
+    {
+      if (!_dbus_credentials_add_unix_pid (credentials, pid_read))
+        {
+          _DBUS_SET_OOM (error);
+          return FALSE;
+        }
+    }
+
+  if (uid_read != DBUS_UID_UNSET)
+    {
+      if (!_dbus_credentials_add_unix_uid (credentials, uid_read))
+        {
+          _DBUS_SET_OOM (error);
+          return FALSE;
+        }
+    }
+  
+  return TRUE;
+}
+
+/**
+ * Sends a single nul byte with our UNIX credentials as ancillary
+ * data.  Returns #TRUE if the data was successfully written.  On
+ * systems that don't support sending credentials, just writes a byte,
+ * doesn't send any credentials.  On some systems, such as Linux,
+ * reading/writing the byte isn't actually required, but we do it
+ * anyway just to avoid multiple codepaths.
+ *
+ * Fails if no byte can be written, so you must select() first.
+ *
+ * The point of the byte is that on some systems we have to
+ * use sendmsg()/recvmsg() to transmit credentials.
+ *
+ * @param server_fd file descriptor for connection to server
+ * @param error return location for error code
+ * @returns #TRUE if the byte was sent
+ */
+dbus_bool_t
+_dbus_send_credentials_socket  (int              server_fd,
+                                DBusError       *error)
+{
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  if (write_credentials_byte (server_fd, error))
+    return TRUE;
+  else
+    return FALSE;
+}
+
+/**
+ * Accepts a connection on a listening socket.
+ * Handles EINTR for you.
+ *
+ * @param listen_fd the listen file descriptor
+ * @returns the connection fd of the client, or -1 on error
+ */
+int
+_dbus_accept  (int listen_fd)
+{
+  int client_fd;
+  struct sockaddr addr;
+  socklen_t addrlen;
+
+  addrlen = sizeof (addr);
+  
+ retry:
+  client_fd = accept (listen_fd, &addr, &addrlen);
+  
+  if (client_fd < 0)
+    {
+      if (errno == EINTR)
+        goto retry;
+    }
+
+  _dbus_verbose ("client fd %d accepted\n", client_fd);
+  
+  return client_fd;
+}
+
+/**
+ * Checks to make sure the given directory is 
+ * private to the user 
+ *
+ * @param dir the name of the directory
+ * @param error error return
+ * @returns #FALSE on failure
+ **/
+dbus_bool_t
+_dbus_check_dir_is_private_to_user (DBusString *dir, DBusError *error)
+{
+  const char *directory;
+  struct stat sb;
+       
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+    
+  directory = _dbus_string_get_const_data (dir);
+       
+  if (stat (directory, &sb) < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "%s", _dbus_strerror (errno));
+   
+      return FALSE;
+    }
+    
+  if ((S_IROTH & sb.st_mode) || (S_IWOTH & sb.st_mode) ||
+      (S_IRGRP & sb.st_mode) || (S_IWGRP & sb.st_mode))
+    {
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                     "%s directory is not private to the user", directory);
+      return FALSE;
+    }
+    
+  return TRUE;
+}
+
+static dbus_bool_t
+fill_user_info_from_passwd (struct passwd *p,
+                            DBusUserInfo  *info,
+                            DBusError     *error)
+{
+  _dbus_assert (p->pw_name != NULL);
+  _dbus_assert (p->pw_dir != NULL);
+  
+  info->uid = p->pw_uid;
+  info->primary_gid = p->pw_gid;
+  info->username = _dbus_strdup (p->pw_name);
+  info->homedir = _dbus_strdup (p->pw_dir);
+  
+  if (info->username == NULL ||
+      info->homedir == NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static dbus_bool_t
+fill_user_info (DBusUserInfo       *info,
+                dbus_uid_t          uid,
+                const DBusString   *username,
+                DBusError          *error)
+{
+  const char *username_c;
+  
+  /* exactly one of username/uid provided */
+  _dbus_assert (username != NULL || uid != DBUS_UID_UNSET);
+  _dbus_assert (username == NULL || uid == DBUS_UID_UNSET);
+
+  info->uid = DBUS_UID_UNSET;
+  info->primary_gid = DBUS_GID_UNSET;
+  info->group_ids = NULL;
+  info->n_group_ids = 0;
+  info->username = NULL;
+  info->homedir = NULL;
+  
+  if (username != NULL)
+    username_c = _dbus_string_get_const_data (username);
+  else
+    username_c = NULL;
+
+  /* For now assuming that the getpwnam() and getpwuid() flavors
+   * are always symmetrical, if not we have to add more configure
+   * checks
+   */
+  
+#if defined (HAVE_POSIX_GETPWNAM_R) || defined (HAVE_NONPOSIX_GETPWNAM_R)
+  {
+    struct passwd *p;
+    int result;
+    size_t buflen;
+    char *buf;
+    struct passwd p_str;
+
+    /* retrieve maximum needed size for buf */
+    buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
+
+    /* sysconf actually returns a long, but everything else expects size_t,
+     * so just recast here.
+     * https://bugs.freedesktop.org/show_bug.cgi?id=17061
+     */
+    if ((long) buflen <= 0)
+      buflen = 1024;
+
+    result = -1;
+    while (1)
+      {
+        buf = dbus_malloc (buflen);
+        if (buf == NULL)
+          {
+            dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+            return FALSE;
+          }
+
+        p = NULL;
+#ifdef HAVE_POSIX_GETPWNAM_R
+        if (uid != DBUS_UID_UNSET)
+          result = getpwuid_r (uid, &p_str, buf, buflen,
+                               &p);
+        else
+          result = getpwnam_r (username_c, &p_str, buf, buflen,
+                               &p);
+#else
+        if (uid != DBUS_UID_UNSET)
+          p = getpwuid_r (uid, &p_str, buf, buflen);
+        else
+          p = getpwnam_r (username_c, &p_str, buf, buflen);
+        result = 0;
+#endif /* !HAVE_POSIX_GETPWNAM_R */
+        //Try a bigger buffer if ERANGE was returned
+        if (result == ERANGE && buflen < 512 * 1024)
+          {
+            dbus_free (buf);
+            buflen *= 2;
+          }
+        else
+          {
+            break;
+          }
+      }
+    if (result == 0 && p == &p_str)
+      {
+        if (!fill_user_info_from_passwd (p, info, error))
+          {
+            dbus_free (buf);
+            return FALSE;
+          }
+        dbus_free (buf);
+      }
+    else
+      {
+        dbus_set_error (error, _dbus_error_from_errno (errno),
+                        "User \"%s\" unknown or no memory to allocate password entry\n",
+                        username_c ? username_c : "???");
+        _dbus_verbose ("User %s unknown\n", username_c ? username_c : "???");
+        dbus_free (buf);
+        return FALSE;
+      }
+  }
+#else /* ! HAVE_GETPWNAM_R */
+  {
+    /* I guess we're screwed on thread safety here */
+    struct passwd *p;
+
+    if (uid != DBUS_UID_UNSET)
+      p = getpwuid (uid);
+    else
+      p = getpwnam (username_c);
+
+    if (p != NULL)
+      {
+        if (!fill_user_info_from_passwd (p, info, error))
+          {
+            return FALSE;
+          }
+      }
+    else
+      {
+        dbus_set_error (error, _dbus_error_from_errno (errno),
+                        "User \"%s\" unknown or no memory to allocate password entry\n",
+                        username_c ? username_c : "???");
+        _dbus_verbose ("User %s unknown\n", username_c ? username_c : "???");
+        return FALSE;
+      }
+  }
+#endif  /* ! HAVE_GETPWNAM_R */
+
+  /* Fill this in so we can use it to get groups */
+  username_c = info->username;
+  
+#ifdef HAVE_GETGROUPLIST
+  {
+    gid_t *buf;
+    int buf_count;
+    int i;
+    
+    buf_count = 17;
+    buf = dbus_new (gid_t, buf_count);
+    if (buf == NULL)
+      {
+        dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+        goto failed;
+      }
+    
+    if (getgrouplist (username_c,
+                      info->primary_gid,
+                      buf, &buf_count) < 0)
+      {
+        gid_t *new = dbus_realloc (buf, buf_count * sizeof (buf[0]));
+        if (new == NULL)
+          {
+            dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+            dbus_free (buf);
+            goto failed;
+          }
+        
+        buf = new;
+
+        errno = 0;
+        if (getgrouplist (username_c, info->primary_gid, buf, &buf_count) < 0)
+          {
+            dbus_set_error (error,
+                            _dbus_error_from_errno (errno),
+                            "Failed to get groups for username \"%s\" primary GID "
+                            DBUS_GID_FORMAT ": %s\n",
+                            username_c, info->primary_gid,
+                            _dbus_strerror (errno));
+            dbus_free (buf);
+            goto failed;
+          }
+      }
+
+    info->group_ids = dbus_new (dbus_gid_t, buf_count);
+    if (info->group_ids == NULL)
+      {
+        dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+        dbus_free (buf);
+        goto failed;
+      }
+    
+    for (i = 0; i < buf_count; ++i)
+      info->group_ids[i] = buf[i];
+
+    info->n_group_ids = buf_count;
+    
+    dbus_free (buf);
+  }
+#else  /* HAVE_GETGROUPLIST */
+  {
+    /* We just get the one group ID */
+    info->group_ids = dbus_new (dbus_gid_t, 1);
+    if (info->group_ids == NULL)
+      {
+        dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+        goto failed;
+      }
+
+    info->n_group_ids = 1;
+
+    (info->group_ids)[0] = info->primary_gid;
+  }
+#endif /* HAVE_GETGROUPLIST */
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  return TRUE;
+  
+ failed:
+  _DBUS_ASSERT_ERROR_IS_SET (error);
+  return FALSE;
+}
+
+/**
+ * Gets user info for the given username.
+ *
+ * @param info user info object to initialize
+ * @param username the username
+ * @param error error return
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_user_info_fill (DBusUserInfo     *info,
+                      const DBusString *username,
+                      DBusError        *error)
+{
+  return fill_user_info (info, DBUS_UID_UNSET,
+                         username, error);
+}
+
+/**
+ * Gets user info for the given user ID.
+ *
+ * @param info user info object to initialize
+ * @param uid the user ID
+ * @param error error return
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_user_info_fill_uid (DBusUserInfo *info,
+                          dbus_uid_t    uid,
+                          DBusError    *error)
+{
+  return fill_user_info (info, uid,
+                         NULL, error);
+}
+
+/**
+ * Adds the credentials of the current process to the
+ * passed-in credentials object.
+ *
+ * @param credentials credentials to add to
+ * @returns #FALSE if no memory; does not properly roll back on failure, so only some credentials may have been added
+ */
+dbus_bool_t
+_dbus_credentials_add_from_current_process (DBusCredentials *credentials)
+{
+  /* The POSIX spec certainly doesn't promise this, but
+   * we need these assertions to fail as soon as we're wrong about
+   * it so we can do the porting fixups
+   */
+  _dbus_assert (sizeof (pid_t) <= sizeof (dbus_pid_t));
+  _dbus_assert (sizeof (uid_t) <= sizeof (dbus_uid_t));
+  _dbus_assert (sizeof (gid_t) <= sizeof (dbus_gid_t));
+
+  if (!_dbus_credentials_add_unix_pid(credentials, _dbus_getpid()))
+    return FALSE;
+  if (!_dbus_credentials_add_unix_uid(credentials, _dbus_geteuid()))
+    return FALSE;
+
+  return TRUE;
+}
+
+/**
+ * Append to the string the identity we would like to have when we
+ * authenticate, on UNIX this is the current process UID and on
+ * Windows something else, probably a Windows SID string.  No escaping
+ * is required, that is done in dbus-auth.c. The username here
+ * need not be anything human-readable, it can be the machine-readable
+ * form i.e. a user id.
+ * 
+ * @param str the string to append to
+ * @returns #FALSE on no memory
+ */
+dbus_bool_t
+_dbus_append_user_from_current_process (DBusString *str)
+{
+  return _dbus_string_append_uint (str,
+                                   _dbus_geteuid ());
+}
+
+/**
+ * Gets our process ID
+ * @returns process ID
+ */
+dbus_pid_t
+_dbus_getpid (void)
+{
+  return getpid ();
+}
+
+/** Gets our UID
+ * @returns process UID
+ */
+dbus_uid_t
+_dbus_getuid (void)
+{
+  return getuid ();
+}
+
+/** Gets our effective UID
+ * @returns process effective UID
+ */
+dbus_uid_t
+_dbus_geteuid (void)
+{
+  return geteuid ();
+}
+
+/**
+ * The only reason this is separate from _dbus_getpid() is to allow it
+ * on Windows for logging but not for other purposes.
+ * 
+ * @returns process ID to put in log messages
+ */
+unsigned long
+_dbus_pid_for_log (void)
+{
+  return getpid ();
+}
+
+/**
+ * Gets a UID from a UID string.
+ *
+ * @param uid_str the UID in string form
+ * @param uid UID to fill in
+ * @returns #TRUE if successfully filled in UID
+ */
+dbus_bool_t
+_dbus_parse_uid (const DBusString      *uid_str,
+                 dbus_uid_t            *uid)
+{
+  int end;
+  long val;
+  
+  if (_dbus_string_get_length (uid_str) == 0)
+    {
+      _dbus_verbose ("UID string was zero length\n");
+      return FALSE;
+    }
+
+  val = -1;
+  end = 0;
+  if (!_dbus_string_parse_int (uid_str, 0, &val,
+                               &end))
+    {
+      _dbus_verbose ("could not parse string as a UID\n");
+      return FALSE;
+    }
+  
+  if (end != _dbus_string_get_length (uid_str))
+    {
+      _dbus_verbose ("string contained trailing stuff after UID\n");
+      return FALSE;
+    }
+
+  *uid = val;
+
+  return TRUE;
+}
+
+
+_DBUS_DEFINE_GLOBAL_LOCK (atomic);
+
+#if DBUS_USE_ATOMIC_INT_486_COND
+/* Taken from CVS version 1.7 of glibc's sysdeps/i386/i486/atomicity.h */
+/* Since the asm stuff here is gcc-specific we go ahead and use "inline" also */
+static inline dbus_int32_t
+atomic_exchange_and_add (DBusAtomic            *atomic,
+                         volatile dbus_int32_t  val)
+{
+  register dbus_int32_t result;
+
+  __asm__ __volatile__ ("lock; xaddl %0,%1"
+                        : "=r" (result), "=m" (atomic->value)
+                       : "0" (val), "m" (atomic->value));
+  return result;
+}
+#endif
+
+/**
+ * Atomically increments an integer
+ *
+ * @param atomic pointer to the integer to increment
+ * @returns the value before incrementing
+ *
+ * @todo implement arch-specific faster atomic ops
+ */
+dbus_int32_t
+_dbus_atomic_inc (DBusAtomic *atomic)
+{
+#if DBUS_USE_ATOMIC_INT_486_COND
+  return atomic_exchange_and_add (atomic, 1);
+#else
+  dbus_int32_t res;
+  _DBUS_LOCK (atomic);
+  res = atomic->value;
+  atomic->value += 1;
+  _DBUS_UNLOCK (atomic);
+  return res;
+#endif
+}
+
+/**
+ * Atomically decrement an integer
+ *
+ * @param atomic pointer to the integer to decrement
+ * @returns the value before decrementing
+ *
+ * @todo implement arch-specific faster atomic ops
+ */
+dbus_int32_t
+_dbus_atomic_dec (DBusAtomic *atomic)
+{
+#if DBUS_USE_ATOMIC_INT_486_COND
+  return atomic_exchange_and_add (atomic, -1);
+#else
+  dbus_int32_t res;
+  
+  _DBUS_LOCK (atomic);
+  res = atomic->value;
+  atomic->value -= 1;
+  _DBUS_UNLOCK (atomic);
+  return res;
+#endif
+}
+
+#ifdef DBUS_BUILD_TESTS
+/** Gets our GID
+ * @returns process GID
+ */
+dbus_gid_t
+_dbus_getgid (void)
+{
+  return getgid ();
+}
+#endif
+
+/**
+ * Wrapper for poll().
+ *
+ * @param fds the file descriptors to poll
+ * @param n_fds number of descriptors in the array
+ * @param timeout_milliseconds timeout or -1 for infinite
+ * @returns numbers of fds with revents, or <0 on error
+ */
+int
+_dbus_poll (DBusPollFD *fds,
+            int         n_fds,
+            int         timeout_milliseconds)
+{
+#if defined(HAVE_POLL) && !defined(BROKEN_POLL)
+  /* This big thing is a constant expression and should get optimized
+   * out of existence. So it's more robust than a configure check at
+   * no cost.
+   */
+  if (_DBUS_POLLIN == POLLIN &&
+      _DBUS_POLLPRI == POLLPRI &&
+      _DBUS_POLLOUT == POLLOUT &&
+      _DBUS_POLLERR == POLLERR &&
+      _DBUS_POLLHUP == POLLHUP &&
+      _DBUS_POLLNVAL == POLLNVAL &&
+      sizeof (DBusPollFD) == sizeof (struct pollfd) &&
+      _DBUS_STRUCT_OFFSET (DBusPollFD, fd) ==
+      _DBUS_STRUCT_OFFSET (struct pollfd, fd) &&
+      _DBUS_STRUCT_OFFSET (DBusPollFD, events) ==
+      _DBUS_STRUCT_OFFSET (struct pollfd, events) &&
+      _DBUS_STRUCT_OFFSET (DBusPollFD, revents) ==
+      _DBUS_STRUCT_OFFSET (struct pollfd, revents))
+    {
+      return poll ((struct pollfd*) fds,
+                   n_fds, 
+                   timeout_milliseconds);
+    }
+  else
+    {
+      /* We have to convert the DBusPollFD to an array of
+       * struct pollfd, poll, and convert back.
+       */
+      _dbus_warn ("didn't implement poll() properly for this system yet\n");
+      return -1;
+    }
+#else /* ! HAVE_POLL */
+
+  fd_set read_set, write_set, err_set;
+  int max_fd = 0;
+  int i;
+  struct timeval tv;
+  int ready;
+  
+  FD_ZERO (&read_set);
+  FD_ZERO (&write_set);
+  FD_ZERO (&err_set);
+
+  for (i = 0; i < n_fds; i++)
+    {
+      DBusPollFD *fdp = &fds[i];
+
+      if (fdp->events & _DBUS_POLLIN)
+       FD_SET (fdp->fd, &read_set);
+
+      if (fdp->events & _DBUS_POLLOUT)
+       FD_SET (fdp->fd, &write_set);
+
+      FD_SET (fdp->fd, &err_set);
+
+      max_fd = MAX (max_fd, fdp->fd);
+    }
+    
+  tv.tv_sec = timeout_milliseconds / 1000;
+  tv.tv_usec = (timeout_milliseconds % 1000) * 1000;
+
+  ready = select (max_fd + 1, &read_set, &write_set, &err_set,
+                  timeout_milliseconds < 0 ? NULL : &tv);
+
+  if (ready > 0)
+    {
+      for (i = 0; i < n_fds; i++)
+       {
+         DBusPollFD *fdp = &fds[i];
+
+         fdp->revents = 0;
+
+         if (FD_ISSET (fdp->fd, &read_set))
+           fdp->revents |= _DBUS_POLLIN;
+
+         if (FD_ISSET (fdp->fd, &write_set))
+           fdp->revents |= _DBUS_POLLOUT;
+
+         if (FD_ISSET (fdp->fd, &err_set))
+           fdp->revents |= _DBUS_POLLERR;
+       }
+    }
+
+  return ready;
+#endif
+}
+
+/**
+ * Get current time, as in gettimeofday().
+ *
+ * @param tv_sec return location for number of seconds
+ * @param tv_usec return location for number of microseconds (thousandths)
+ */
+void
+_dbus_get_current_time (long *tv_sec,
+                        long *tv_usec)
+{
+  struct timeval t;
+
+  gettimeofday (&t, NULL);
+
+  if (tv_sec)
+    *tv_sec = t.tv_sec;
+  if (tv_usec)
+    *tv_usec = t.tv_usec;
+}
+
+/**
+ * Appends the contents of the given file to the string,
+ * returning error code. At the moment, won't open a file
+ * more than a megabyte in size.
+ *
+ * @param str the string to append to
+ * @param filename filename to load
+ * @param error place to set an error
+ * @returns #FALSE if error was set
+ */
+dbus_bool_t
+_dbus_file_get_contents (DBusString       *str,
+                         const DBusString *filename,
+                         DBusError        *error)
+{
+  int fd;
+  struct stat sb;
+  int orig_len;
+  int total;
+  const char *filename_c;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  filename_c = _dbus_string_get_const_data (filename);
+  
+  /* O_BINARY useful on Cygwin */
+  fd = open (filename_c, O_RDONLY | O_BINARY);
+  if (fd < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to open \"%s\": %s",
+                      filename_c,
+                      _dbus_strerror (errno));
+      return FALSE;
+    }
+
+  _dbus_verbose ("file fd %d opened\n", fd);
+  
+  if (fstat (fd, &sb) < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to stat \"%s\": %s",
+                      filename_c,
+                      _dbus_strerror (errno));
+
+      _dbus_verbose ("fstat() failed: %s",
+                     _dbus_strerror (errno));
+      
+      _dbus_close (fd, NULL);
+      
+      return FALSE;
+    }
+
+  if (sb.st_size > _DBUS_ONE_MEGABYTE)
+    {
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                      "File size %lu of \"%s\" is too large.",
+                      (unsigned long) sb.st_size, filename_c);
+      _dbus_close (fd, NULL);
+      return FALSE;
+    }
+  
+  total = 0;
+  orig_len = _dbus_string_get_length (str);
+  if (sb.st_size > 0 && S_ISREG (sb.st_mode))
+    {
+      int bytes_read;
+
+      while (total < (int) sb.st_size)
+        {
+          bytes_read = _dbus_read (fd, str,
+                                   sb.st_size - total);
+          if (bytes_read <= 0)
+            {
+              dbus_set_error (error, _dbus_error_from_errno (errno),
+                              "Error reading \"%s\": %s",
+                              filename_c,
+                              _dbus_strerror (errno));
+
+              _dbus_verbose ("read() failed: %s",
+                             _dbus_strerror (errno));
+              
+              _dbus_close (fd, NULL);
+              _dbus_string_set_length (str, orig_len);
+              return FALSE;
+            }
+          else
+            total += bytes_read;
+        }
+
+      _dbus_close (fd, NULL);
+      return TRUE;
+    }
+  else if (sb.st_size != 0)
+    {
+      _dbus_verbose ("Can only open regular files at the moment.\n");
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                      "\"%s\" is not a regular file",
+                      filename_c);
+      _dbus_close (fd, NULL);
+      return FALSE;
+    }
+  else
+    {
+      _dbus_close (fd, NULL);
+      return TRUE;
+    }
+}
+
+/**
+ * Writes a string out to a file. If the file exists,
+ * it will be atomically overwritten by the new data.
+ *
+ * @param str the string to write out
+ * @param filename the file to save string to
+ * @param error error to be filled in on failure
+ * @returns #FALSE on failure
+ */
+dbus_bool_t
+_dbus_string_save_to_file (const DBusString *str,
+                           const DBusString *filename,
+                           DBusError        *error)
+{
+  int fd;
+  int bytes_to_write;
+  const char *filename_c;
+  DBusString tmp_filename;
+  const char *tmp_filename_c;
+  int total;
+  dbus_bool_t need_unlink;
+  dbus_bool_t retval;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  fd = -1;
+  retval = FALSE;
+  need_unlink = FALSE;
+  
+  if (!_dbus_string_init (&tmp_filename))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      return FALSE;
+    }
+
+  if (!_dbus_string_copy (filename, 0, &tmp_filename, 0))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      _dbus_string_free (&tmp_filename);
+      return FALSE;
+    }
+  
+  if (!_dbus_string_append (&tmp_filename, "."))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      _dbus_string_free (&tmp_filename);
+      return FALSE;
+    }
+
+#define N_TMP_FILENAME_RANDOM_BYTES 8
+  if (!_dbus_generate_random_ascii (&tmp_filename, N_TMP_FILENAME_RANDOM_BYTES))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      _dbus_string_free (&tmp_filename);
+      return FALSE;
+    }
+    
+  filename_c = _dbus_string_get_const_data (filename);
+  tmp_filename_c = _dbus_string_get_const_data (&tmp_filename);
+
+  fd = open (tmp_filename_c, O_WRONLY | O_BINARY | O_EXCL | O_CREAT,
+             0600);
+  if (fd < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Could not create %s: %s", tmp_filename_c,
+                      _dbus_strerror (errno));
+      goto out;
+    }
+
+  _dbus_verbose ("tmp file fd %d opened\n", fd);
+  
+  need_unlink = TRUE;
+  
+  total = 0;
+  bytes_to_write = _dbus_string_get_length (str);
+
+  while (total < bytes_to_write)
+    {
+      int bytes_written;
+
+      bytes_written = _dbus_write (fd, str, total,
+                                   bytes_to_write - total);
+
+      if (bytes_written <= 0)
+        {
+          dbus_set_error (error, _dbus_error_from_errno (errno),
+                          "Could not write to %s: %s", tmp_filename_c,
+                          _dbus_strerror (errno));
+          
+          goto out;
+        }
+
+      total += bytes_written;
+    }
+
+  if (fsync(fd))
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Could not synchronize file %s: %s",
+                      tmp_filename_c, _dbus_strerror (errno));
+
+      goto out;
+  }
+
+  if (!_dbus_close (fd, NULL))
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Could not close file %s: %s",
+                      tmp_filename_c, _dbus_strerror (errno));
+
+      goto out;
+    }
+
+  fd = -1;
+  
+  if (rename (tmp_filename_c, filename_c) < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Could not rename %s to %s: %s",
+                      tmp_filename_c, filename_c,
+                      _dbus_strerror (errno));
+
+      goto out;
+    }
+
+  need_unlink = FALSE;
+  
+  retval = TRUE;
+  
+ out:
+  /* close first, then unlink, to prevent ".nfs34234235" garbage
+   * files
+   */
+
+  if (fd >= 0)
+    _dbus_close (fd, NULL);
+        
+  if (need_unlink && unlink (tmp_filename_c) < 0)
+    _dbus_verbose ("Failed to unlink temp file %s: %s\n",
+                   tmp_filename_c, _dbus_strerror (errno));
+
+  _dbus_string_free (&tmp_filename);
+
+  if (!retval)
+    _DBUS_ASSERT_ERROR_IS_SET (error);
+  
+  return retval;
+}
+
+/** Makes the file readable by every user in the system.
+ *
+ * @param filename the filename
+ * @param error error location
+ * @returns #TRUE if the file's permissions could be changed.
+ */
+dbus_bool_t
+_dbus_make_file_world_readable(const DBusString *filename,
+                               DBusError *error)
+{
+  const char *filename_c;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  filename_c = _dbus_string_get_const_data (filename);
+  if (chmod (filename_c, 0644) == -1)
+    {
+      dbus_set_error (error,
+                      DBUS_ERROR_FAILED,
+                      "Could not change permissions of file %s: %s\n",
+                      filename_c,
+                      _dbus_strerror (errno));
+      return FALSE;
+    }
+  return TRUE;
+}
+
+/** Creates the given file, failing if the file already exists.
+ *
+ * @param filename the filename
+ * @param error error location
+ * @returns #TRUE if we created the file and it didn't exist
+ */
+dbus_bool_t
+_dbus_create_file_exclusively (const DBusString *filename,
+                               DBusError        *error)
+{
+  int fd;
+  const char *filename_c;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  filename_c = _dbus_string_get_const_data (filename);
+  
+  fd = open (filename_c, O_WRONLY | O_BINARY | O_EXCL | O_CREAT,
+             0600);
+  if (fd < 0)
+    {
+      dbus_set_error (error,
+                      DBUS_ERROR_FAILED,
+                      "Could not create file %s: %s\n",
+                      filename_c,
+                      _dbus_strerror (errno));
+      return FALSE;
+    }
+
+  _dbus_verbose ("exclusive file fd %d opened\n", fd);
+  
+  if (!_dbus_close (fd, NULL))
+    {
+      dbus_set_error (error,
+                      DBUS_ERROR_FAILED,
+                      "Could not close file %s: %s\n",
+                      filename_c,
+                      _dbus_strerror (errno));
+      return FALSE;
+    }
+  
+  return TRUE;
+}
+
+/**
+ * Deletes the given file.
+ *
+ * @param filename the filename
+ * @param error error location
+ * 
+ * @returns #TRUE if unlink() succeeded
+ */
+dbus_bool_t
+_dbus_delete_file (const DBusString *filename,
+                   DBusError        *error)
+{
+  const char *filename_c;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  filename_c = _dbus_string_get_const_data (filename);
+
+  if (unlink (filename_c) < 0)
+    {
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                      "Failed to delete file %s: %s\n",
+                      filename_c, _dbus_strerror (errno));
+      return FALSE;
+    }
+  else
+    return TRUE;
+}
+
+/**
+ * Creates a directory; succeeds if the directory
+ * is created or already existed.
+ *
+ * @param filename directory filename
+ * @param error initialized error object
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_create_directory (const DBusString *filename,
+                        DBusError        *error)
+{
+  const char *filename_c;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  filename_c = _dbus_string_get_const_data (filename);
+
+  if (mkdir (filename_c, 0700) < 0)
+    {
+      if (errno == EEXIST)
+        return TRUE;
+      
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                      "Failed to create directory %s: %s\n",
+                      filename_c, _dbus_strerror (errno));
+      return FALSE;
+    }
+  else
+    return TRUE;
+}
+
+/**
+ * Appends the given filename to the given directory.
+ *
+ * @todo it might be cute to collapse multiple '/' such as "foo//"
+ * concat "//bar"
+ *
+ * @param dir the directory name
+ * @param next_component the filename
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_concat_dir_and_file (DBusString       *dir,
+                           const DBusString *next_component)
+{
+  dbus_bool_t dir_ends_in_slash;
+  dbus_bool_t file_starts_with_slash;
+
+  if (_dbus_string_get_length (dir) == 0 ||
+      _dbus_string_get_length (next_component) == 0)
+    return TRUE;
+  
+  dir_ends_in_slash = '/' == _dbus_string_get_byte (dir,
+                                                    _dbus_string_get_length (dir) - 1);
+
+  file_starts_with_slash = '/' == _dbus_string_get_byte (next_component, 0);
+
+  if (dir_ends_in_slash && file_starts_with_slash)
+    {
+      _dbus_string_shorten (dir, 1);
+    }
+  else if (!(dir_ends_in_slash || file_starts_with_slash))
+    {
+      if (!_dbus_string_append_byte (dir, '/'))
+        return FALSE;
+    }
+
+  return _dbus_string_copy (next_component, 0, dir,
+                            _dbus_string_get_length (dir));
+}
+
+/** nanoseconds in a second */
+#define NANOSECONDS_PER_SECOND       1000000000
+/** microseconds in a second */
+#define MICROSECONDS_PER_SECOND      1000000
+/** milliseconds in a second */
+#define MILLISECONDS_PER_SECOND      1000
+/** nanoseconds in a millisecond */
+#define NANOSECONDS_PER_MILLISECOND  1000000
+/** microseconds in a millisecond */
+#define MICROSECONDS_PER_MILLISECOND 1000
+
+/**
+ * Sleeps the given number of milliseconds.
+ * @param milliseconds number of milliseconds
+ */
+void
+_dbus_sleep_milliseconds (int milliseconds)
+{
+#ifdef HAVE_NANOSLEEP
+  struct timespec req;
+  struct timespec rem;
+
+  req.tv_sec = milliseconds / MILLISECONDS_PER_SECOND;
+  req.tv_nsec = (milliseconds % MILLISECONDS_PER_SECOND) * NANOSECONDS_PER_MILLISECOND;
+  rem.tv_sec = 0;
+  rem.tv_nsec = 0;
+
+  while (nanosleep (&req, &rem) < 0 && errno == EINTR)
+    req = rem;
+#elif defined (HAVE_USLEEP)
+  usleep (milliseconds * MICROSECONDS_PER_MILLISECOND);
+#else /* ! HAVE_USLEEP */
+  sleep (MAX (milliseconds / 1000, 1));
+#endif
+}
+
+static dbus_bool_t
+_dbus_generate_pseudorandom_bytes (DBusString *str,
+                                   int         n_bytes)
+{
+  int old_len;
+  char *p;
+  
+  old_len = _dbus_string_get_length (str);
+
+  if (!_dbus_string_lengthen (str, n_bytes))
+    return FALSE;
+
+  p = _dbus_string_get_data_len (str, old_len, n_bytes);
+
+  _dbus_generate_pseudorandom_bytes_buffer (p, n_bytes);
+
+  return TRUE;
+}
+
+/**
+ * Generates the given number of random bytes,
+ * using the best mechanism we can come up with.
+ *
+ * @param str the string
+ * @param n_bytes the number of random bytes to append to string
+ * @returns #TRUE on success, #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_generate_random_bytes (DBusString *str,
+                             int         n_bytes)
+{
+  int old_len;
+  int fd;
+
+  /* FALSE return means "no memory", if it could
+   * mean something else then we'd need to return
+   * a DBusError. So we always fall back to pseudorandom
+   * if the I/O fails.
+   */
+  
+  old_len = _dbus_string_get_length (str);
+  fd = -1;
+
+  /* note, urandom on linux will fall back to pseudorandom */
+  fd = open ("/dev/urandom", O_RDONLY);
+  if (fd < 0)
+    return _dbus_generate_pseudorandom_bytes (str, n_bytes);
+
+  _dbus_verbose ("/dev/urandom fd %d opened\n", fd);
+  
+  if (_dbus_read (fd, str, n_bytes) != n_bytes)
+    {
+      _dbus_close (fd, NULL);
+      _dbus_string_set_length (str, old_len);
+      return _dbus_generate_pseudorandom_bytes (str, n_bytes);
+    }
+
+  _dbus_verbose ("Read %d bytes from /dev/urandom\n",
+                 n_bytes);
+  
+  _dbus_close (fd, NULL);
+  
+  return TRUE;
+}
+
+/**
+ * Exit the process, returning the given value.
+ *
+ * @param code the exit code
+ */
+void
+_dbus_exit (int code)
+{
+  _exit (code);
+}
+
+/**
+ * A wrapper around strerror() because some platforms
+ * may be lame and not have strerror(). Also, never
+ * returns NULL.
+ *
+ * @param error_number errno.
+ * @returns error description.
+ */
+const char*
+_dbus_strerror (int error_number)
+{
+  const char *msg;
+  
+  msg = strerror (error_number);
+  if (msg == NULL)
+    msg = "unknown";
+
+  return msg;
+}
+
+/**
+ * signal (SIGPIPE, SIG_IGN);
+ */
+void
+_dbus_disable_sigpipe (void)
+{
+  signal (SIGPIPE, SIG_IGN);
+}
+
+/**
+ * Sets the file descriptor to be close
+ * on exec. Should be called for all file
+ * descriptors in D-Bus code.
+ *
+ * @param fd the file descriptor
+ */
+void
+_dbus_fd_set_close_on_exec (int fd)
+{
+  int val;
+  
+  val = fcntl (fd, F_GETFD, 0);
+  
+  if (val < 0)
+    return;
+
+  val |= FD_CLOEXEC;
+  
+  fcntl (fd, F_SETFD, val);
+}
+
+/**
+ * Closes a file descriptor.
+ *
+ * @param fd the file descriptor
+ * @param error error object
+ * @returns #FALSE if error set
+ */
+dbus_bool_t
+_dbus_close (int        fd,
+             DBusError *error)
+{
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+ again:
+  if (close (fd) < 0)
+    {
+      if (errno == EINTR)
+        goto again;
+
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Could not close fd %d", fd);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+/**
+ * Sets a file descriptor to be nonblocking.
+ *
+ * @param fd the file descriptor.
+ * @param error address of error location.
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_set_fd_nonblocking (int             fd,
+                          DBusError      *error)
+{
+  int val;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  val = fcntl (fd, F_GETFL, 0);
+  if (val < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to get flags from file descriptor %d: %s",
+                      fd, _dbus_strerror (errno));
+      _dbus_verbose ("Failed to get flags for fd %d: %s\n", fd,
+                     _dbus_strerror (errno));
+      return FALSE;
+    }
+
+  if (fcntl (fd, F_SETFL, val | O_NONBLOCK) < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to set nonblocking flag of file descriptor %d: %s",
+                      fd, _dbus_strerror (errno));
+      _dbus_verbose ("Failed to set fd %d nonblocking: %s\n",
+                     fd, _dbus_strerror (errno));
+
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+/**
+ * On GNU libc systems, print a crude backtrace to stderr.  On other
+ * systems, print "no backtrace support" and block for possible gdb
+ * attachment if an appropriate environment variable is set.
+ */
+void
+_dbus_print_backtrace (void)
+{  
+#if defined (HAVE_BACKTRACE) && defined (DBUS_BUILT_R_DYNAMIC)
+  void *bt[500];
+  int bt_size;
+  int i;
+  char **syms;
+  
+  bt_size = backtrace (bt, 500);
+
+  syms = backtrace_symbols (bt, bt_size);
+  
+  i = 0;
+  while (i < bt_size)
+    {
+      /* don't use dbus_warn since it can _dbus_abort() */
+      fprintf (stderr, "  %s\n", syms[i]);
+      ++i;
+    }
+  fflush (stderr);
+
+  free (syms);
+#elif defined (HAVE_BACKTRACE) && ! defined (DBUS_BUILT_R_DYNAMIC)
+  fprintf (stderr, "  D-Bus not built with -rdynamic so unable to print a backtrace\n");
+#else
+  fprintf (stderr, "  D-Bus not compiled with backtrace support so unable to print a backtrace\n");
+#endif
+}
+
+/**
+ * Creates a full-duplex pipe (as in socketpair()).
+ * Sets both ends of the pipe nonblocking.
+ *
+ * @todo libdbus only uses this for the debug-pipe server, so in
+ * principle it could be in dbus-sysdeps-util.c, except that
+ * dbus-sysdeps-util.c isn't in libdbus when tests are enabled and the
+ * debug-pipe server is used.
+ * 
+ * @param fd1 return location for one end
+ * @param fd2 return location for the other end
+ * @param blocking #TRUE if pipe should be blocking
+ * @param error error return
+ * @returns #FALSE on failure (if error is set)
+ */
+dbus_bool_t
+_dbus_full_duplex_pipe (int        *fd1,
+                        int        *fd2,
+                        dbus_bool_t blocking,
+                        DBusError  *error)
+{
+#ifdef HAVE_SOCKETPAIR
+  int fds[2];
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  if (socketpair (AF_UNIX, SOCK_STREAM, 0, fds) < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Could not create full-duplex pipe");
+      return FALSE;
+    }
+
+  if (!blocking &&
+      (!_dbus_set_fd_nonblocking (fds[0], NULL) ||
+       !_dbus_set_fd_nonblocking (fds[1], NULL)))
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Could not set full-duplex pipe nonblocking");
+      
+      _dbus_close (fds[0], NULL);
+      _dbus_close (fds[1], NULL);
+      
+      return FALSE;
+    }
+  
+  *fd1 = fds[0];
+  *fd2 = fds[1];
+
+  _dbus_verbose ("full-duplex pipe %d <-> %d\n",
+                 *fd1, *fd2);
+  
+  return TRUE;  
+#else
+  _dbus_warn ("_dbus_full_duplex_pipe() not implemented on this OS\n");
+  dbus_set_error (error, DBUS_ERROR_FAILED,
+                  "_dbus_full_duplex_pipe() not implemented on this OS");
+  return FALSE;
+#endif
+}
+
+/**
+ * Measure the length of the given format string and arguments,
+ * not including the terminating nul.
+ *
+ * @param format a printf-style format string
+ * @param args arguments for the format string
+ * @returns length of the given format string and args
+ */
+int
+_dbus_printf_string_upper_bound (const char *format,
+                                 va_list     args)
+{
+  char c;
+  return vsnprintf (&c, 1, format, args);
+}
+
+/**
+ * Gets the temporary files directory by inspecting the environment variables 
+ * TMPDIR, TMP, and TEMP in that order. If none of those are set "/tmp" is returned
+ *
+ * @returns location of temp directory
+ */
+const char*
+_dbus_get_tmpdir(void)
+{
+  static const char* tmpdir = NULL;
+
+  if (tmpdir == NULL)
+    {
+      /* TMPDIR is what glibc uses, then
+       * glibc falls back to the P_tmpdir macro which
+       * just expands to "/tmp"
+       */
+      if (tmpdir == NULL)
+        tmpdir = getenv("TMPDIR");
+
+      /* These two env variables are probably
+       * broken, but maybe some OS uses them?
+       */
+      if (tmpdir == NULL)
+        tmpdir = getenv("TMP");
+      if (tmpdir == NULL)
+        tmpdir = getenv("TEMP");
+
+      /* And this is the sane fallback. */
+      if (tmpdir == NULL)
+        tmpdir = "/tmp";
+    }
+  
+  _dbus_assert(tmpdir != NULL);
+  
+  return tmpdir;
+}
+
+/**
+ * Determines the address of the session bus by querying a
+ * platform-specific method.
+ *
+ * If successful, returns #TRUE and appends the address to @p
+ * address. If a failure happens, returns #FALSE and
+ * sets an error in @p error.
+ *
+ * @param address a DBusString where the address can be stored
+ * @param error a DBusError to store the error in case of failure
+ * @returns #TRUE on success, #FALSE if an error happened
+ */
+dbus_bool_t
+_dbus_get_autolaunch_address (DBusString *address,
+                              DBusError  *error)
+{
+  static char *argv[6];
+  int address_pipe[2] = { -1, -1 };
+  int errors_pipe[2] = { -1, -1 };
+  pid_t pid;
+  int ret;
+  int status;
+  int orig_len;
+  int i;
+  DBusString uuid;
+  dbus_bool_t retval;
+  
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  retval = FALSE;
+
+  if (!_dbus_string_init (&uuid))
+    {
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+  
+  if (!_dbus_get_local_machine_uuid_encoded (&uuid))
+    {
+      _DBUS_SET_OOM (error);
+      goto out;
+    }
+  
+  i = 0;
+  argv[i] = "dbus-launch";
+  ++i;
+  argv[i] = "--autolaunch";
+  ++i;
+  argv[i] = _dbus_string_get_data (&uuid);
+  ++i;
+  argv[i] = "--binary-syntax";
+  ++i;
+  argv[i] = "--close-stderr";
+  ++i;
+  argv[i] = NULL;
+  ++i;
+
+  _dbus_assert (i == _DBUS_N_ELEMENTS (argv));
+  
+  orig_len = _dbus_string_get_length (address);
+  
+#define READ_END        0
+#define WRITE_END       1
+  if (pipe (address_pipe) < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to create a pipe: %s",
+                      _dbus_strerror (errno));
+      _dbus_verbose ("Failed to create a pipe to call dbus-launch: %s\n",
+                     _dbus_strerror (errno));
+      goto out;
+    }
+  if (pipe (errors_pipe) < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to create a pipe: %s",
+                      _dbus_strerror (errno));
+      _dbus_verbose ("Failed to create a pipe to call dbus-launch: %s\n",
+                     _dbus_strerror (errno));
+      goto out;
+    }
+
+  pid = fork ();
+  if (pid < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to fork(): %s",
+                      _dbus_strerror (errno));
+      _dbus_verbose ("Failed to fork() to call dbus-launch: %s\n",
+                     _dbus_strerror (errno));
+      goto out;
+    }
+
+  if (pid == 0)
+    {
+      /* child process */
+      int maxfds;
+      int fd;
+
+      fd = open ("/dev/null", O_RDWR);
+      if (fd == -1)
+        /* huh?! can't open /dev/null? */
+        _exit (1);
+
+      _dbus_verbose ("/dev/null fd %d opened\n", fd);
+      
+      /* set-up stdXXX */
+      close (address_pipe[READ_END]);
+      close (errors_pipe[READ_END]);
+      close (0);                /* close stdin */
+      close (1);                /* close stdout */
+      close (2);                /* close stderr */
+
+      if (dup2 (fd, 0) == -1)
+        _exit (1);
+      if (dup2 (address_pipe[WRITE_END], 1) == -1)
+        _exit (1);
+      if (dup2 (errors_pipe[WRITE_END], 2) == -1)
+        _exit (1);
+
+      maxfds = sysconf (_SC_OPEN_MAX);
+      /* Pick something reasonable if for some reason sysconf
+       * says unlimited.
+       */
+      if (maxfds < 0)
+        maxfds = 1024;
+      /* close all inherited fds */
+      for (i = 3; i < maxfds; i++)
+        close (i);
+
+      execv (DBUS_BINDIR "/dbus-launch", argv);
+
+      /* failed, try searching PATH */
+      execvp ("dbus-launch", argv);
+
+      /* still nothing, we failed */
+      _exit (1);
+    }
+
+  /* parent process */
+  close (address_pipe[WRITE_END]);
+  close (errors_pipe[WRITE_END]);
+  address_pipe[WRITE_END] = -1;
+  errors_pipe[WRITE_END] = -1;
+
+  ret = 0;
+  do 
+    {
+      ret = _dbus_read (address_pipe[READ_END], address, 1024);
+    }
+  while (ret > 0);
+
+  /* reap the child process to avoid it lingering as zombie */
+  do
+    {
+      ret = waitpid (pid, &status, 0);
+    }
+  while (ret == -1 && errno == EINTR);
+
+  /* We succeeded if the process exited with status 0 and
+     anything was read */
+  if (!WIFEXITED (status) || WEXITSTATUS (status) != 0 ||
+      _dbus_string_get_length (address) == orig_len)
+    {
+      /* The process ended with error */
+      DBusString error_message;
+      _dbus_string_init (&error_message);
+      ret = 0;
+      do
+       {
+         ret = _dbus_read (errors_pipe[READ_END], &error_message, 1024);
+       }
+      while (ret > 0);
+
+      _dbus_string_set_length (address, orig_len);
+      if (_dbus_string_get_length (&error_message) > 0)
+       dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED,
+                       "dbus-launch failed to autolaunch D-Bus session: %s",
+                       _dbus_string_get_data (&error_message));
+      else
+       dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED,
+                       "Failed to execute dbus-launch to autolaunch D-Bus session");
+      goto out;
+    }
+
+  retval = TRUE;
+  
+ out:
+  if (retval)
+    _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  else
+    _DBUS_ASSERT_ERROR_IS_SET (error);
+
+  if (address_pipe[0] != -1)
+    close (address_pipe[0]);
+  if (address_pipe[1] != -1)
+    close (address_pipe[1]);
+  if (errors_pipe[0] != -1)
+    close (errors_pipe[0]);
+  if (errors_pipe[1] != -1)
+    close (errors_pipe[1]);
+
+  _dbus_string_free (&uuid);
+  return retval;
+}
+
+/**
+ * Reads the uuid of the machine we're running on from
+ * the dbus configuration. Optionally try to create it
+ * (only root can do this usually).
+ *
+ * On UNIX, reads a file that gets created by dbus-uuidgen
+ * in a post-install script. On Windows, if there's a standard
+ * machine uuid we could just use that, but I can't find one
+ * with the right properties (the hardware profile guid can change
+ * without rebooting I believe). If there's no standard one
+ * we might want to use the registry instead of a file for
+ * this, and I'm not sure how we'd ensure the uuid gets created.
+ *
+ * @param machine_id guid to init with the machine's uuid
+ * @param create_if_not_found try to create the uuid if it doesn't exist
+ * @param error the error return
+ * @returns #FALSE if the error is set
+ */
+dbus_bool_t
+_dbus_read_local_machine_uuid (DBusGUID   *machine_id,
+                               dbus_bool_t create_if_not_found,
+                               DBusError  *error)
+{
+  DBusString filename;
+  _dbus_string_init_const (&filename, DBUS_MACHINE_UUID_FILE);
+  return _dbus_read_uuid_file (&filename, machine_id, create_if_not_found, error);
+}
+
+#define DBUS_UNIX_STANDARD_SESSION_SERVICEDIR "/dbus-1/services"
+#define DBUS_UNIX_STANDARD_SYSTEM_SERVICEDIR "/dbus-1/system-services"
+
+
+/**
+ * Returns the standard directories for a session bus to look for service 
+ * activation files 
+ *
+ * On UNIX this should be the standard xdg freedesktop.org data directories:
+ *
+ * XDG_DATA_HOME=${XDG_DATA_HOME-$HOME/.local/share}
+ * XDG_DATA_DIRS=${XDG_DATA_DIRS-/usr/local/share:/usr/share}
+ *
+ * and
+ *
+ * DBUS_DATADIR
+ *
+ * @param dirs the directory list we are returning
+ * @returns #FALSE on OOM 
+ */
+
+dbus_bool_t 
+_dbus_get_standard_session_servicedirs (DBusList **dirs)
+{
+  const char *xdg_data_home;
+  const char *xdg_data_dirs;
+  DBusString servicedir_path;
+
+  if (!_dbus_string_init (&servicedir_path))
+    return FALSE;
+
+  xdg_data_home = _dbus_getenv ("XDG_DATA_HOME");
+  xdg_data_dirs = _dbus_getenv ("XDG_DATA_DIRS");
+
+  if (xdg_data_dirs != NULL)
+    {
+      if (!_dbus_string_append (&servicedir_path, xdg_data_dirs))
+        goto oom;
+
+      if (!_dbus_string_append (&servicedir_path, ":"))
+        goto oom;
+    }
+  else
+    {
+      if (!_dbus_string_append (&servicedir_path, "/usr/local/share:/usr/share:"))
+        goto oom;
+    }
+
+  /* 
+   * add configured datadir to defaults
+   * this may be the same as an xdg dir
+   * however the config parser should take 
+   * care of duplicates 
+   */
+  if (!_dbus_string_append (&servicedir_path, DBUS_DATADIR":"))
+        goto oom;
+
+  if (xdg_data_home != NULL)
+    {
+      if (!_dbus_string_append (&servicedir_path, xdg_data_home))
+        goto oom;
+    }
+  else
+    {
+      const DBusString *homedir;
+      DBusString local_share;
+
+      if (!_dbus_homedir_from_current_process (&homedir))
+        goto oom;
+       
+      if (!_dbus_string_append (&servicedir_path, _dbus_string_get_const_data (homedir)))
+        goto oom;
+
+      _dbus_string_init_const (&local_share, "/.local/share");
+      if (!_dbus_concat_dir_and_file (&servicedir_path, &local_share))
+        goto oom;
+    }
+
+  if (!_dbus_split_paths_and_append (&servicedir_path, 
+                                     DBUS_UNIX_STANDARD_SESSION_SERVICEDIR, 
+                                     dirs))
+    goto oom;
+
+  _dbus_string_free (&servicedir_path);  
+  return TRUE;
+
+ oom:
+  _dbus_string_free (&servicedir_path);
+  return FALSE;
+}
+
+
+/**
+ * Returns the standard directories for a system bus to look for service 
+ * activation files 
+ *
+ * On UNIX this should be the standard xdg freedesktop.org data directories:
+ *
+ * XDG_DATA_DIRS=${XDG_DATA_DIRS-/usr/local/share:/usr/share}
+ *
+ * and
+ *
+ * DBUS_DATADIR
+ *
+ * On Windows there is no system bus and this function can return nothing.
+ *
+ * @param dirs the directory list we are returning
+ * @returns #FALSE on OOM 
+ */
+
+dbus_bool_t 
+_dbus_get_standard_system_servicedirs (DBusList **dirs)
+{
+  const char *xdg_data_dirs;
+  DBusString servicedir_path;
+
+  if (!_dbus_string_init (&servicedir_path))
+    return FALSE;
+
+  xdg_data_dirs = _dbus_getenv ("XDG_DATA_DIRS");
+
+  if (xdg_data_dirs != NULL)
+    {
+      if (!_dbus_string_append (&servicedir_path, xdg_data_dirs))
+        goto oom;
+
+      if (!_dbus_string_append (&servicedir_path, ":"))
+        goto oom;
+    }
+  else
+    {
+      if (!_dbus_string_append (&servicedir_path, "/usr/local/share:/usr/share:"))
+        goto oom;
+    }
+
+  /* 
+   * add configured datadir to defaults
+   * this may be the same as an xdg dir
+   * however the config parser should take 
+   * care of duplicates 
+   */
+  if (!_dbus_string_append (&servicedir_path, DBUS_DATADIR":"))
+        goto oom;
+
+  if (!_dbus_split_paths_and_append (&servicedir_path, 
+                                     DBUS_UNIX_STANDARD_SYSTEM_SERVICEDIR, 
+                                     dirs))
+    goto oom;
+
+  _dbus_string_free (&servicedir_path);  
+  return TRUE;
+
+ oom:
+  _dbus_string_free (&servicedir_path);
+  return FALSE;
+}
+
+/**
+ * Append the absolute path of the system.conf file
+ * (there is no system bus on Windows so this can just
+ * return FALSE and print a warning or something)
+ * 
+ * @param str the string to append to
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_append_system_config_file (DBusString *str)
+{
+  return _dbus_string_append (str, DBUS_SYSTEM_CONFIG_FILE);
+}
+
+/**
+ * Append the absolute path of the session.conf file.
+ * 
+ * @param str the string to append to
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_append_session_config_file (DBusString *str)
+{
+  return _dbus_string_append (str, DBUS_SESSION_CONFIG_FILE);
+}
+
+/**
+ * Called when the bus daemon is signaled to reload its configuration; any
+ * caches should be nuked. Of course any caches that need explicit reload
+ * are probably broken, but c'est la vie.
+ *
+ * 
+ */
+void
+_dbus_flush_caches (void)
+{
+  _dbus_user_database_flush_system ();
+}
+
+/**
+ * Appends the directory in which a keyring for the given credentials
+ * should be stored.  The credentials should have either a Windows or
+ * UNIX user in them.  The directory should be an absolute path.
+ *
+ * On UNIX the directory is ~/.dbus-keyrings while on Windows it should probably
+ * be something else, since the dotfile convention is not normal on Windows.
+ * 
+ * @param directory string to append directory to
+ * @param credentials credentials the directory should be for
+ *  
+ * @returns #FALSE on no memory
+ */
+dbus_bool_t
+_dbus_append_keyring_directory_for_credentials (DBusString      *directory,
+                                                DBusCredentials *credentials)
+{
+  DBusString homedir;
+  DBusString dotdir;
+  dbus_uid_t uid;
+  
+  _dbus_assert (credentials != NULL);
+  _dbus_assert (!_dbus_credentials_are_anonymous (credentials));
+  
+  if (!_dbus_string_init (&homedir))
+    return FALSE;
+
+  uid = _dbus_credentials_get_unix_uid (credentials);
+  _dbus_assert (uid != DBUS_UID_UNSET);
+
+  if (!_dbus_homedir_from_uid (uid, &homedir))
+    goto failed;
+  
+#ifdef DBUS_BUILD_TESTS
+  {
+    const char *override;
+    
+    override = _dbus_getenv ("DBUS_TEST_HOMEDIR");
+    if (override != NULL && *override != '\0')
+      {
+        _dbus_string_set_length (&homedir, 0);
+        if (!_dbus_string_append (&homedir, override))
+          goto failed;
+
+        _dbus_verbose ("Using fake homedir for testing: %s\n",
+                       _dbus_string_get_const_data (&homedir));
+      }
+    else
+      {
+        static dbus_bool_t already_warned = FALSE;
+        if (!already_warned)
+          {
+            _dbus_warn ("Using your real home directory for testing, set DBUS_TEST_HOMEDIR to avoid\n");
+            already_warned = TRUE;
+          }
+      }
+  }
+#endif
+
+  _dbus_string_init_const (&dotdir, ".dbus-keyrings");
+  if (!_dbus_concat_dir_and_file (&homedir,
+                                  &dotdir))
+    goto failed;
+  
+  if (!_dbus_string_copy (&homedir, 0,
+                          directory, _dbus_string_get_length (directory))) {
+    goto failed;
+  }
+
+  _dbus_string_free (&homedir);
+  return TRUE;
+  
+ failed: 
+  _dbus_string_free (&homedir);
+  return FALSE;
+}
+
+
+/**
+ * See if errno is EAGAIN or EWOULDBLOCK (this has to be done differently
+ * for Winsock so is abstracted)
+ *
+ * @returns #TRUE if errno == EAGAIN or errno == EWOULDBLOCK
+ */
+dbus_bool_t
+_dbus_get_is_errno_eagain_or_ewouldblock (void)
+{
+  return errno == EAGAIN || errno == EWOULDBLOCK;
+}
+
+/* tests in dbus-sysdeps-util.c */
diff --git a/src/dbus/dbus-sysdeps-unix.h b/src/dbus/dbus-sysdeps-unix.h
new file mode 100644 (file)
index 0000000..0005cd8
--- /dev/null
@@ -0,0 +1,134 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sysdeps-unix.h UNIX-specific wrappers around system/libc features (internal to D-Bus implementation)
+ * 
+ * Copyright (C) 2002, 2003, 2006  Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_SYSDEPS_UNIX_H
+#define DBUS_SYSDEPS_UNIX_H
+
+#include <config.h>
+#include <dbus/dbus-sysdeps.h>
+
+#ifdef DBUS_WIN
+#error "Don't include this on Windows"
+#endif
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @defgroup DBusSysdepsUnix UNIX-specific internal API
+ * @ingroup DBusInternals
+ * @brief Internal system-dependent API available on UNIX only
+ * @{
+ */
+
+dbus_bool_t 
+_dbus_close     (int               fd,
+                 DBusError        *error);
+int 
+_dbus_read      (int               fd,
+                 DBusString       *buffer,
+                 int               count);
+int 
+_dbus_write     (int               fd,
+                 const DBusString *buffer,
+                 int               start,
+                 int               len);
+int 
+_dbus_write_two (int               fd,
+                 const DBusString *buffer1,
+                 int               start1,
+                 int               len1,
+                 const DBusString *buffer2,
+                 int               start2,
+                 int               len2);
+
+dbus_bool_t _dbus_open_unix_socket (int              *fd,
+                                    DBusError        *error);
+int _dbus_connect_unix_socket (const char     *path,
+                               dbus_bool_t     abstract,
+                               DBusError      *error);
+int _dbus_listen_unix_socket  (const char     *path,
+                               dbus_bool_t     abstract,
+                               DBusError      *error);
+
+dbus_bool_t _dbus_read_credentials (int               client_fd,
+                                    DBusCredentials  *credentials,
+                                    DBusError        *error);
+dbus_bool_t _dbus_send_credentials (int              server_fd,
+                                    DBusError       *error);
+
+/** Information about a UNIX user */
+typedef struct DBusUserInfo  DBusUserInfo;
+/** Information about a UNIX group */
+typedef struct DBusGroupInfo DBusGroupInfo;
+
+/**
+ * Information about a UNIX user
+ */
+struct DBusUserInfo
+{
+  dbus_uid_t  uid;            /**< UID */
+  dbus_gid_t  primary_gid;    /**< GID */
+  dbus_gid_t *group_ids;      /**< Groups IDs, *including* above primary group */
+  int         n_group_ids;    /**< Size of group IDs array */
+  char       *username;       /**< Username */
+  char       *homedir;        /**< Home directory */
+};
+
+/**
+ * Information about a UNIX group
+ */
+struct DBusGroupInfo
+{
+  dbus_gid_t  gid;            /**< GID */
+  char       *groupname;      /**< Group name */
+};
+
+dbus_bool_t _dbus_user_info_fill     (DBusUserInfo     *info,
+                                      const DBusString *username,
+                                      DBusError        *error);
+dbus_bool_t _dbus_user_info_fill_uid (DBusUserInfo     *info,
+                                      dbus_uid_t        uid,
+                                      DBusError        *error);
+void        _dbus_user_info_free     (DBusUserInfo     *info);
+
+dbus_bool_t _dbus_group_info_fill     (DBusGroupInfo    *info,
+                                       const DBusString *groupname,
+                                       DBusError        *error);
+dbus_bool_t _dbus_group_info_fill_gid (DBusGroupInfo    *info,
+                                       dbus_gid_t        gid,
+                                       DBusError        *error);
+void        _dbus_group_info_free     (DBusGroupInfo    *info);
+
+dbus_uid_t    _dbus_getuid (void);
+dbus_uid_t    _dbus_geteuid (void);
+dbus_gid_t    _dbus_getgid (void);
+
+dbus_bool_t _dbus_parse_uid (const DBusString  *uid_str,
+                             dbus_uid_t        *uid);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_SYSDEPS_UNIX_H */
diff --git a/src/dbus/dbus-sysdeps-util-unix.c b/src/dbus/dbus-sysdeps-util-unix.c
new file mode 100644 (file)
index 0000000..d31e144
--- /dev/null
@@ -0,0 +1,1237 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sysdeps-util-unix.c Would be in dbus-sysdeps-unix.c, but not used in libdbus
+ * 
+ * Copyright (C) 2002, 2003, 2004, 2005  Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-sysdeps.h"
+#include "dbus-sysdeps-unix.h"
+#include "dbus-internals.h"
+#include "dbus-protocol.h"
+#include "dbus-string.h"
+#define DBUS_USERDB_INCLUDES_PRIVATE 1
+#include "dbus-userdb.h"
+#include "dbus-test.h"
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <grp.h>
+#include <sys/socket.h>
+#include <dirent.h>
+#include <sys/un.h>
+#include <syslog.h>
+#ifdef HAVE_LIBAUDIT
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#include <libaudit.h>
+#endif /* HAVE_LIBAUDIT */
+
+#ifdef HAVE_SYS_SYSLIMITS_H
+#include <sys/syslimits.h>
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/**
+ * @addtogroup DBusInternalsUtils
+ * @{
+ */
+
+
+/**
+ * Does the chdir, fork, setsid, etc. to become a daemon process.
+ *
+ * @param pidfile #NULL, or pidfile to create
+ * @param print_pid_pipe pipe to print daemon's pid to, or -1 for none
+ * @param error return location for errors
+ * @param keep_umask #TRUE to keep the original umask
+ * @returns #FALSE on failure
+ */
+dbus_bool_t
+_dbus_become_daemon (const DBusString *pidfile,
+                     DBusPipe         *print_pid_pipe,
+                     DBusError        *error,
+                     dbus_bool_t       keep_umask)
+{
+  const char *s;
+  pid_t child_pid;
+  int dev_null_fd;
+
+  _dbus_verbose ("Becoming a daemon...\n");
+
+  _dbus_verbose ("chdir to /\n");
+  if (chdir ("/") < 0)
+    {
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                      "Could not chdir() to root directory");
+      return FALSE;
+    }
+
+  _dbus_verbose ("forking...\n");
+  switch ((child_pid = fork ()))
+    {
+    case -1:
+      _dbus_verbose ("fork failed\n");
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to fork daemon: %s", _dbus_strerror (errno));
+      return FALSE;
+      break;
+
+    case 0:
+      _dbus_verbose ("in child, closing std file descriptors\n");
+
+      /* silently ignore failures here, if someone
+       * doesn't have /dev/null we may as well try
+       * to continue anyhow
+       */
+      
+      dev_null_fd = open ("/dev/null", O_RDWR);
+      if (dev_null_fd >= 0)
+        {
+          dup2 (dev_null_fd, 0);
+          dup2 (dev_null_fd, 1);
+          
+          s = _dbus_getenv ("DBUS_DEBUG_OUTPUT");
+          if (s == NULL || *s == '\0')
+            dup2 (dev_null_fd, 2);
+          else
+            _dbus_verbose ("keeping stderr open due to DBUS_DEBUG_OUTPUT\n");
+        }
+
+      if (!keep_umask)
+        {
+          /* Get a predictable umask */
+          _dbus_verbose ("setting umask\n");
+          umask (022);
+        }
+
+      _dbus_verbose ("calling setsid()\n");
+      if (setsid () == -1)
+        _dbus_assert_not_reached ("setsid() failed");
+      
+      break;
+
+    default:
+      if (!_dbus_write_pid_to_file_and_pipe (pidfile, print_pid_pipe,
+                                             child_pid, error))
+        {
+          _dbus_verbose ("pid file or pipe write failed: %s\n",
+                         error->message);
+          kill (child_pid, SIGTERM);
+          return FALSE;
+        }
+
+      _dbus_verbose ("parent exiting\n");
+      _exit (0);
+      break;
+    }
+  
+  return TRUE;
+}
+
+
+/**
+ * Creates a file containing the process ID.
+ *
+ * @param filename the filename to write to
+ * @param pid our process ID
+ * @param error return location for errors
+ * @returns #FALSE on failure
+ */
+static dbus_bool_t
+_dbus_write_pid_file (const DBusString *filename,
+                      unsigned long     pid,
+                     DBusError        *error)
+{
+  const char *cfilename;
+  int fd;
+  FILE *f;
+
+  cfilename = _dbus_string_get_const_data (filename);
+  
+  fd = open (cfilename, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0644);
+  
+  if (fd < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to open \"%s\": %s", cfilename,
+                      _dbus_strerror (errno));
+      return FALSE;
+    }
+
+  if ((f = fdopen (fd, "w")) == NULL)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to fdopen fd %d: %s", fd, _dbus_strerror (errno));
+      _dbus_close (fd, NULL);
+      return FALSE;
+    }
+  
+  if (fprintf (f, "%lu\n", pid) < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to write to \"%s\": %s", cfilename,
+                      _dbus_strerror (errno));
+      
+      fclose (f);
+      return FALSE;
+    }
+
+  if (fclose (f) == EOF)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to close \"%s\": %s", cfilename,
+                      _dbus_strerror (errno));
+      return FALSE;
+    }
+  
+  return TRUE;
+}
+
+/**
+ * Writes the given pid_to_write to a pidfile (if non-NULL) and/or to a
+ * pipe (if non-NULL). Does nothing if pidfile and print_pid_pipe are both
+ * NULL.
+ *
+ * @param pidfile the file to write to or #NULL
+ * @param print_pid_pipe the pipe to write to or #NULL
+ * @param pid_to_write the pid to write out
+ * @param error error on failure
+ * @returns FALSE if error is set
+ */
+dbus_bool_t
+_dbus_write_pid_to_file_and_pipe (const DBusString *pidfile,
+                                  DBusPipe         *print_pid_pipe,
+                                  dbus_pid_t        pid_to_write,
+                                  DBusError        *error)
+{
+  if (pidfile)
+    {
+      _dbus_verbose ("writing pid file %s\n", _dbus_string_get_const_data (pidfile));
+      if (!_dbus_write_pid_file (pidfile,
+                                 pid_to_write,
+                                 error))
+        {
+          _dbus_verbose ("pid file write failed\n");
+          _DBUS_ASSERT_ERROR_IS_SET(error);
+          return FALSE;
+        }
+    }
+  else
+    {
+      _dbus_verbose ("No pid file requested\n");
+    }
+
+  if (print_pid_pipe != NULL && _dbus_pipe_is_valid (print_pid_pipe))
+    {
+      DBusString pid;
+      int bytes;
+
+      _dbus_verbose ("writing our pid to pipe %d\n", print_pid_pipe->fd_or_handle);
+      
+      if (!_dbus_string_init (&pid))
+        {
+          _DBUS_SET_OOM (error);
+          return FALSE;
+        }
+         
+      if (!_dbus_string_append_int (&pid, pid_to_write) ||
+          !_dbus_string_append (&pid, "\n"))
+        {
+          _dbus_string_free (&pid);
+          _DBUS_SET_OOM (error);
+          return FALSE;
+        }
+         
+      bytes = _dbus_string_get_length (&pid);
+      if (_dbus_pipe_write (print_pid_pipe, &pid, 0, bytes, error) != bytes)
+        {
+          /* _dbus_pipe_write sets error only on failure, not short write */
+          if (error != NULL && !dbus_error_is_set(error))
+            {
+              dbus_set_error (error, DBUS_ERROR_FAILED,
+                              "Printing message bus PID: did not write enough bytes\n");
+            }
+          _dbus_string_free (&pid);
+          return FALSE;
+        }
+         
+      _dbus_string_free (&pid);
+    }
+  else
+    {
+      _dbus_verbose ("No pid pipe to write to\n");
+    }
+
+  return TRUE;
+}
+
+/**
+ * Verify that after the fork we can successfully change to this user.
+ *
+ * @param user the username given in the daemon configuration
+ * @returns #TRUE if username is valid
+ */
+dbus_bool_t
+_dbus_verify_daemon_user (const char *user)
+{
+  DBusString u;
+
+  _dbus_string_init_const (&u, user);
+
+  return _dbus_get_user_id_and_primary_group (&u, NULL, NULL);
+}
+
+/**
+ * Changes the user and group the bus is running as.
+ *
+ * @param user the user to become
+ * @param error return location for errors
+ * @returns #FALSE on failure
+ */
+dbus_bool_t
+_dbus_change_to_daemon_user  (const char    *user,
+                              DBusError     *error)
+{
+  dbus_uid_t uid;
+  dbus_gid_t gid;
+  DBusString u;
+#ifdef HAVE_LIBAUDIT
+  dbus_bool_t we_were_root;
+  cap_t new_caps;
+#endif
+  
+  _dbus_string_init_const (&u, user);
+  
+  if (!_dbus_get_user_id_and_primary_group (&u, &uid, &gid))
+    {
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                      "User '%s' does not appear to exist?",
+                      user);
+      return FALSE;
+    }
+  
+#ifdef HAVE_LIBAUDIT
+  we_were_root = _dbus_geteuid () == 0;
+  new_caps = NULL;
+  /* have a tmp set of caps that we use to transition to the usr/grp dbus should
+   * run as ... doesn't really help. But keeps people happy.
+   */
+    
+  if (we_were_root)
+    {
+      cap_value_t new_cap_list[] = { CAP_AUDIT_WRITE };
+      cap_value_t tmp_cap_list[] = { CAP_AUDIT_WRITE, CAP_SETUID, CAP_SETGID };
+      cap_t tmp_caps = cap_init();
+        
+      if (!tmp_caps || !(new_caps = cap_init ()))
+        {
+          dbus_set_error (error, DBUS_ERROR_FAILED,
+                          "Failed to initialize drop of capabilities: %s\n",
+                          _dbus_strerror (errno));
+
+          if (tmp_caps)
+            cap_free (tmp_caps);
+
+          return FALSE;
+        }
+
+      /* assume these work... */
+      cap_set_flag (new_caps, CAP_PERMITTED, 1, new_cap_list, CAP_SET);
+      cap_set_flag (new_caps, CAP_EFFECTIVE, 1, new_cap_list, CAP_SET);
+      cap_set_flag (tmp_caps, CAP_PERMITTED, 3, tmp_cap_list, CAP_SET);
+      cap_set_flag (tmp_caps, CAP_EFFECTIVE, 3, tmp_cap_list, CAP_SET);
+      
+      if (prctl (PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1)
+        {
+          dbus_set_error (error, _dbus_error_from_errno (errno),
+                          "Failed to set keep-capabilities: %s\n",
+                          _dbus_strerror (errno));
+          cap_free (tmp_caps);
+          goto fail;
+        }
+        
+      if (cap_set_proc (tmp_caps) == -1)
+        {
+          dbus_set_error (error, DBUS_ERROR_FAILED,
+                          "Failed to drop capabilities: %s\n",
+                          _dbus_strerror (errno));
+          cap_free (tmp_caps);
+          goto fail;
+        }
+      cap_free (tmp_caps);
+    }
+#endif /* HAVE_LIBAUDIT */
+  
+  /* setgroups() only works if we are a privileged process,
+   * so we don't return error on failure; the only possible
+   * failure is that we don't have perms to do it.
+   *
+   * not sure this is right, maybe if setuid()
+   * is going to work then setgroups() should also work.
+   */
+  if (setgroups (0, NULL) < 0)
+    _dbus_warn ("Failed to drop supplementary groups: %s\n",
+                _dbus_strerror (errno));
+  
+  /* Set GID first, or the setuid may remove our permission
+   * to change the GID
+   */
+  if (setgid (gid) < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to set GID to %lu: %s", gid,
+                      _dbus_strerror (errno));
+      goto fail;
+    }
+  
+  if (setuid (uid) < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to set UID to %lu: %s", uid,
+                      _dbus_strerror (errno));
+      goto fail;
+    }
+  
+#ifdef HAVE_LIBAUDIT
+  if (we_were_root)
+    {
+      if (cap_set_proc (new_caps))
+        {
+          dbus_set_error (error, DBUS_ERROR_FAILED,
+                          "Failed to drop capabilities: %s\n",
+                          _dbus_strerror (errno));
+          goto fail;
+        }
+      cap_free (new_caps);
+
+      /* should always work, if it did above */      
+      if (prctl (PR_SET_KEEPCAPS, 0, 0, 0, 0) == -1)
+        {
+          dbus_set_error (error, _dbus_error_from_errno (errno),
+                          "Failed to unset keep-capabilities: %s\n",
+                          _dbus_strerror (errno));
+          return FALSE;
+        }
+    }
+#endif
+
+ return TRUE;
+
+ fail:
+#ifdef HAVE_LIBAUDIT
+ if (!we_were_root)
+   {
+     /* should always work, if it did above */
+     prctl (PR_SET_KEEPCAPS, 0, 0, 0, 0);
+     cap_free (new_caps);
+   }
+#endif
+
+ return FALSE;
+}
+
+void 
+_dbus_init_system_log (void)
+{
+  openlog ("dbus", LOG_PID, LOG_DAEMON);
+}
+
+/**
+ * Log an informative message.  Intended for use primarily by
+ * the system bus.
+ *
+ * @param msg a printf-style format string
+ * @param args arguments for the format string
+ */
+void 
+_dbus_log_info (const char *msg, va_list args)
+{
+  vsyslog (LOG_DAEMON|LOG_NOTICE, msg, args);
+}
+
+/**
+ * Log a security-related message.  Intended for use primarily by
+ * the system bus.
+ *
+ * @param msg a printf-style format string
+ * @param args arguments for the format string
+ */
+void 
+_dbus_log_security (const char *msg, va_list args)
+{
+  vsyslog (LOG_AUTH|LOG_NOTICE, msg, args);
+}
+
+/** Installs a UNIX signal handler
+ *
+ * @param sig the signal to handle
+ * @param handler the handler
+ */
+void
+_dbus_set_signal_handler (int               sig,
+                          DBusSignalHandler handler)
+{
+  struct sigaction act;
+  sigset_t empty_mask;
+  
+  sigemptyset (&empty_mask);
+  act.sa_handler = handler;
+  act.sa_mask    = empty_mask;
+  act.sa_flags   = 0;
+  sigaction (sig,  &act, NULL);
+}
+
+
+/**
+ * Removes a directory; Directory must be empty
+ * 
+ * @param filename directory filename
+ * @param error initialized error object
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_delete_directory (const DBusString *filename,
+                       DBusError        *error)
+{
+  const char *filename_c;
+  
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  filename_c = _dbus_string_get_const_data (filename);
+
+  if (rmdir (filename_c) != 0)
+    {
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                     "Failed to remove directory %s: %s\n",
+                     filename_c, _dbus_strerror (errno));
+      return FALSE;
+    }
+  
+  return TRUE;
+}
+
+/** Checks if a file exists
+*
+* @param file full path to the file
+* @returns #TRUE if file exists
+*/
+dbus_bool_t 
+_dbus_file_exists (const char *file)
+{
+  return (access (file, F_OK) == 0);
+}
+
+/** Checks if user is at the console
+*
+* @param username user to check
+* @param error return location for errors
+* @returns #TRUE is the user is at the consolei and there are no errors
+*/
+dbus_bool_t 
+_dbus_user_at_console (const char *username,
+                       DBusError  *error)
+{
+
+  DBusString f;
+  dbus_bool_t result;
+
+  result = FALSE;
+  if (!_dbus_string_init (&f))
+    {
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+
+  if (!_dbus_string_append (&f, DBUS_CONSOLE_AUTH_DIR))
+    {
+      _DBUS_SET_OOM (error);
+      goto out;
+    }
+
+
+  if (!_dbus_string_append (&f, username))
+    {
+      _DBUS_SET_OOM (error);
+      goto out;
+    }
+
+  result = _dbus_file_exists (_dbus_string_get_const_data (&f));
+
+ out:
+  _dbus_string_free (&f);
+
+  return result;
+}
+
+
+/**
+ * Checks whether the filename is an absolute path
+ *
+ * @param filename the filename
+ * @returns #TRUE if an absolute path
+ */
+dbus_bool_t
+_dbus_path_is_absolute (const DBusString *filename)
+{
+  if (_dbus_string_get_length (filename) > 0)
+    return _dbus_string_get_byte (filename, 0) == '/';
+  else
+    return FALSE;
+}
+
+/**
+ * stat() wrapper.
+ *
+ * @param filename the filename to stat
+ * @param statbuf the stat info to fill in
+ * @param error return location for error
+ * @returns #FALSE if error was set
+ */
+dbus_bool_t
+_dbus_stat (const DBusString *filename,
+            DBusStat         *statbuf,
+            DBusError        *error)
+{
+  const char *filename_c;
+  struct stat sb;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  filename_c = _dbus_string_get_const_data (filename);
+
+  if (stat (filename_c, &sb) < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "%s", _dbus_strerror (errno));
+      return FALSE;
+    }
+
+  statbuf->mode = sb.st_mode;
+  statbuf->nlink = sb.st_nlink;
+  statbuf->uid = sb.st_uid;
+  statbuf->gid = sb.st_gid;
+  statbuf->size = sb.st_size;
+  statbuf->atime = sb.st_atime;
+  statbuf->mtime = sb.st_mtime;
+  statbuf->ctime = sb.st_ctime;
+
+  return TRUE;
+}
+
+
+/**
+ * Internals of directory iterator
+ */
+struct DBusDirIter
+{
+  DIR *d; /**< The DIR* from opendir() */
+  
+};
+
+/**
+ * Open a directory to iterate over.
+ *
+ * @param filename the directory name
+ * @param error exception return object or #NULL
+ * @returns new iterator, or #NULL on error
+ */
+DBusDirIter*
+_dbus_directory_open (const DBusString *filename,
+                      DBusError        *error)
+{
+  DIR *d;
+  DBusDirIter *iter;
+  const char *filename_c;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  filename_c = _dbus_string_get_const_data (filename);
+
+  d = opendir (filename_c);
+  if (d == NULL)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to read directory \"%s\": %s",
+                      filename_c,
+                      _dbus_strerror (errno));
+      return NULL;
+    }
+  iter = dbus_new0 (DBusDirIter, 1);
+  if (iter == NULL)
+    {
+      closedir (d);
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
+                      "Could not allocate memory for directory iterator");
+      return NULL;
+    }
+
+  iter->d = d;
+
+  return iter;
+}
+
+/* Calculate the required buffer size (in bytes) for directory
+ * entries read from the given directory handle.  Return -1 if this
+ * this cannot be done. 
+ *
+ * If you use autoconf, include fpathconf and dirfd in your
+ * AC_CHECK_FUNCS list.  Otherwise use some other method to detect
+ * and use them where available.
+ */
+static dbus_bool_t
+dirent_buf_size(DIR * dirp, size_t *size)
+{
+ long name_max;
+#   if defined(HAVE_FPATHCONF) && defined(_PC_NAME_MAX)
+#      if defined(HAVE_DIRFD)
+          name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
+#      elif defined(HAVE_DDFD)
+          name_max = fpathconf(dirp->dd_fd, _PC_NAME_MAX);
+#      else
+          name_max = fpathconf(dirp->__dd_fd, _PC_NAME_MAX);
+#      endif /* HAVE_DIRFD */
+     if (name_max == -1)
+#           if defined(NAME_MAX)
+            name_max = NAME_MAX;
+#           else
+            return FALSE;
+#           endif
+#   elif defined(MAXNAMELEN)
+     name_max = MAXNAMELEN;
+#   else
+#       if defined(NAME_MAX)
+        name_max = NAME_MAX;
+#       else
+#           error "buffer size for readdir_r cannot be determined"
+#       endif
+#   endif
+  if (size)
+    *size = (size_t)offsetof(struct dirent, d_name) + name_max + 1;
+  else
+    return FALSE;
+
+  return TRUE;
+}
+
+/**
+ * Get next file in the directory. Will not return "." or ".."  on
+ * UNIX. If an error occurs, the contents of "filename" are
+ * undefined. The error is never set if the function succeeds.
+ *
+ * @param iter the iterator
+ * @param filename string to be set to the next file in the dir
+ * @param error return location for error
+ * @returns #TRUE if filename was filled in with a new filename
+ */
+dbus_bool_t
+_dbus_directory_get_next_file (DBusDirIter      *iter,
+                               DBusString       *filename,
+                               DBusError        *error)
+{
+  struct dirent *d, *ent;
+  size_t buf_size;
+  int err;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  if (!dirent_buf_size (iter->d, &buf_size))
+    {
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                      "Can't calculate buffer size when reading directory");
+      return FALSE;
+    }
+
+  d = (struct dirent *)dbus_malloc (buf_size);
+  if (!d)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
+                      "No memory to read directory entry");
+      return FALSE;
+    }
+
+ again:
+  err = readdir_r (iter->d, d, &ent);
+  if (err || !ent)
+    {
+      if (err != 0)
+        dbus_set_error (error,
+                        _dbus_error_from_errno (err),
+                        "%s", _dbus_strerror (err));
+
+      dbus_free (d);
+      return FALSE;
+    }
+  else if (ent->d_name[0] == '.' &&
+           (ent->d_name[1] == '\0' ||
+            (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
+    goto again;
+  else
+    {
+      _dbus_string_set_length (filename, 0);
+      if (!_dbus_string_append (filename, ent->d_name))
+        {
+          dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
+                          "No memory to read directory entry");
+          dbus_free (d);
+          return FALSE;
+        }
+      else
+        {
+          dbus_free (d);
+          return TRUE;
+        }
+    }
+}
+
+/**
+ * Closes a directory iteration.
+ */
+void
+_dbus_directory_close (DBusDirIter *iter)
+{
+  closedir (iter->d);
+  dbus_free (iter);
+}
+
+static dbus_bool_t
+fill_user_info_from_group (struct group  *g,
+                           DBusGroupInfo *info,
+                           DBusError     *error)
+{
+  _dbus_assert (g->gr_name != NULL);
+  
+  info->gid = g->gr_gid;
+  info->groupname = _dbus_strdup (g->gr_name);
+
+  /* info->members = dbus_strdupv (g->gr_mem) */
+  
+  if (info->groupname == NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static dbus_bool_t
+fill_group_info (DBusGroupInfo    *info,
+                 dbus_gid_t        gid,
+                 const DBusString *groupname,
+                 DBusError        *error)
+{
+  const char *group_c_str;
+
+  _dbus_assert (groupname != NULL || gid != DBUS_GID_UNSET);
+  _dbus_assert (groupname == NULL || gid == DBUS_GID_UNSET);
+
+  if (groupname)
+    group_c_str = _dbus_string_get_const_data (groupname);
+  else
+    group_c_str = NULL;
+  
+  /* For now assuming that the getgrnam() and getgrgid() flavors
+   * always correspond to the pwnam flavors, if not we have
+   * to add more configure checks.
+   */
+  
+#if defined (HAVE_POSIX_GETPWNAM_R) || defined (HAVE_NONPOSIX_GETPWNAM_R)
+  {
+    struct group *g;
+    int result;
+    size_t buflen;
+    char *buf;
+    struct group g_str;
+    dbus_bool_t b;
+
+    /* retrieve maximum needed size for buf */
+    buflen = sysconf (_SC_GETGR_R_SIZE_MAX);
+
+    /* sysconf actually returns a long, but everything else expects size_t,
+     * so just recast here.
+     * https://bugs.freedesktop.org/show_bug.cgi?id=17061
+     */
+    if ((long) buflen <= 0)
+      buflen = 1024;
+
+    result = -1;
+    while (1)
+      {
+        buf = dbus_malloc (buflen);
+        if (buf == NULL)
+          {
+            dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+            return FALSE;
+          }
+
+        g = NULL;
+#ifdef HAVE_POSIX_GETPWNAM_R
+        if (group_c_str)
+          result = getgrnam_r (group_c_str, &g_str, buf, buflen,
+                               &g);
+        else
+          result = getgrgid_r (gid, &g_str, buf, buflen,
+                               &g);
+#else
+        g = getgrnam_r (group_c_str, &g_str, buf, buflen);
+        result = 0;
+#endif /* !HAVE_POSIX_GETPWNAM_R */
+        /* Try a bigger buffer if ERANGE was returned:
+           https://bugs.freedesktop.org/show_bug.cgi?id=16727
+        */
+        if (result == ERANGE && buflen < 512 * 1024)
+          {
+            dbus_free (buf);
+            buflen *= 2;
+          }
+        else
+          {
+            break;
+          }
+      }
+
+    if (result == 0 && g == &g_str)
+      {
+        b = fill_user_info_from_group (g, info, error);
+        dbus_free (buf);
+        return b;
+      }
+    else
+      {
+        dbus_set_error (error, _dbus_error_from_errno (errno),
+                        "Group %s unknown or failed to look it up\n",
+                        group_c_str ? group_c_str : "???");
+        dbus_free (buf);
+        return FALSE;
+      }
+  }
+#else /* ! HAVE_GETPWNAM_R */
+  {
+    /* I guess we're screwed on thread safety here */
+    struct group *g;
+
+    g = getgrnam (group_c_str);
+
+    if (g != NULL)
+      {
+        return fill_user_info_from_group (g, info, error);
+      }
+    else
+      {
+        dbus_set_error (error, _dbus_error_from_errno (errno),
+                        "Group %s unknown or failed to look it up\n",
+                        group_c_str ? group_c_str : "???");
+        return FALSE;
+      }
+  }
+#endif  /* ! HAVE_GETPWNAM_R */
+}
+
+/**
+ * Initializes the given DBusGroupInfo struct
+ * with information about the given group name.
+ *
+ * @param info the group info struct
+ * @param groupname name of group
+ * @param error the error return
+ * @returns #FALSE if error is set
+ */
+dbus_bool_t
+_dbus_group_info_fill (DBusGroupInfo    *info,
+                       const DBusString *groupname,
+                       DBusError        *error)
+{
+  return fill_group_info (info, DBUS_GID_UNSET,
+                          groupname, error);
+
+}
+
+/**
+ * Initializes the given DBusGroupInfo struct
+ * with information about the given group ID.
+ *
+ * @param info the group info struct
+ * @param gid group ID
+ * @param error the error return
+ * @returns #FALSE if error is set
+ */
+dbus_bool_t
+_dbus_group_info_fill_gid (DBusGroupInfo *info,
+                           dbus_gid_t     gid,
+                           DBusError     *error)
+{
+  return fill_group_info (info, gid, NULL, error);
+}
+
+/**
+ * Parse a UNIX user from the bus config file. On Windows, this should
+ * simply always fail (just return #FALSE).
+ *
+ * @param username the username text
+ * @param uid_p place to return the uid
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_parse_unix_user_from_config (const DBusString  *username,
+                                   dbus_uid_t        *uid_p)
+{
+  return _dbus_get_user_id (username, uid_p);
+
+}
+
+/**
+ * Parse a UNIX group from the bus config file. On Windows, this should
+ * simply always fail (just return #FALSE).
+ *
+ * @param groupname the groupname text
+ * @param gid_p place to return the gid
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_parse_unix_group_from_config (const DBusString  *groupname,
+                                    dbus_gid_t        *gid_p)
+{
+  return _dbus_get_group_id (groupname, gid_p);
+}
+
+/**
+ * Gets all groups corresponding to the given UNIX user ID. On UNIX,
+ * just calls _dbus_groups_from_uid(). On Windows, should always
+ * fail since we don't know any UNIX groups.
+ *
+ * @param uid the UID
+ * @param group_ids return location for array of group IDs
+ * @param n_group_ids return location for length of returned array
+ * @returns #TRUE if the UID existed and we got some credentials
+ */
+dbus_bool_t
+_dbus_unix_groups_from_uid (dbus_uid_t            uid,
+                            dbus_gid_t          **group_ids,
+                            int                  *n_group_ids)
+{
+  return _dbus_groups_from_uid (uid, group_ids, n_group_ids);
+}
+
+/**
+ * Checks to see if the UNIX user ID is at the console.
+ * Should always fail on Windows (set the error to
+ * #DBUS_ERROR_NOT_SUPPORTED).
+ *
+ * @param uid UID of person to check 
+ * @param error return location for errors
+ * @returns #TRUE if the UID is the same as the console user and there are no errors
+ */
+dbus_bool_t
+_dbus_unix_user_is_at_console (dbus_uid_t         uid,
+                               DBusError         *error)
+{
+  return _dbus_is_console_user (uid, error);
+
+}
+
+/**
+ * Checks to see if the UNIX user ID matches the UID of
+ * the process. Should always return #FALSE on Windows.
+ *
+ * @param uid the UNIX user ID
+ * @returns #TRUE if this uid owns the process.
+ */
+dbus_bool_t
+_dbus_unix_user_is_process_owner (dbus_uid_t uid)
+{
+  return uid == _dbus_geteuid ();
+}
+
+/**
+ * Checks to see if the Windows user SID matches the owner of
+ * the process. Should always return #FALSE on UNIX.
+ *
+ * @param windows_sid the Windows user SID
+ * @returns #TRUE if this user owns the process.
+ */
+dbus_bool_t
+_dbus_windows_user_is_process_owner (const char *windows_sid)
+{
+  return FALSE;
+}
+
+/** @} */ /* End of DBusInternalsUtils functions */
+
+/**
+ * @addtogroup DBusString
+ *
+ * @{
+ */
+/**
+ * Get the directory name from a complete filename
+ * @param filename the filename
+ * @param dirname string to append directory name to
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_get_dirname  (const DBusString *filename,
+                           DBusString       *dirname)
+{
+  int sep;
+  
+  _dbus_assert (filename != dirname);
+  _dbus_assert (filename != NULL);
+  _dbus_assert (dirname != NULL);
+
+  /* Ignore any separators on the end */
+  sep = _dbus_string_get_length (filename);
+  if (sep == 0)
+    return _dbus_string_append (dirname, "."); /* empty string passed in */
+    
+  while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/')
+    --sep;
+
+  _dbus_assert (sep >= 0);
+  
+  if (sep == 0)
+    return _dbus_string_append (dirname, "/");
+  
+  /* Now find the previous separator */
+  _dbus_string_find_byte_backward (filename, sep, '/', &sep);
+  if (sep < 0)
+    return _dbus_string_append (dirname, ".");
+  
+  /* skip multiple separators */
+  while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/')
+    --sep;
+
+  _dbus_assert (sep >= 0);
+  
+  if (sep == 0 &&
+      _dbus_string_get_byte (filename, 0) == '/')
+    return _dbus_string_append (dirname, "/");
+  else
+    return _dbus_string_copy_len (filename, 0, sep - 0,
+                                  dirname, _dbus_string_get_length (dirname));
+}
+/** @} */ /* DBusString stuff */
+
+static void
+string_squash_nonprintable (DBusString *str)
+{
+  char *buf;
+  int i, len; 
+  
+  buf = _dbus_string_get_data (str);
+  len = _dbus_string_get_length (str);
+  
+  for (i = 0; i < len; i++)
+    if (buf[i] == '\0')
+      buf[i] = ' ';
+    else if (buf[i] < 0x20 || buf[i] > 127)
+      buf[i] = '?';
+}
+
+/**
+ * Get a printable string describing the command used to execute
+ * the process with pid.  This string should only be used for
+ * informative purposes such as logging; it may not be trusted.
+ * 
+ * The command is guaranteed to be printable ASCII and no longer
+ * than max_len.
+ * 
+ * @param pid Process id
+ * @param str Append command to this string
+ * @param max_len Maximum length of returned command
+ * @param error return location for errors
+ * @returns #FALSE on error
+ */
+dbus_bool_t 
+_dbus_command_for_pid (unsigned long  pid,
+                       DBusString    *str,
+                       int            max_len,
+                       DBusError     *error)
+{
+  /* This is all Linux-specific for now */
+  DBusString path;
+  DBusString cmdline;
+  int fd;
+  
+  if (!_dbus_string_init (&path)) 
+    {
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+  
+  if (!_dbus_string_init (&cmdline))
+    {
+      _DBUS_SET_OOM (error);
+      _dbus_string_free (&path);
+      return FALSE;
+    }
+  
+  if (!_dbus_string_append_printf (&path, "/proc/%ld/cmdline", pid))
+    goto oom;
+  
+  fd = open (_dbus_string_get_const_data (&path), O_RDONLY);
+  if (fd < 0) 
+    {
+      dbus_set_error (error,
+                      _dbus_error_from_errno (errno),
+                      "Failed to open \"%s\": %s",
+                      _dbus_string_get_const_data (&path),
+                      _dbus_strerror (errno));
+      goto fail;
+    }
+  
+  if (!_dbus_read (fd, &cmdline, max_len))
+    {
+      dbus_set_error (error,
+                      _dbus_error_from_errno (errno),
+                      "Failed to read from \"%s\": %s",
+                      _dbus_string_get_const_data (&path),
+                      _dbus_strerror (errno));      
+      goto fail;
+    }
+  
+  if (!_dbus_close (fd, error))
+    goto fail;
+  
+  string_squash_nonprintable (&cmdline);  
+  
+  if (!_dbus_string_copy (&cmdline, 0, str, _dbus_string_get_length (str)))
+    goto oom;
+  
+  _dbus_string_free (&cmdline);  
+  _dbus_string_free (&path);
+  return TRUE;
+oom:
+  _DBUS_SET_OOM (error);
+fail:
+  _dbus_string_free (&cmdline);
+  _dbus_string_free (&path);
+  return FALSE;
+}
+
diff --git a/src/dbus/dbus-sysdeps-util.c b/src/dbus/dbus-sysdeps-util.c
new file mode 100644 (file)
index 0000000..448f5c4
--- /dev/null
@@ -0,0 +1,176 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sysdeps-util.c Tests for dbus-sysdeps.h API
+ * 
+ * Copyright (C) 2002, 2003, 2004, 2005  Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-sysdeps.h"
+#include "dbus-internals.h"
+#include "dbus-string.h"
+#include "dbus-test.h"
+
+#ifdef DBUS_BUILD_TESTS
+#include <stdlib.h>
+static void
+check_dirname (const char *filename,
+               const char *dirname)
+{
+  DBusString f, d;
+  
+  _dbus_string_init_const (&f, filename);
+
+  if (!_dbus_string_init (&d))
+    _dbus_assert_not_reached ("no memory");
+
+  if (!_dbus_string_get_dirname (&f, &d))
+    _dbus_assert_not_reached ("no memory");
+
+  if (!_dbus_string_equal_c_str (&d, dirname))
+    {
+      _dbus_warn ("For filename \"%s\" got dirname \"%s\" and expected \"%s\"\n",
+                  filename,
+                  _dbus_string_get_const_data (&d),
+                  dirname);
+      exit (1);
+    }
+
+  _dbus_string_free (&d);
+}
+
+static void
+check_path_absolute (const char *path,
+                     dbus_bool_t expected)
+{
+  DBusString p;
+
+  _dbus_string_init_const (&p, path);
+
+  if (_dbus_path_is_absolute (&p) != expected)
+    {
+      _dbus_warn ("For path \"%s\" expected absolute = %d got %d\n",
+                  path, expected, _dbus_path_is_absolute (&p));
+      exit (1);
+    }
+}
+
+/**
+ * Unit test for dbus-sysdeps.c.
+ * 
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_sysdeps_test (void)
+{
+  DBusString str;
+  double val;
+  int pos;
+
+#ifdef DBUS_WIN
+  check_dirname ("foo\\bar", "foo");
+  check_dirname ("foo\\\\bar", "foo");
+  check_dirname ("foo/\\/bar", "foo");
+  check_dirname ("foo\\bar/", "foo");
+  check_dirname ("foo//bar\\", "foo");
+  check_dirname ("foo\\bar/", "foo");
+  check_dirname ("foo/bar\\\\", "foo");
+  check_dirname ("\\foo", "\\");
+  check_dirname ("\\\\foo", "\\");
+  check_dirname ("\\", "\\");
+  check_dirname ("\\\\", "\\");
+  check_dirname ("\\/", "\\");
+  check_dirname ("/\\/", "/");
+  check_dirname ("c:\\foo\\bar", "c:\\foo");
+  check_dirname ("c:\\foo", "c:\\");
+  check_dirname ("c:/foo", "c:/");
+  check_dirname ("c:\\", "c:\\");
+  check_dirname ("c:/", "c:/");
+  check_dirname ("", ".");  
+#else  
+  check_dirname ("foo", ".");
+  check_dirname ("foo/bar", "foo");
+  check_dirname ("foo//bar", "foo");
+  check_dirname ("foo///bar", "foo");
+  check_dirname ("foo/bar/", "foo");
+  check_dirname ("foo//bar/", "foo");
+  check_dirname ("foo///bar/", "foo");
+  check_dirname ("foo/bar//", "foo");
+  check_dirname ("foo//bar////", "foo");
+  check_dirname ("foo///bar///////", "foo");
+  check_dirname ("/foo", "/");
+  check_dirname ("////foo", "/");
+  check_dirname ("/foo/bar", "/foo");
+  check_dirname ("/foo//bar", "/foo");
+  check_dirname ("/foo///bar", "/foo");
+  check_dirname ("/", "/");
+  check_dirname ("///", "/");
+  check_dirname ("", ".");  
+#endif
+
+  _dbus_string_init_const (&str, "3.5");
+  if (!_dbus_string_parse_double (&str,
+                                 0, &val, &pos))
+    {
+      _dbus_warn ("Failed to parse double");
+      exit (1);
+    }
+  if (ABS(3.5 - val) > 1e-6)
+    {
+      _dbus_warn ("Failed to parse 3.5 correctly, got: %f", val);
+      exit (1);
+    }
+  if (pos != 3)
+    {
+      _dbus_warn ("_dbus_string_parse_double of \"3.5\" returned wrong position %d", pos);
+      exit (1);
+    }
+
+  _dbus_string_init_const (&str, "0xff");
+  if (_dbus_string_parse_double (&str,
+                                 0, &val, &pos))
+    {
+      _dbus_warn ("Should not have parsed hex as double\n");
+      exit (1);
+    }
+
+#ifdef DBUS_WIN
+  check_path_absolute ("c:/", TRUE);
+  check_path_absolute ("c:/foo", TRUE);
+  check_path_absolute ("", FALSE);
+  check_path_absolute ("foo", FALSE);
+  check_path_absolute ("foo/bar", FALSE);
+  check_path_absolute ("", FALSE);
+  check_path_absolute ("foo\\bar", FALSE);
+  check_path_absolute ("c:\\", TRUE);
+  check_path_absolute ("c:\\foo", TRUE);
+  check_path_absolute ("c:", TRUE);
+  check_path_absolute ("c:\\foo\\bar", TRUE);
+  check_path_absolute ("\\", TRUE);
+  check_path_absolute ("/", TRUE);
+#else  
+  check_path_absolute ("/", TRUE);
+  check_path_absolute ("/foo", TRUE);
+  check_path_absolute ("", FALSE);
+  check_path_absolute ("foo", FALSE);
+  check_path_absolute ("foo/bar", FALSE);
+#endif
+  
+  return TRUE;
+}
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-sysdeps.c b/src/dbus/dbus-sysdeps.c
new file mode 100644 (file)
index 0000000..00a1a3d
--- /dev/null
@@ -0,0 +1,1092 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sysdeps.c Wrappers around system/libc features shared between UNIX and Windows (internal to D-Bus implementation)
+ * 
+ * Copyright (C) 2002, 2003, 2006  Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-internals.h"
+#include "dbus-sysdeps.h"
+#include "dbus-threads.h"
+#include "dbus-protocol.h"
+#include "dbus-string.h"
+#include "dbus-list.h"
+
+/* NOTE: If you include any unix/windows-specific headers here, you are probably doing something
+ * wrong and should be putting some code in dbus-sysdeps-unix.c or dbus-sysdeps-win.c.
+ *
+ * These are the standard ANSI C headers...
+ */
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+/* This is UNIX-specific (on windows it's just in stdlib.h I believe)
+ * but OK since the same stuff does exist on Windows in stdlib.h
+ * and covered by a configure check.
+ */
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+_DBUS_DEFINE_GLOBAL_LOCK (win_fds);
+_DBUS_DEFINE_GLOBAL_LOCK (sid_atom_cache);
+_DBUS_DEFINE_GLOBAL_LOCK (system_users);
+
+extern char **environ;
+
+/**
+ * @defgroup DBusSysdeps Internal system-dependent API
+ * @ingroup DBusInternals
+ * @brief Internal system-dependent API available on UNIX and Windows
+ *
+ * The system-dependent API has a dual purpose. First, it encapsulates
+ * all usage of operating system APIs for ease of auditing and to
+ * avoid cluttering the rest of the code with bizarre OS quirks and
+ * headers. Second, it abstracts different operating system APIs for
+ * portability.
+ * 
+ * @{
+ */
+
+/**
+ * Aborts the program with SIGABRT (dumping core).
+ */
+void
+_dbus_abort (void)
+{
+  const char *s;
+  
+  _dbus_print_backtrace ();
+  
+  s = _dbus_getenv ("DBUS_BLOCK_ON_ABORT");
+  if (s && *s)
+    {
+      /* don't use _dbus_warn here since it can _dbus_abort() */
+      fprintf (stderr, "  Process %lu sleeping for gdb attach\n", _dbus_pid_for_log ());
+      _dbus_sleep_milliseconds (1000 * 180);
+    }
+  
+  abort ();
+  _dbus_exit (1); /* in case someone manages to ignore SIGABRT ? */
+}
+
+/**
+ * Wrapper for setenv(). If the value is #NULL, unsets
+ * the environment variable.
+ *
+ * There is an unfixable memleak in that it is unsafe to
+ * free memory malloced for use with setenv. This is because
+ * we can not rely on internal implementation details of
+ * the underlying libc library.
+ *
+ * @param varname name of environment variable
+ * @param value value of environment variable
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_setenv (const char *varname,
+              const char *value)
+{
+  _dbus_assert (varname != NULL);
+  
+  if (value == NULL)
+    {
+#ifdef HAVE_UNSETENV
+      unsetenv (varname);
+      return TRUE;
+#else
+      char *putenv_value;
+      size_t len;
+
+      len = strlen (varname);
+
+      /* Use system malloc to avoid memleaks that dbus_malloc
+       * will get upset about.
+       */
+      
+      putenv_value = malloc (len + 2);
+      if (putenv_value == NULL)
+        return FALSE;
+
+      strcpy (putenv_value, varname);
+#if defined(DBUS_WIN)
+      strcat (putenv_value, "=");
+#endif
+      
+      return (putenv (putenv_value) == 0);
+#endif
+    }
+  else
+    {
+#ifdef HAVE_SETENV
+      return (setenv (varname, value, TRUE) == 0);
+#else
+      char *putenv_value;
+      size_t len;
+      size_t varname_len;
+      size_t value_len;
+
+      varname_len = strlen (varname);
+      value_len = strlen (value);
+      
+      len = varname_len + value_len + 1 /* '=' */ ;
+
+      /* Use system malloc to avoid memleaks that dbus_malloc
+       * will get upset about.
+       */
+      
+      putenv_value = malloc (len + 1);
+      if (putenv_value == NULL)
+        return FALSE;
+
+      strcpy (putenv_value, varname);
+      strcpy (putenv_value + varname_len, "=");
+      strcpy (putenv_value + varname_len + 1, value);
+      
+      return (putenv (putenv_value) == 0);
+#endif
+    }
+}
+
+/**
+ * Wrapper for getenv().
+ *
+ * @param varname name of environment variable
+ * @returns value of environment variable or #NULL if unset
+ */
+const char*
+_dbus_getenv (const char *varname)
+{  
+  return getenv (varname);
+}
+
+/**
+ * Wrapper for clearenv().
+ *
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_clearenv (void)
+{
+  dbus_bool_t rc = TRUE;
+
+#ifdef HAVE_CLEARENV
+  if (clearenv () != 0)
+     rc = FALSE;
+#else
+
+  if (environ != NULL)
+    environ[0] = NULL;
+#endif
+
+  return rc;
+}
+
+/**
+ * Gets a #NULL-terminated list of key=value pairs from the
+ * environment. Use dbus_free_string_array to free it.
+ *
+ * @returns the environment or #NULL on OOM
+ */
+char **
+_dbus_get_environment (void)
+{
+  int i, length;
+  char **environment;
+
+  _dbus_assert (environ != NULL);
+
+  for (length = 0; environ[length] != NULL; length++);
+
+  /* Add one for NULL */
+  length++;
+
+  environment = dbus_new0 (char *, length);
+
+  if (environment == NULL)
+    return NULL;
+
+  for (i = 0; environ[i] != NULL; i++)
+    {
+      environment[i] = _dbus_strdup (environ[i]);
+
+      if (environment[i] == NULL)
+        break;
+    }
+
+  if (environ[i] != NULL)
+    {
+      dbus_free_string_array (environment);
+      environment = NULL;
+    }
+
+  return environment;
+}
+
+/*
+ * init a pipe instance.
+ *
+ * @param pipe the pipe
+ * @param fd the file descriptor to init from 
+ */
+void
+_dbus_pipe_init (DBusPipe *pipe,
+                 int       fd)
+{
+  pipe->fd_or_handle = fd;
+}
+
+/**
+ * init a pipe with stdout
+ *
+ * @param pipe the pipe
+ */
+void
+_dbus_pipe_init_stdout (DBusPipe *pipe)
+{
+  _dbus_pipe_init (pipe, 1);
+}
+
+/**
+ * check if a pipe is valid; pipes can be set invalid, similar to
+ * a -1 file descriptor.
+ *
+ * @param pipe the pipe instance
+ * @returns #FALSE if pipe is not valid
+ */
+dbus_bool_t
+_dbus_pipe_is_valid(DBusPipe *pipe)
+{
+  return pipe->fd_or_handle >= 0;
+}
+
+/**
+ * Check if a pipe is stdout or stderr.
+ *
+ * @param pipe the pipe instance
+ * @returns #TRUE if pipe is one of the standard out/err channels
+ */
+dbus_bool_t
+_dbus_pipe_is_stdout_or_stderr (DBusPipe *pipe)
+{
+  return pipe->fd_or_handle == 1 || pipe->fd_or_handle == 2;
+}
+
+/**
+ * Initializes a pipe to an invalid value.
+ * @param pipe the pipe
+ */
+void
+_dbus_pipe_invalidate (DBusPipe *pipe)
+{
+  pipe->fd_or_handle = -1;
+}
+
+/**
+ * Split paths into a list of char strings
+ * 
+ * @param dirs string with pathes 
+ * @param suffix string concated to each path in dirs
+ * @param dir_list contains a list of splitted pathes
+ * return #TRUE is pathes could be splittes,#FALSE in oom case 
+ */
+dbus_bool_t
+_dbus_split_paths_and_append (DBusString *dirs, 
+                              const char *suffix, 
+                              DBusList  **dir_list)
+{
+   int start;
+   int i;
+   int len;
+   char *cpath;
+   DBusString file_suffix;
+
+   start = 0;
+   i = 0;
+
+   _dbus_string_init_const (&file_suffix, suffix);
+
+   len = _dbus_string_get_length (dirs);
+
+   while (_dbus_string_find (dirs, start, _DBUS_PATH_SEPARATOR, &i))
+     {
+       DBusString path;
+
+       if (!_dbus_string_init (&path))
+          goto oom;
+
+       if (!_dbus_string_copy_len (dirs,
+                                   start,
+                                   i - start,
+                                   &path,
+                                   0))
+          {
+            _dbus_string_free (&path);
+            goto oom;
+          }
+
+        _dbus_string_chop_white (&path);
+
+        /* check for an empty path */
+        if (_dbus_string_get_length (&path) == 0)
+          goto next;
+
+        if (!_dbus_concat_dir_and_file (&path,
+                                        &file_suffix))
+          {
+            _dbus_string_free (&path);
+            goto oom;
+          }
+
+        if (!_dbus_string_copy_data(&path, &cpath))
+          {
+            _dbus_string_free (&path);
+            goto oom;
+          }
+
+        if (!_dbus_list_append (dir_list, cpath))
+          {
+            _dbus_string_free (&path);              
+            dbus_free (cpath);
+            goto oom;
+          }
+
+       next:
+        _dbus_string_free (&path);
+        start = i + 1;
+    } 
+      
+  if (start != len)
+    { 
+      DBusString path;
+
+      if (!_dbus_string_init (&path))
+        goto oom;
+
+      if (!_dbus_string_copy_len (dirs,
+                                  start,
+                                  len - start,
+                                  &path,
+                                  0))
+        {
+          _dbus_string_free (&path);
+          goto oom;
+        }
+
+      if (!_dbus_concat_dir_and_file (&path,
+                                      &file_suffix))
+        {
+          _dbus_string_free (&path);
+          goto oom;
+        }
+
+      if (!_dbus_string_copy_data(&path, &cpath))
+        {
+          _dbus_string_free (&path);
+          goto oom;
+        }
+
+      if (!_dbus_list_append (dir_list, cpath))
+        {
+          _dbus_string_free (&path);              
+          dbus_free (cpath);
+          goto oom;
+        }
+
+      _dbus_string_free (&path); 
+    }
+
+  return TRUE;
+
+ oom:
+  _dbus_list_foreach (dir_list, (DBusForeachFunction)dbus_free, NULL); 
+  _dbus_list_clear (dir_list);
+  return FALSE;
+}
+
+/** @} */
+
+/**
+ * @addtogroup DBusString
+ *
+ * @{
+ */
+/**
+ * Appends an integer to a DBusString.
+ * 
+ * @param str the string
+ * @param value the integer value
+ * @returns #FALSE if not enough memory or other failure.
+ */
+dbus_bool_t
+_dbus_string_append_int (DBusString *str,
+                         long        value)
+{
+  /* this calculation is from comp.lang.c faq */
+#define MAX_LONG_LEN ((sizeof (long) * 8 + 2) / 3 + 1)  /* +1 for '-' */
+  int orig_len;
+  int i;
+  char *buf;
+  
+  orig_len = _dbus_string_get_length (str);
+
+  if (!_dbus_string_lengthen (str, MAX_LONG_LEN))
+    return FALSE;
+
+  buf = _dbus_string_get_data_len (str, orig_len, MAX_LONG_LEN);
+
+  snprintf (buf, MAX_LONG_LEN, "%ld", value);
+
+  i = 0;
+  while (*buf)
+    {
+      ++buf;
+      ++i;
+    }
+  
+  _dbus_string_shorten (str, MAX_LONG_LEN - i);
+  
+  return TRUE;
+}
+
+/**
+ * Appends an unsigned integer to a DBusString.
+ * 
+ * @param str the string
+ * @param value the integer value
+ * @returns #FALSE if not enough memory or other failure.
+ */
+dbus_bool_t
+_dbus_string_append_uint (DBusString    *str,
+                          unsigned long  value)
+{
+  /* this is wrong, but definitely on the high side. */
+#define MAX_ULONG_LEN (MAX_LONG_LEN * 2)
+  int orig_len;
+  int i;
+  char *buf;
+  
+  orig_len = _dbus_string_get_length (str);
+
+  if (!_dbus_string_lengthen (str, MAX_ULONG_LEN))
+    return FALSE;
+
+  buf = _dbus_string_get_data_len (str, orig_len, MAX_ULONG_LEN);
+
+  snprintf (buf, MAX_ULONG_LEN, "%lu", value);
+
+  i = 0;
+  while (*buf)
+    {
+      ++buf;
+      ++i;
+    }
+  
+  _dbus_string_shorten (str, MAX_ULONG_LEN - i);
+  
+  return TRUE;
+}
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Appends a double to a DBusString.
+ * 
+ * @param str the string
+ * @param value the floating point value
+ * @returns #FALSE if not enough memory or other failure.
+ */
+dbus_bool_t
+_dbus_string_append_double (DBusString *str,
+                            double      value)
+{
+#define MAX_DOUBLE_LEN 64 /* this is completely made up :-/ */
+  int orig_len;
+  char *buf;
+  int i;
+  
+  orig_len = _dbus_string_get_length (str);
+
+  if (!_dbus_string_lengthen (str, MAX_DOUBLE_LEN))
+    return FALSE;
+
+  buf = _dbus_string_get_data_len (str, orig_len, MAX_DOUBLE_LEN);
+
+  snprintf (buf, MAX_LONG_LEN, "%g", value);
+
+  i = 0;
+  while (*buf)
+    {
+      ++buf;
+      ++i;
+    }
+  
+  _dbus_string_shorten (str, MAX_DOUBLE_LEN - i);
+  
+  return TRUE;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * Parses an integer contained in a DBusString. Either return parameter
+ * may be #NULL if you aren't interested in it. The integer is parsed
+ * and stored in value_return. Return parameters are not initialized
+ * if the function returns #FALSE.
+ *
+ * @param str the string
+ * @param start the byte index of the start of the integer
+ * @param value_return return location of the integer value or #NULL
+ * @param end_return return location of the end of the integer, or #NULL
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_string_parse_int (const DBusString *str,
+                        int               start,
+                        long             *value_return,
+                        int              *end_return)
+{
+  long v;
+  const char *p;
+  char *end;
+
+  p = _dbus_string_get_const_data_len (str, start,
+                                       _dbus_string_get_length (str) - start);
+
+  end = NULL;
+  errno = 0;
+  v = strtol (p, &end, 0);
+  if (end == NULL || end == p || errno != 0)
+    return FALSE;
+
+  if (value_return)
+    *value_return = v;
+  if (end_return)
+    *end_return = start + (end - p);
+
+  return TRUE;
+}
+
+/**
+ * Parses an unsigned integer contained in a DBusString. Either return
+ * parameter may be #NULL if you aren't interested in it. The integer
+ * is parsed and stored in value_return. Return parameters are not
+ * initialized if the function returns #FALSE.
+ *
+ * @param str the string
+ * @param start the byte index of the start of the integer
+ * @param value_return return location of the integer value or #NULL
+ * @param end_return return location of the end of the integer, or #NULL
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_string_parse_uint (const DBusString *str,
+                         int               start,
+                         unsigned long    *value_return,
+                         int              *end_return)
+{
+  unsigned long v;
+  const char *p;
+  char *end;
+
+  p = _dbus_string_get_const_data_len (str, start,
+                                       _dbus_string_get_length (str) - start);
+
+  end = NULL;
+  errno = 0;
+  v = strtoul (p, &end, 0);
+  if (end == NULL || end == p || errno != 0)
+    return FALSE;
+
+  if (value_return)
+    *value_return = v;
+  if (end_return)
+    *end_return = start + (end - p);
+
+  return TRUE;
+}
+
+#ifdef DBUS_BUILD_TESTS
+static dbus_bool_t
+ascii_isspace (char c)
+{
+  return (c == ' ' ||
+         c == '\f' ||
+         c == '\n' ||
+         c == '\r' ||
+         c == '\t' ||
+         c == '\v');
+}
+#endif /* DBUS_BUILD_TESTS */
+
+#ifdef DBUS_BUILD_TESTS
+static dbus_bool_t
+ascii_isdigit (char c)
+{
+  return c >= '0' && c <= '9';
+}
+#endif /* DBUS_BUILD_TESTS */
+
+#ifdef DBUS_BUILD_TESTS
+static dbus_bool_t
+ascii_isxdigit (char c)
+{
+  return (ascii_isdigit (c) ||
+         (c >= 'a' && c <= 'f') ||
+         (c >= 'A' && c <= 'F'));
+}
+#endif /* DBUS_BUILD_TESTS */
+
+#ifdef DBUS_BUILD_TESTS
+/* Calls strtod in a locale-independent fashion, by looking at
+ * the locale data and patching the decimal comma to a point.
+ *
+ * Relicensed from glib.
+ */
+static double
+ascii_strtod (const char *nptr,
+             char      **endptr)
+{
+  /* FIXME: The Win32 C library's strtod() doesn't handle hex.
+   * Presumably many Unixes don't either.
+   */
+
+  char *fail_pos;
+  double val;
+  struct lconv *locale_data;
+  const char *decimal_point;
+  int decimal_point_len;
+  const char *p, *decimal_point_pos;
+  const char *end = NULL; /* Silence gcc */
+
+  fail_pos = NULL;
+
+  locale_data = localeconv ();
+  decimal_point = locale_data->decimal_point;
+  decimal_point_len = strlen (decimal_point);
+
+  _dbus_assert (decimal_point_len != 0);
+  
+  decimal_point_pos = NULL;
+  if (decimal_point[0] != '.' ||
+      decimal_point[1] != 0)
+    {
+      p = nptr;
+      /* Skip leading space */
+      while (ascii_isspace (*p))
+       p++;
+      
+      /* Skip leading optional sign */
+      if (*p == '+' || *p == '-')
+       p++;
+      
+      if (p[0] == '0' &&
+         (p[1] == 'x' || p[1] == 'X'))
+       {
+         p += 2;
+         /* HEX - find the (optional) decimal point */
+         
+         while (ascii_isxdigit (*p))
+           p++;
+         
+         if (*p == '.')
+           {
+             decimal_point_pos = p++;
+             
+             while (ascii_isxdigit (*p))
+               p++;
+             
+             if (*p == 'p' || *p == 'P')
+               p++;
+             if (*p == '+' || *p == '-')
+               p++;
+             while (ascii_isdigit (*p))
+               p++;
+             end = p;
+           }
+       }
+      else
+       {
+         while (ascii_isdigit (*p))
+           p++;
+         
+         if (*p == '.')
+           {
+             decimal_point_pos = p++;
+             
+             while (ascii_isdigit (*p))
+               p++;
+             
+             if (*p == 'e' || *p == 'E')
+               p++;
+             if (*p == '+' || *p == '-')
+               p++;
+             while (ascii_isdigit (*p))
+               p++;
+             end = p;
+           }
+       }
+      /* For the other cases, we need not convert the decimal point */
+    }
+
+  /* Set errno to zero, so that we can distinguish zero results
+     and underflows */
+  errno = 0;
+  
+  if (decimal_point_pos)
+    {
+      char *copy, *c;
+
+      /* We need to convert the '.' to the locale specific decimal point */
+      copy = dbus_malloc (end - nptr + 1 + decimal_point_len);
+      
+      c = copy;
+      memcpy (c, nptr, decimal_point_pos - nptr);
+      c += decimal_point_pos - nptr;
+      memcpy (c, decimal_point, decimal_point_len);
+      c += decimal_point_len;
+      memcpy (c, decimal_point_pos + 1, end - (decimal_point_pos + 1));
+      c += end - (decimal_point_pos + 1);
+      *c = 0;
+
+      val = strtod (copy, &fail_pos);
+
+      if (fail_pos)
+       {
+         if (fail_pos > decimal_point_pos)
+           fail_pos = (char *)nptr + (fail_pos - copy) - (decimal_point_len - 1);
+         else
+           fail_pos = (char *)nptr + (fail_pos - copy);
+       }
+      
+      dbus_free (copy);
+         
+    }
+  else
+    val = strtod (nptr, &fail_pos);
+
+  if (endptr)
+    *endptr = fail_pos;
+  
+  return val;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Parses a floating point number contained in a DBusString. Either
+ * return parameter may be #NULL if you aren't interested in it. The
+ * integer is parsed and stored in value_return. Return parameters are
+ * not initialized if the function returns #FALSE.
+ *
+ * @param str the string
+ * @param start the byte index of the start of the float
+ * @param value_return return location of the float value or #NULL
+ * @param end_return return location of the end of the float, or #NULL
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_string_parse_double (const DBusString *str,
+                           int               start,
+                           double           *value_return,
+                           int              *end_return)
+{
+  double v;
+  const char *p;
+  char *end;
+
+  p = _dbus_string_get_const_data_len (str, start,
+                                       _dbus_string_get_length (str) - start);
+
+  /* parsing hex works on linux but isn't portable, so intercept it
+   * here to get uniform behavior.
+   */
+  if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
+    return FALSE;
+  
+  end = NULL;
+  errno = 0;
+  v = ascii_strtod (p, &end);
+  if (end == NULL || end == p || errno != 0)
+    return FALSE;
+
+  if (value_return)
+    *value_return = v;
+  if (end_return)
+    *end_return = start + (end - p);
+
+  return TRUE;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/** @} */ /* DBusString group */
+
+/**
+ * @addtogroup DBusInternalsUtils
+ * @{
+ */
+
+void
+_dbus_generate_pseudorandom_bytes_buffer (char *buffer,
+                                          int   n_bytes)
+{
+  long tv_usec;
+  int i;
+  
+  /* fall back to pseudorandom */
+  _dbus_verbose ("Falling back to pseudorandom for %d bytes\n",
+                 n_bytes);
+  
+  _dbus_get_current_time (NULL, &tv_usec);
+  srand (tv_usec);
+  
+  i = 0;
+  while (i < n_bytes)
+    {
+      double r;
+      unsigned int b;
+          
+      r = rand ();
+      b = (r / (double) RAND_MAX) * 255.0;
+
+      buffer[i] = b;
+
+      ++i;
+    }
+}
+
+/**
+ * Fills n_bytes of the given buffer with random bytes.
+ *
+ * @param buffer an allocated buffer
+ * @param n_bytes the number of bytes in buffer to write to
+ */
+void
+_dbus_generate_random_bytes_buffer (char *buffer,
+                                    int   n_bytes)
+{
+  DBusString str;
+
+  if (!_dbus_string_init (&str))
+    {
+      _dbus_generate_pseudorandom_bytes_buffer (buffer, n_bytes);
+      return;
+    }
+
+  if (!_dbus_generate_random_bytes (&str, n_bytes))
+    {
+      _dbus_string_free (&str);
+      _dbus_generate_pseudorandom_bytes_buffer (buffer, n_bytes);
+      return;
+    }
+
+  _dbus_string_copy_to_buffer (&str, buffer, n_bytes);
+
+  _dbus_string_free (&str);
+}
+
+/**
+ * Generates the given number of random bytes, where the bytes are
+ * chosen from the alphanumeric ASCII subset.
+ *
+ * @param str the string
+ * @param n_bytes the number of random ASCII bytes to append to string
+ * @returns #TRUE on success, #FALSE if no memory or other failure
+ */
+dbus_bool_t
+_dbus_generate_random_ascii (DBusString *str,
+                             int         n_bytes)
+{
+  static const char letters[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
+  int i;
+  int len;
+  
+  if (!_dbus_generate_random_bytes (str, n_bytes))
+    return FALSE;
+  
+  len = _dbus_string_get_length (str);
+  i = len - n_bytes;
+  while (i < len)
+    {
+      _dbus_string_set_byte (str, i,
+                             letters[_dbus_string_get_byte (str, i) %
+                                     (sizeof (letters) - 1)]);
+
+      ++i;
+    }
+
+  _dbus_assert (_dbus_string_validate_ascii (str, len - n_bytes,
+                                             n_bytes));
+
+  return TRUE;
+}
+
+/**
+ * Converts a UNIX or Windows errno
+ * into a #DBusError name.
+ *
+ * @todo should cover more errnos, specifically those
+ * from open().
+ * 
+ * @param error_number the errno.
+ * @returns an error name
+ */
+const char*
+_dbus_error_from_errno (int error_number)
+{
+  switch (error_number)
+    {
+    case 0:
+      return DBUS_ERROR_FAILED;
+      
+#ifdef EPROTONOSUPPORT
+    case EPROTONOSUPPORT:
+      return DBUS_ERROR_NOT_SUPPORTED;
+#endif
+#ifdef EAFNOSUPPORT
+    case EAFNOSUPPORT:
+      return DBUS_ERROR_NOT_SUPPORTED;
+#endif
+#ifdef ENFILE
+    case ENFILE:
+      return DBUS_ERROR_LIMITS_EXCEEDED; /* kernel out of memory */
+#endif
+#ifdef EMFILE
+    case EMFILE:
+      return DBUS_ERROR_LIMITS_EXCEEDED;
+#endif
+#ifdef EACCES
+    case EACCES:
+      return DBUS_ERROR_ACCESS_DENIED;
+#endif
+#ifdef EPERM
+    case EPERM:
+      return DBUS_ERROR_ACCESS_DENIED;
+#endif
+#ifdef ENOBUFS
+    case ENOBUFS:
+      return DBUS_ERROR_NO_MEMORY;
+#endif
+#ifdef ENOMEM
+    case ENOMEM:
+      return DBUS_ERROR_NO_MEMORY;
+#endif
+#ifdef EINVAL
+    case EINVAL:
+      return DBUS_ERROR_FAILED;
+#endif
+#ifdef EBADF
+    case EBADF:
+      return DBUS_ERROR_FAILED;
+#endif
+#ifdef EFAULT
+    case EFAULT:
+      return DBUS_ERROR_FAILED;
+#endif
+#ifdef ENOTSOCK
+    case ENOTSOCK:
+      return DBUS_ERROR_FAILED;
+#endif
+#ifdef EISCONN
+    case EISCONN:
+      return DBUS_ERROR_FAILED;
+#endif
+#ifdef ECONNREFUSED
+    case ECONNREFUSED:
+      return DBUS_ERROR_NO_SERVER;
+#endif
+#ifdef ETIMEDOUT
+    case ETIMEDOUT:
+      return DBUS_ERROR_TIMEOUT;
+#endif
+#ifdef ENETUNREACH
+    case ENETUNREACH:
+      return DBUS_ERROR_NO_NETWORK;
+#endif
+#ifdef EADDRINUSE
+    case EADDRINUSE:
+      return DBUS_ERROR_ADDRESS_IN_USE;
+#endif
+#ifdef EEXIST
+    case EEXIST:
+      return DBUS_ERROR_FILE_EXISTS;
+#endif
+#ifdef ENOENT
+    case ENOENT:
+      return DBUS_ERROR_FILE_NOT_FOUND;
+#endif
+    }
+
+  return DBUS_ERROR_FAILED;
+}
+
+/**
+ * Assign 0 to the global errno variable
+ */
+void
+_dbus_set_errno_to_zero (void)
+{
+  errno = 0;
+}
+
+/**
+ * See if errno is set
+ * @returns #TRUE if errno is not 0
+ */
+dbus_bool_t
+_dbus_get_is_errno_nonzero (void)
+{
+  return errno != 0;
+}
+
+/**
+ * See if errno is ENOMEM
+ * @returns #TRUE if errno == ENOMEM
+ */
+dbus_bool_t
+_dbus_get_is_errno_enomem (void)
+{
+  return errno == ENOMEM;
+}
+
+/**
+ * See if errno is EINTR
+ * @returns #TRUE if errno == EINTR
+ */
+dbus_bool_t
+_dbus_get_is_errno_eintr (void)
+{
+  return errno == EINTR;
+}
+
+/**
+ * Get error message from errno
+ * @returns _dbus_strerror(errno)
+ */
+const char*
+_dbus_strerror_from_errno (void)
+{
+  return _dbus_strerror (errno);
+}
+
+/** @} end of sysdeps */
+
+/* tests in dbus-sysdeps-util.c */
diff --git a/src/dbus/dbus-sysdeps.h b/src/dbus/dbus-sysdeps.h
new file mode 100644 (file)
index 0000000..b766f3f
--- /dev/null
@@ -0,0 +1,505 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sysdeps.h Wrappers around system/libc features (internal to D-Bus implementation)
+ * 
+ * Copyright (C) 2002, 2003  Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_SYSDEPS_H
+#define DBUS_SYSDEPS_H
+
+#include <config.h>
+
+#include <dbus/dbus-errors.h>
+
+/* this is perhaps bogus, but strcmp() etc. are faster if we use the
+ * stuff straight out of string.h, so have this here for now.
+ */
+#include <string.h>
+#include <stdarg.h>
+
+/* AIX sys/poll.h does #define events reqevents, and other
+ * wonderousness, so must include sys/poll before declaring
+ * DBusPollFD
+ */ 
+#ifdef HAVE_POLL
+#include <sys/poll.h>
+#endif
+
+DBUS_BEGIN_DECLS
+
+#ifdef DBUS_WIN
+#define _DBUS_PATH_SEPARATOR ";"
+#else
+#define _DBUS_PATH_SEPARATOR ":"
+#endif
+
+/* Forward declarations */
+
+/** An opaque string type */
+typedef struct DBusString DBusString;
+
+/** An opaque list type */
+typedef struct DBusList DBusList;
+
+/** Object that contains a list of credentials such as UNIX or Windows user ID */
+typedef struct DBusCredentials DBusCredentials;
+
+/**
+ * @addtogroup DBusSysdeps
+ *
+ * @{
+ */
+
+/* The idea of this file is to encapsulate everywhere that we're
+ * relying on external libc features, for ease of security
+ * auditing. The idea is from vsftpd. This also gives us a chance to
+ * make things more convenient to use, e.g.  by reading into a
+ * DBusString. Operating system headers aren't intended to be used
+ * outside of this file and a limited number of others (such as
+ * dbus-memory.c)
+ */
+
+#if     __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
+#define _DBUS_GNUC_PRINTF( format_idx, arg_idx )    \
+  __attribute__((__format__ (__printf__, format_idx, arg_idx)))
+#define _DBUS_GNUC_NORETURN                         \
+  __attribute__((__noreturn__))
+#else   /* !__GNUC__ */
+#define _DBUS_GNUC_PRINTF( format_idx, arg_idx )
+#define _DBUS_GNUC_NORETURN
+#endif  /* !__GNUC__ */
+
+/** @def _DBUS_GNUC_PRINTF
+ * used to tell gcc about printf format strings
+ */
+/** @def _DBUS_GNUC_NORETURN
+ * used to tell gcc about functions that never return, such as _dbus_abort()
+ */
+
+void _dbus_abort (void) _DBUS_GNUC_NORETURN;
+
+const char* _dbus_getenv (const char *varname);
+dbus_bool_t _dbus_setenv (const char *varname,
+                         const char *value);
+dbus_bool_t _dbus_clearenv (void);
+char **     _dbus_get_environment (void);
+
+/** A process ID */
+typedef unsigned long dbus_pid_t;
+/** A user ID */
+typedef unsigned long dbus_uid_t;
+/** A group ID */
+typedef unsigned long dbus_gid_t;
+
+/** an invalid PID used to represent an uninitialized dbus_pid_t field */
+#define DBUS_PID_UNSET ((dbus_pid_t) -1)
+/** an invalid UID used to represent an uninitialized dbus_uid_t field */
+#define DBUS_UID_UNSET ((dbus_uid_t) -1)
+/** an invalid GID used to represent an uninitialized dbus_gid_t field */
+#define DBUS_GID_UNSET ((dbus_gid_t) -1)
+
+/** an appropriate printf format for dbus_pid_t */
+#define DBUS_PID_FORMAT "%lu"
+/** an appropriate printf format for dbus_uid_t */
+#define DBUS_UID_FORMAT "%lu"
+/** an appropriate printf format for dbus_gid_t */
+#define DBUS_GID_FORMAT "%lu"
+
+
+/**
+ * Socket interface
+ *
+ *  @todo Use for the file descriptors a struct
+ *           - struct DBusSocket{ int d; }; -
+ *        instead of int to get type-safety which 
+ *        will be checked by the compiler.
+ * 
+ */
+
+dbus_bool_t _dbus_open_tcp_socket  (int              *fd,
+                                    DBusError        *error);
+dbus_bool_t _dbus_close_socket     (int               fd,
+                                    DBusError        *error);
+int         _dbus_read_socket      (int               fd,
+                                    DBusString       *buffer,
+                                    int               count);
+int         _dbus_write_socket     (int               fd,
+                                    const DBusString *buffer,
+                                    int               start,
+                                    int               len);
+int         _dbus_write_socket_two (int               fd,
+                                    const DBusString *buffer1,
+                                    int               start1,
+                                    int               len1,
+                                    const DBusString *buffer2,
+                                    int               start2,
+                                    int               len2);
+int _dbus_connect_tcp_socket  (const char     *host,
+                               const char     *port,
+                               const char     *family,
+                               DBusError      *error);
+int _dbus_listen_tcp_socket   (const char     *host,
+                               const char     *port,
+                               const char     *family,
+                               DBusString     *retport,
+                               int           **fds_p,
+                               DBusError      *error);
+int _dbus_accept              (int             listen_fd);
+
+
+dbus_bool_t _dbus_read_credentials_socket (int               client_fd,
+                                           DBusCredentials  *credentials,
+                                           DBusError        *error);
+dbus_bool_t _dbus_send_credentials_socket (int              server_fd,
+                                           DBusError       *error);
+
+dbus_bool_t _dbus_credentials_add_from_user            (DBusCredentials  *credentials,
+                                                        const DBusString *username);
+dbus_bool_t _dbus_credentials_add_from_current_process (DBusCredentials  *credentials);
+dbus_bool_t _dbus_append_user_from_current_process     (DBusString        *str);
+
+dbus_bool_t _dbus_parse_unix_user_from_config   (const DBusString  *username,
+                                                 dbus_uid_t        *uid_p);
+dbus_bool_t _dbus_parse_unix_group_from_config  (const DBusString  *groupname,
+                                                 dbus_gid_t        *gid_p);
+dbus_bool_t _dbus_unix_groups_from_uid          (dbus_uid_t         uid,
+                                                 dbus_gid_t       **group_ids,
+                                                 int               *n_group_ids);
+dbus_bool_t _dbus_unix_user_is_at_console       (dbus_uid_t         uid,
+                                                 DBusError         *error);
+dbus_bool_t _dbus_unix_user_is_process_owner    (dbus_uid_t         uid);
+dbus_bool_t _dbus_windows_user_is_process_owner (const char        *windows_sid);
+
+dbus_bool_t _dbus_append_keyring_directory_for_credentials (DBusString      *directory,
+                                                            DBusCredentials *credentials);
+
+/** Opaque type representing an atomically-modifiable integer
+ * that can be used from multiple threads.
+ */
+typedef struct DBusAtomic DBusAtomic;
+
+/**
+ * An atomic integer safe to increment or decrement from multiple threads.
+ */
+struct DBusAtomic
+{
+#ifdef DBUS_WIN
+  volatile long value; /**< Value of the atomic integer. */
+#else
+  volatile dbus_int32_t value; /**< Value of the atomic integer. */
+#endif
+};
+
+/* The value we get from autofoo is in the form of a cpp expression;
+ * convert that to a conventional defined/undef switch. (We can't get
+ * the conventional defined/undef because of multiarch builds only running
+ * ./configure once, on Darwin.) */
+#if DBUS_HAVE_ATOMIC_INT_COND
+#   define DBUS_HAVE_ATOMIC_INT 1
+#else
+#   undef DBUS_HAVE_ATOMIC_INT
+#endif
+
+dbus_int32_t _dbus_atomic_inc (DBusAtomic *atomic);
+dbus_int32_t _dbus_atomic_dec (DBusAtomic *atomic);
+
+
+/* AIX uses different values for poll */
+
+#ifdef _AIX
+/** There is data to read */
+#define _DBUS_POLLIN      0x0001
+/** There is urgent data to read */
+#define _DBUS_POLLPRI     0x0004
+/** Writing now will not block */
+#define _DBUS_POLLOUT     0x0002
+/** Error condition */
+#define _DBUS_POLLERR     0x4000
+/** Hung up */
+#define _DBUS_POLLHUP     0x2000
+/** Invalid request: fd not open */
+#define _DBUS_POLLNVAL    0x8000
+#else
+/** There is data to read */
+#define _DBUS_POLLIN      0x0001
+/** There is urgent data to read */
+#define _DBUS_POLLPRI     0x0002
+/** Writing now will not block */
+#define _DBUS_POLLOUT     0x0004
+/** Error condition */
+#define _DBUS_POLLERR     0x0008
+/** Hung up */
+#define _DBUS_POLLHUP     0x0010
+/** Invalid request: fd not open */
+#define _DBUS_POLLNVAL    0x0020
+#endif
+
+/**
+ * A portable struct pollfd wrapper. 
+ */
+typedef struct
+{
+  int fd;            /**< File descriptor */
+  short events;      /**< Events to poll for */
+  short revents;     /**< Events that occurred */
+} DBusPollFD;
+
+int _dbus_poll (DBusPollFD *fds,
+                int         n_fds,
+                int         timeout_milliseconds);
+
+void _dbus_sleep_milliseconds (int milliseconds);
+
+void _dbus_get_current_time (long *tv_sec,
+                             long *tv_usec);
+
+/**
+ * File/directory interface
+ */
+dbus_bool_t _dbus_file_exists         (const char       *file);
+dbus_bool_t _dbus_file_get_contents   (DBusString       *str,
+                                       const DBusString *filename,
+                                       DBusError        *error);
+dbus_bool_t _dbus_string_save_to_file (const DBusString *str,
+                                       const DBusString *filename,
+                                       DBusError        *error);
+
+dbus_bool_t _dbus_make_file_world_readable   (const DBusString *filename,
+                                              DBusError *error);
+
+dbus_bool_t    _dbus_create_file_exclusively (const DBusString *filename,
+                                              DBusError        *error);
+dbus_bool_t    _dbus_delete_file             (const DBusString *filename,
+                                              DBusError        *error);
+dbus_bool_t    _dbus_create_directory        (const DBusString *filename,
+                                              DBusError        *error);
+dbus_bool_t    _dbus_delete_directory        (const DBusString *filename,
+                                             DBusError        *error);
+
+dbus_bool_t _dbus_concat_dir_and_file (DBusString       *dir,
+                                       const DBusString *next_component);
+dbus_bool_t _dbus_string_get_dirname  (const DBusString *filename,
+                                       DBusString       *dirname);
+dbus_bool_t _dbus_path_is_absolute    (const DBusString *filename);
+
+dbus_bool_t _dbus_get_standard_session_servicedirs (DBusList **dirs);
+dbus_bool_t _dbus_get_standard_system_servicedirs (DBusList **dirs);
+
+dbus_bool_t _dbus_append_system_config_file  (DBusString *str);
+dbus_bool_t _dbus_append_session_config_file (DBusString *str);
+
+typedef struct {
+  int fd_or_handle;
+} DBusPipe;
+
+void        _dbus_pipe_init                (DBusPipe         *pipe,
+                                            int               fd);
+void        _dbus_pipe_init_stdout         (DBusPipe         *pipe);
+int         _dbus_pipe_write               (DBusPipe         *pipe,
+                                            const DBusString *buffer,
+                                            int               start,
+                                            int               len,
+                                            DBusError        *error);
+int         _dbus_pipe_close               (DBusPipe         *pipe,
+                                            DBusError        *error);
+dbus_bool_t _dbus_pipe_is_valid            (DBusPipe         *pipe);
+void        _dbus_pipe_invalidate          (DBusPipe         *pipe);
+dbus_bool_t _dbus_pipe_is_stdout_or_stderr (DBusPipe         *pipe);
+
+
+/** Opaque type for reading a directory listing */
+typedef struct DBusDirIter DBusDirIter;
+
+DBusDirIter* _dbus_directory_open          (const DBusString *filename,
+                                            DBusError        *error);
+dbus_bool_t  _dbus_directory_get_next_file (DBusDirIter      *iter,
+                                            DBusString       *filename,
+                                            DBusError        *error);
+void         _dbus_directory_close         (DBusDirIter      *iter);
+
+dbus_bool_t  _dbus_check_dir_is_private_to_user    (DBusString *dir,
+                                                    DBusError *error);
+
+void _dbus_fd_set_close_on_exec (int fd);
+
+const char* _dbus_get_tmpdir      (void);
+
+/**
+ * Random numbers 
+ */
+void        _dbus_generate_pseudorandom_bytes_buffer (char *buffer,
+                                                      int   n_bytes);
+void        _dbus_generate_random_bytes_buffer (char       *buffer,
+                                                int         n_bytes);
+dbus_bool_t _dbus_generate_random_bytes        (DBusString *str,
+                                                int         n_bytes);
+dbus_bool_t _dbus_generate_random_ascii        (DBusString *str,
+                                                int         n_bytes);
+
+const char* _dbus_error_from_errno (int error_number);
+
+void        _dbus_set_errno_to_zero                  (void);
+dbus_bool_t _dbus_get_is_errno_nonzero               (void);
+dbus_bool_t _dbus_get_is_errno_eagain_or_ewouldblock (void);
+dbus_bool_t _dbus_get_is_errno_enomem                (void);
+dbus_bool_t _dbus_get_is_errno_eintr                 (void);
+const char* _dbus_strerror_from_errno                (void);
+
+void _dbus_disable_sigpipe (void);
+
+
+void _dbus_exit (int code) _DBUS_GNUC_NORETURN;
+
+int _dbus_printf_string_upper_bound (const char *format,
+                                     va_list args);
+
+
+/**
+ * Portable struct with stat() results
+ */
+typedef struct
+{
+  unsigned long mode;  /**< File mode */
+  unsigned long nlink; /**< Number of hard links */
+  dbus_uid_t    uid;   /**< User owning file */
+  dbus_gid_t    gid;   /**< Group owning file */
+  unsigned long size;  /**< Size of file */
+  unsigned long atime; /**< Access time */
+  unsigned long mtime; /**< Modify time */
+  unsigned long ctime; /**< Creation time */
+} DBusStat;
+
+dbus_bool_t _dbus_stat             (const DBusString *filename,
+                                    DBusStat         *statbuf,
+                                    DBusError        *error);
+dbus_bool_t _dbus_full_duplex_pipe (int              *fd1,
+                                    int              *fd2,
+                                    dbus_bool_t       blocking,
+                                    DBusError        *error);
+
+void        _dbus_print_backtrace  (void);
+
+dbus_bool_t _dbus_become_daemon   (const DBusString *pidfile,
+                                   DBusPipe         *print_pid_pipe,
+                                   DBusError        *error,
+                                   dbus_bool_t       keep_umask);
+
+dbus_bool_t _dbus_verify_daemon_user    (const char *user);
+dbus_bool_t _dbus_change_to_daemon_user (const char *user,
+                                         DBusError  *error);
+
+dbus_bool_t _dbus_write_pid_to_file_and_pipe (const DBusString *pidfile,
+                                              DBusPipe         *print_pid_pipe,
+                                              dbus_pid_t        pid_to_write,
+                                              DBusError        *error);
+
+dbus_bool_t _dbus_command_for_pid (unsigned long  pid,
+                                   DBusString    *str,
+                                   int            max_len,
+                                   DBusError     *error);
+
+/** A UNIX signal handler */
+typedef void (* DBusSignalHandler) (int sig);
+
+void _dbus_set_signal_handler (int               sig,
+                               DBusSignalHandler handler);
+
+dbus_bool_t _dbus_user_at_console (const char *username,
+                                   DBusError  *error);
+
+void _dbus_init_system_log (void);
+void _dbus_log_info (const char *msg, va_list args);
+void _dbus_log_security (const char *msg, va_list args);
+
+/* Define DBUS_VA_COPY() to do the right thing for copying va_list variables. 
+ * config.h may have already defined DBUS_VA_COPY as va_copy or __va_copy. 
+ */
+#if !defined (DBUS_VA_COPY)
+#  if defined (__GNUC__) && defined (__PPC__) && (defined (_CALL_SYSV) || defined (_WIN32))
+#    define DBUS_VA_COPY(ap1, ap2)   (*(ap1) = *(ap2))
+#  elif defined (DBUS_VA_COPY_AS_ARRAY)
+#    define DBUS_VA_COPY(ap1, ap2)   memcpy ((ap1), (ap2), sizeof (va_list))
+#  else /* va_list is a pointer */
+#    define DBUS_VA_COPY(ap1, ap2)   ((ap1) = (ap2))
+#  endif /* va_list is a pointer */
+#endif /* !DBUS_VA_COPY */
+
+
+/**
+ * Casts a primitive C type to a byte array and then indexes
+ * a particular byte of the array.
+ */
+#define _DBUS_BYTE_OF_PRIMITIVE(p, i) \
+    (((const char*)&(p))[(i)])
+/** On x86 there is an 80-bit FPU, and if you do "a == b" it may have a
+ * or b in an 80-bit register, thus failing to compare the two 64-bit
+ * doubles for bitwise equality. So this macro compares the two doubles
+ * bitwise.
+ */
+#define _DBUS_DOUBLES_BITWISE_EQUAL(a, b)                                       \
+     (_DBUS_BYTE_OF_PRIMITIVE (a, 0) == _DBUS_BYTE_OF_PRIMITIVE (b, 0) &&       \
+      _DBUS_BYTE_OF_PRIMITIVE (a, 1) == _DBUS_BYTE_OF_PRIMITIVE (b, 1) &&       \
+      _DBUS_BYTE_OF_PRIMITIVE (a, 2) == _DBUS_BYTE_OF_PRIMITIVE (b, 2) &&       \
+      _DBUS_BYTE_OF_PRIMITIVE (a, 3) == _DBUS_BYTE_OF_PRIMITIVE (b, 3) &&       \
+      _DBUS_BYTE_OF_PRIMITIVE (a, 4) == _DBUS_BYTE_OF_PRIMITIVE (b, 4) &&       \
+      _DBUS_BYTE_OF_PRIMITIVE (a, 5) == _DBUS_BYTE_OF_PRIMITIVE (b, 5) &&       \
+      _DBUS_BYTE_OF_PRIMITIVE (a, 6) == _DBUS_BYTE_OF_PRIMITIVE (b, 6) &&       \
+      _DBUS_BYTE_OF_PRIMITIVE (a, 7) == _DBUS_BYTE_OF_PRIMITIVE (b, 7))
+
+dbus_bool_t _dbus_get_autolaunch_address (DBusString *address, 
+                                         DBusError *error);
+
+/** Type representing a universally unique ID
+ * @todo rename to UUID instead of GUID
+ */
+typedef union DBusGUID DBusGUID;
+
+dbus_bool_t _dbus_read_local_machine_uuid   (DBusGUID         *machine_id,
+                                             dbus_bool_t       create_if_not_found,
+                                             DBusError        *error);
+
+/**
+ * Initialize threads as in dbus_threads_init_default(), appropriately
+ * for the platform.
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t _dbus_threads_init_platform_specific (void);
+
+dbus_bool_t _dbus_split_paths_and_append (DBusString *dirs, 
+                                          const char *suffix, 
+                                          DBusList **dir_list);
+
+unsigned long _dbus_pid_for_log (void);
+
+/* FIXME move back to dbus-sysdeps-unix.h probably -
+ * the PID file handling just needs a little more abstraction
+ * in the bus daemon first.
+ */
+dbus_pid_t    _dbus_getpid (void);
+
+void _dbus_flush_caches (void);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_SYSDEPS_H */
diff --git a/src/dbus/dbus-test-main.c b/src/dbus/dbus-test-main.c
new file mode 100644 (file)
index 0000000..e7fcc67
--- /dev/null
@@ -0,0 +1,54 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-test.c  Program to run all tests
+ *
+ * Copyright (C) 2002  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-types.h"
+#include "dbus-test.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+
+int
+main (int    argc,
+      char **argv)
+{
+  const char *test_data_dir;
+  const char *specific_test;
+
+  setlocale(LC_ALL, "");
+
+  
+  if (argc > 1)
+    test_data_dir = argv[1];
+  else
+    test_data_dir = NULL;
+
+  if (argc > 2)
+    specific_test = argv[2];
+  else
+    specific_test = NULL;
+  
+  dbus_internal_do_not_use_run_tests (test_data_dir, specific_test);
+  
+  return 0;
+}
diff --git a/src/dbus/dbus-test.c b/src/dbus/dbus-test.c
new file mode 100644 (file)
index 0000000..180235f
--- /dev/null
@@ -0,0 +1,190 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-test.c  Program to run all tests
+ *
+ * Copyright (C) 2002, 2003, 2004, 2005  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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 <config.h>
+#include "dbus-test.h"
+#include "dbus-sysdeps.h"
+#include "dbus-internals.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef DBUS_BUILD_TESTS
+static void
+die (const char *failure)
+{
+  fprintf (stderr, "Unit test failed: %s\n", failure);
+  exit (1);
+}
+
+static void
+check_memleaks (void)
+{
+  dbus_shutdown ();
+
+  printf ("%s: checking for memleaks\n", "dbus-test");
+  if (_dbus_get_malloc_blocks_outstanding () != 0)
+    {
+      _dbus_warn ("%d dbus_malloc blocks were not freed\n",
+                  _dbus_get_malloc_blocks_outstanding ());
+      die ("memleaks");
+    }
+}
+
+typedef dbus_bool_t (*TestFunc)(void);
+typedef dbus_bool_t (*TestDataFunc)(const char *data);
+
+static void
+run_test (const char             *test_name,
+         const char             *specific_test,
+         TestFunc                test)
+{
+  if (!specific_test || strcmp (specific_test, test_name) == 0)
+    {
+      printf ("%s: running %s tests\n", "dbus-test", test_name);
+      if (!test ())
+       die (test_name);
+    }
+
+  check_memleaks ();
+}
+
+static void
+run_data_test (const char             *test_name,
+              const char             *specific_test,
+              TestDataFunc            test,
+              const char             *test_data_dir)
+{
+  if (!specific_test || strcmp (specific_test, test_name) == 0)
+    {
+      printf ("%s: running %s tests\n", "dbus-test", test_name);
+      if (!test (test_data_dir))
+       die (test_name);
+    }
+
+  check_memleaks ();
+}
+
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * An exported symbol to be run in order to execute
+ * unit tests. Should not be used by
+ * any app other than our test app, this symbol
+ * won't exist in some builds of the library.
+ * (with --enable-tests=no)
+ *
+ * @param test_data_dir the directory with test data (test/data normally)
+ */
+void
+dbus_internal_do_not_use_run_tests (const char *test_data_dir, const char *specific_test)
+{
+#ifdef DBUS_BUILD_TESTS
+  if (!_dbus_threads_init_debug ())
+    die ("debug threads init");
+  
+  if (test_data_dir == NULL)
+    test_data_dir = _dbus_getenv ("DBUS_TEST_DATA");
+
+  if (test_data_dir != NULL)
+    printf ("Test data in %s\n", test_data_dir);
+  else
+    printf ("No test data!\n");
+
+  run_test ("string", specific_test, _dbus_string_test);
+  
+  run_test ("sysdeps", specific_test, _dbus_sysdeps_test);
+  
+  run_test ("data-slot", specific_test, _dbus_data_slot_test);
+
+  run_test ("misc", specific_test, _dbus_misc_test);
+  
+  run_test ("address", specific_test, _dbus_address_test);
+
+  run_test ("server", specific_test, _dbus_server_test);
+
+  run_test ("object-tree", specific_test, _dbus_object_tree_test);
+
+  run_test ("signature", specific_test, _dbus_signature_test);
+  
+  run_test ("marshalling", specific_test, _dbus_marshal_test);
+
+#if 0
+  printf ("%s: running recursive marshalling tests\n", "dbus-test");
+  if (!_dbus_marshal_recursive_test ())
+    die ("recursive marshal");
+
+  check_memleaks ();
+#else
+  _dbus_warn ("recursive marshal tests disabled\n");
+#endif
+
+  run_test ("byteswap", specific_test, _dbus_marshal_byteswap_test);
+  
+  run_test ("memory", specific_test, _dbus_memory_test);
+
+#if 1
+  run_test ("mem-pool", specific_test, _dbus_mem_pool_test);
+#endif
+  
+  run_test ("list", specific_test, _dbus_list_test);
+
+  run_test ("marshal-validate", specific_test, _dbus_marshal_validate_test);
+
+  run_test ("marshal-header", specific_test, _dbus_marshal_header_test);
+  
+  run_data_test ("message", specific_test, _dbus_message_test, test_data_dir);
+  
+  run_test ("hash", specific_test, _dbus_hash_test);
+
+#if !defined(DBUS_WINCE)
+  run_data_test ("spawn", specific_test, _dbus_spawn_test, test_data_dir);
+#endif
+  
+  run_data_test ("credentials", specific_test, _dbus_credentials_test, test_data_dir);
+
+#ifdef DBUS_UNIX
+  run_data_test ("userdb", specific_test, _dbus_userdb_test, test_data_dir);
+#endif
+  
+  run_test ("keyring", specific_test, _dbus_keyring_test);
+  
+#if 0
+  printf ("%s: running md5 tests\n", "dbus-test");
+  if (!_dbus_md5_test ())
+    die ("md5");
+
+  check_memleaks ();
+#endif
+  
+  run_data_test ("sha", specific_test, _dbus_sha_test, test_data_dir);
+  
+  run_data_test ("auth", specific_test, _dbus_auth_test, test_data_dir);
+
+  run_data_test ("pending-call", specific_test, _dbus_pending_call_test, test_data_dir);
+  
+  printf ("%s: completed successfully\n", "dbus-test");
+#else
+  printf ("Not compiled with unit tests, not running any\n");
+#endif
+}
+
diff --git a/src/dbus/dbus-test.h b/src/dbus/dbus-test.h
new file mode 100644 (file)
index 0000000..a7aaea6
--- /dev/null
@@ -0,0 +1,83 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-test.h  Declarations of test functions.
+ *
+ * Copyright (C) 2002  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_TEST_H
+#define DBUS_TEST_H
+
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-marshal-validate.h>
+
+dbus_bool_t _dbus_hash_test              (void);
+dbus_bool_t _dbus_dict_test              (void);
+dbus_bool_t _dbus_list_test              (void);
+dbus_bool_t _dbus_marshal_test           (void);
+dbus_bool_t _dbus_marshal_recursive_test (void);
+dbus_bool_t _dbus_marshal_byteswap_test  (void);
+dbus_bool_t _dbus_marshal_header_test    (void);
+dbus_bool_t _dbus_marshal_validate_test  (void);
+dbus_bool_t _dbus_misc_test              (void);
+dbus_bool_t _dbus_signature_test         (void);
+dbus_bool_t _dbus_mem_pool_test          (void);
+dbus_bool_t _dbus_string_test            (void);
+dbus_bool_t _dbus_address_test           (void);
+dbus_bool_t _dbus_server_test            (void);
+dbus_bool_t _dbus_message_test           (const char *test_data_dir);
+dbus_bool_t _dbus_auth_test              (const char *test_data_dir);
+dbus_bool_t _dbus_md5_test               (void);
+dbus_bool_t _dbus_sha_test               (const char *test_data_dir);
+dbus_bool_t _dbus_keyring_test           (void);
+dbus_bool_t _dbus_data_slot_test         (void);
+dbus_bool_t _dbus_sysdeps_test           (void);
+dbus_bool_t _dbus_spawn_test             (const char *test_data_dir);
+dbus_bool_t _dbus_userdb_test            (const char *test_data_dir);
+dbus_bool_t _dbus_memory_test            (void);
+dbus_bool_t _dbus_object_tree_test       (void);
+dbus_bool_t _dbus_pending_call_test      (const char *test_data_dir);
+dbus_bool_t _dbus_credentials_test       (const char *test_data_dir);
+
+void        dbus_internal_do_not_use_run_tests         (const char          *test_data_dir,
+                                                       const char          *specific_test);
+dbus_bool_t dbus_internal_do_not_use_try_message_file  (const DBusString    *filename,
+                                                        DBusValidity         expected_validity);
+dbus_bool_t dbus_internal_do_not_use_try_message_data  (const DBusString    *data,
+                                                        DBusValidity         expected_validity);
+dbus_bool_t dbus_internal_do_not_use_load_message_file (const DBusString    *filename,
+                                                        DBusString          *data);
+
+
+/* returns FALSE on fatal failure */
+typedef dbus_bool_t (* DBusForeachMessageFileFunc) (const DBusString   *filename,
+                                                    DBusValidity        expected_validity,
+                                                    void               *data);
+
+dbus_bool_t dbus_internal_do_not_use_foreach_message_file (const char                 *test_data_dir,
+                                                           DBusForeachMessageFileFunc  func,
+                                                           void                       *user_data);
+dbus_bool_t dbus_internal_do_not_use_generate_bodies    (int           sequence,
+                                                         int           byte_order,
+                                                         DBusString   *signature,
+                                                         DBusString   *body);
+
+
+#endif /* DBUS_TEST_H */
diff --git a/src/dbus/dbus-threads-internal.h b/src/dbus/dbus-threads-internal.h
new file mode 100644 (file)
index 0000000..bcc4e6b
--- /dev/null
@@ -0,0 +1,53 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-threads-internal.h  D-Bus thread primitives
+ *
+ * Copyright (C) 2002, 2005 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_THREADS_INTERNAL_H
+#define DBUS_THREADS_INTERNAL_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-threads.h>
+
+DBUS_BEGIN_DECLS
+
+DBusMutex*   _dbus_mutex_new                 (void);
+void         _dbus_mutex_free                (DBusMutex         *mutex);
+void         _dbus_mutex_lock                (DBusMutex         *mutex);
+void         _dbus_mutex_unlock              (DBusMutex         *mutex);
+void         _dbus_mutex_new_at_location     (DBusMutex        **location_p);
+void         _dbus_mutex_free_at_location    (DBusMutex        **location_p);
+
+DBusCondVar* _dbus_condvar_new               (void);
+void         _dbus_condvar_free              (DBusCondVar       *cond);
+void         _dbus_condvar_wait              (DBusCondVar       *cond,
+                                              DBusMutex         *mutex);
+dbus_bool_t  _dbus_condvar_wait_timeout      (DBusCondVar       *cond,
+                                              DBusMutex         *mutex,
+                                              int                timeout_milliseconds);
+void         _dbus_condvar_wake_one          (DBusCondVar       *cond);
+void         _dbus_condvar_wake_all          (DBusCondVar       *cond);
+void         _dbus_condvar_new_at_location   (DBusCondVar      **location_p);
+void         _dbus_condvar_free_at_location  (DBusCondVar      **location_p);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_THREADS_INTERNAL_H */
diff --git a/src/dbus/dbus-threads.c b/src/dbus/dbus-threads.c
new file mode 100644 (file)
index 0000000..00c1a4b
--- /dev/null
@@ -0,0 +1,816 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-threads.h  D-Bus threads handling
+ *
+ * Copyright (C) 2002, 2003, 2006 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-threads.h"
+#include "dbus-internals.h"
+#include "dbus-threads-internal.h"
+#include "dbus-list.h"
+
+static DBusThreadFunctions thread_functions =
+{
+  0,
+  NULL, NULL, NULL, NULL, NULL,
+  NULL, NULL, NULL, NULL, NULL,
+  NULL, NULL, NULL, NULL,
+  
+  NULL, NULL, NULL, NULL
+};
+
+static int thread_init_generation = 0;
+static DBusList *uninitialized_mutex_list = NULL;
+static DBusList *uninitialized_condvar_list = NULL;
+
+/** This is used for the no-op default mutex pointer, just to be distinct from #NULL */
+#define _DBUS_DUMMY_MUTEX ((DBusMutex*)0xABCDEF)
+
+/** This is used for the no-op default mutex pointer, just to be distinct from #NULL */
+#define _DBUS_DUMMY_CONDVAR ((DBusCondVar*)0xABCDEF2)
+
+/**
+ * @defgroup DBusThreadsInternals Thread functions
+ * @ingroup  DBusInternals
+ * @brief _dbus_mutex_lock(), etc.
+ *
+ * Functions and macros related to threads and thread locks.
+ *
+ * @{
+ */
+
+/**
+ * Creates a new mutex using the function supplied to dbus_threads_init(),
+ * or creates a no-op mutex if threads are not initialized.
+ * May return #NULL even if threads are initialized, indicating
+ * out-of-memory.
+ *
+ * @returns new mutex or #NULL
+ */
+DBusMutex*
+_dbus_mutex_new (void)
+{
+  if (thread_functions.recursive_mutex_new)
+    return (* thread_functions.recursive_mutex_new) ();
+  else if (thread_functions.mutex_new)
+    return (* thread_functions.mutex_new) ();
+  else
+    return _DBUS_DUMMY_MUTEX;
+}
+
+/**
+ * This does the same thing as _dbus_mutex_new.  It however
+ * gives another level of indirection by allocating a pointer
+ * to point to the mutex location.  This allows the threading
+ * module to swap out dummy mutexes for real a real mutex so libraries
+ * can initialize threads even after the D-Bus API has been used.
+ *
+ * @param location_p the location of the new mutex, can return #NULL on OOM
+ */
+void
+_dbus_mutex_new_at_location (DBusMutex **location_p)
+{
+  _dbus_assert (location_p != NULL);
+
+  *location_p = _dbus_mutex_new();
+
+  if (thread_init_generation != _dbus_current_generation && *location_p)
+    {
+      if (!_dbus_list_append (&uninitialized_mutex_list, location_p))
+        {
+         _dbus_mutex_free (*location_p);
+         *location_p = NULL;
+       }
+    }
+}
+
+/**
+ * Frees a mutex created with dbus_mutex_new(); does
+ * nothing if passed a #NULL pointer.
+ */
+void
+_dbus_mutex_free (DBusMutex *mutex)
+{
+  if (mutex)
+    {
+      if (mutex && thread_functions.recursive_mutex_free)
+        (* thread_functions.recursive_mutex_free) (mutex);
+      else if (mutex && thread_functions.mutex_free)
+        (* thread_functions.mutex_free) (mutex);
+    }
+}
+
+/**
+ * Frees a mutex and removes it from the 
+ * uninitialized_mutex_list;
+ * does nothing if passed a #NULL pointer.
+ */
+void
+_dbus_mutex_free_at_location (DBusMutex **location_p)
+{
+  if (location_p)
+    {
+      if (thread_init_generation != _dbus_current_generation)
+        _dbus_list_remove (&uninitialized_mutex_list, location_p);
+
+      _dbus_mutex_free (*location_p);
+    }
+}
+
+/**
+ * Locks a mutex. Does nothing if passed a #NULL pointer.
+ * Locks may be recursive if threading implementation initialized
+ * recursive locks.
+ */
+void
+_dbus_mutex_lock (DBusMutex *mutex)
+{
+  if (mutex) 
+    {
+      if (thread_functions.recursive_mutex_lock)
+        (* thread_functions.recursive_mutex_lock) (mutex);
+      else if (thread_functions.mutex_lock)
+        (* thread_functions.mutex_lock) (mutex);
+    }
+}
+
+/**
+ * Unlocks a mutex. Does nothing if passed a #NULL pointer.
+ *
+ * @returns #TRUE on success
+ */
+void
+_dbus_mutex_unlock (DBusMutex *mutex)
+{
+  if (mutex)
+    {
+      if (thread_functions.recursive_mutex_unlock)
+        (* thread_functions.recursive_mutex_unlock) (mutex);
+      else if (thread_functions.mutex_unlock)
+        (* thread_functions.mutex_unlock) (mutex);
+    }
+}
+
+/**
+ * Creates a new condition variable using the function supplied
+ * to dbus_threads_init(), or creates a no-op condition variable
+ * if threads are not initialized. May return #NULL even if
+ * threads are initialized, indicating out-of-memory.
+ *
+ * @returns new mutex or #NULL
+ */
+DBusCondVar *
+_dbus_condvar_new (void)
+{
+  if (thread_functions.condvar_new)
+    return (* thread_functions.condvar_new) ();
+  else
+    return _DBUS_DUMMY_CONDVAR;
+}
+
+
+/**
+ * This does the same thing as _dbus_condvar_new.  It however
+ * gives another level of indirection by allocating a pointer
+ * to point to the condvar location.  This allows the threading
+ * module to swap out dummy condvars for real a real condvar so libraries
+ * can initialize threads even after the D-Bus API has been used.
+ *
+ * @returns the location of a new condvar or #NULL on OOM
+ */
+
+void 
+_dbus_condvar_new_at_location (DBusCondVar **location_p)
+{
+  *location_p = _dbus_condvar_new();
+
+  if (thread_init_generation != _dbus_current_generation && *location_p)
+    {
+      if (!_dbus_list_append (&uninitialized_condvar_list, location_p))
+        {
+          _dbus_condvar_free (*location_p);
+          *location_p = NULL;
+        }
+    }
+}
+
+
+/**
+ * Frees a conditional variable created with dbus_condvar_new(); does
+ * nothing if passed a #NULL pointer.
+ */
+void
+_dbus_condvar_free (DBusCondVar *cond)
+{
+  if (cond && thread_functions.condvar_free)
+    (* thread_functions.condvar_free) (cond);
+}
+
+/**
+ * Frees a conditional variable and removes it from the 
+ * uninitialized_condvar_list; 
+ * does nothing if passed a #NULL pointer.
+ */
+void
+_dbus_condvar_free_at_location (DBusCondVar **location_p)
+{
+  if (location_p)
+    {
+      if (thread_init_generation != _dbus_current_generation)
+        _dbus_list_remove (&uninitialized_condvar_list, location_p);
+
+      _dbus_condvar_free (*location_p);
+    }
+}
+
+/**
+ * Atomically unlocks the mutex and waits for the conditions
+ * variable to be signalled. Locks the mutex again before
+ * returning.
+ * Does nothing if passed a #NULL pointer.
+ */
+void
+_dbus_condvar_wait (DBusCondVar *cond,
+                    DBusMutex   *mutex)
+{
+  if (cond && mutex && thread_functions.condvar_wait)
+    (* thread_functions.condvar_wait) (cond, mutex);
+}
+
+/**
+ * Atomically unlocks the mutex and waits for the conditions variable
+ * to be signalled, or for a timeout. Locks the mutex again before
+ * returning.  Does nothing if passed a #NULL pointer.  Return value
+ * is #FALSE if we timed out, #TRUE otherwise.
+ *
+ * @param cond the condition variable
+ * @param mutex the mutex
+ * @param timeout_milliseconds the maximum time to wait
+ * @returns #FALSE if the timeout occurred, #TRUE if not
+ */
+dbus_bool_t
+_dbus_condvar_wait_timeout (DBusCondVar               *cond,
+                            DBusMutex                 *mutex,
+                            int                        timeout_milliseconds)
+{
+  if (cond && mutex && thread_functions.condvar_wait)
+    return (* thread_functions.condvar_wait_timeout) (cond, mutex, timeout_milliseconds);
+  else
+    return TRUE;
+}
+
+/**
+ * If there are threads waiting on the condition variable, wake
+ * up exactly one. 
+ * Does nothing if passed a #NULL pointer.
+ */
+void
+_dbus_condvar_wake_one (DBusCondVar *cond)
+{
+  if (cond && thread_functions.condvar_wake_one)
+    (* thread_functions.condvar_wake_one) (cond);
+}
+
+/**
+ * If there are threads waiting on the condition variable, wake
+ * up all of them. 
+ * Does nothing if passed a #NULL pointer.
+ */
+void
+_dbus_condvar_wake_all (DBusCondVar *cond)
+{
+  if (cond && thread_functions.condvar_wake_all)
+    (* thread_functions.condvar_wake_all) (cond);
+}
+
+static void
+shutdown_global_locks (void *data)
+{
+  DBusMutex ***locks = data;
+  int i;
+
+  i = 0;
+  while (i < _DBUS_N_GLOBAL_LOCKS)
+    {
+      _dbus_mutex_free (*(locks[i]));
+      *(locks[i]) = NULL;
+      ++i;
+    }
+  
+  dbus_free (locks);
+}
+
+static void
+shutdown_uninitialized_locks (void *data)
+{
+  _dbus_list_clear (&uninitialized_mutex_list);
+  _dbus_list_clear (&uninitialized_condvar_list);
+}
+
+static dbus_bool_t
+init_uninitialized_locks (void)
+{
+  DBusList *link;
+
+  _dbus_assert (thread_init_generation != _dbus_current_generation);
+
+  link = uninitialized_mutex_list;
+  while (link != NULL)
+    {
+      DBusMutex **mp;
+
+      mp = (DBusMutex **)link->data;
+      _dbus_assert (*mp == _DBUS_DUMMY_MUTEX);
+
+      *mp = _dbus_mutex_new ();
+      if (*mp == NULL)
+        goto fail_mutex;
+
+      link = _dbus_list_get_next_link (&uninitialized_mutex_list, link);
+    }
+
+  link = uninitialized_condvar_list;
+  while (link != NULL)
+    {
+      DBusCondVar **cp;
+
+      cp = (DBusCondVar **)link->data;
+      _dbus_assert (*cp == _DBUS_DUMMY_CONDVAR);
+
+      *cp = _dbus_condvar_new ();
+      if (*cp == NULL)
+        goto fail_condvar;
+
+      link = _dbus_list_get_next_link (&uninitialized_condvar_list, link);
+    }
+
+  _dbus_list_clear (&uninitialized_mutex_list);
+  _dbus_list_clear (&uninitialized_condvar_list);
+
+  if (!_dbus_register_shutdown_func (shutdown_uninitialized_locks,
+                                     NULL))
+    goto fail_condvar;
+
+  return TRUE;
+
+ fail_condvar:
+  link = uninitialized_condvar_list;
+  while (link != NULL)
+    {
+      DBusCondVar **cp;
+
+      cp = (DBusCondVar **)link->data;
+
+      if (*cp != _DBUS_DUMMY_CONDVAR)
+        _dbus_condvar_free (*cp);
+      else
+        break;
+
+      *cp = _DBUS_DUMMY_CONDVAR;
+
+      link = _dbus_list_get_next_link (&uninitialized_condvar_list, link);
+    }
+
+ fail_mutex:
+  link = uninitialized_mutex_list;
+  while (link != NULL)
+    {
+      DBusMutex **mp;
+
+      mp = (DBusMutex **)link->data;
+
+      if (*mp != _DBUS_DUMMY_MUTEX)
+        _dbus_mutex_free (*mp);
+      else
+        break;
+
+      *mp = _DBUS_DUMMY_MUTEX;
+
+      link = _dbus_list_get_next_link (&uninitialized_mutex_list, link);
+    }
+
+  return FALSE;
+}
+
+static dbus_bool_t
+init_locks (void)
+{
+  int i;
+  DBusMutex ***dynamic_global_locks;
+  
+  DBusMutex **global_locks[] = {
+#define LOCK_ADDR(name) (& _dbus_lock_##name)
+    LOCK_ADDR (win_fds),
+    LOCK_ADDR (sid_atom_cache),
+    LOCK_ADDR (list),
+    LOCK_ADDR (connection_slots),
+    LOCK_ADDR (pending_call_slots),
+    LOCK_ADDR (server_slots),
+    LOCK_ADDR (message_slots),
+    LOCK_ADDR (atomic),
+    LOCK_ADDR (bus),
+    LOCK_ADDR (bus_datas),
+    LOCK_ADDR (shutdown_funcs),
+    LOCK_ADDR (system_users),
+    LOCK_ADDR (message_cache),
+    LOCK_ADDR (shared_connections),
+    LOCK_ADDR (machine_uuid)
+#undef LOCK_ADDR
+  };
+
+  _dbus_assert (_DBUS_N_ELEMENTS (global_locks) ==
+                _DBUS_N_GLOBAL_LOCKS);
+
+  i = 0;
+  
+  dynamic_global_locks = dbus_new (DBusMutex**, _DBUS_N_GLOBAL_LOCKS);
+  if (dynamic_global_locks == NULL)
+    goto failed;
+  
+  while (i < _DBUS_N_ELEMENTS (global_locks))
+    {
+      *global_locks[i] = _dbus_mutex_new ();
+      
+      if (*global_locks[i] == NULL)
+        goto failed;
+
+      dynamic_global_locks[i] = global_locks[i];
+
+      ++i;
+    }
+  
+  if (!_dbus_register_shutdown_func (shutdown_global_locks,
+                                     dynamic_global_locks))
+    goto failed;
+
+  if (!init_uninitialized_locks ())
+    goto failed;
+  
+  return TRUE;
+
+ failed:
+  dbus_free (dynamic_global_locks);
+                                     
+  for (i = i - 1; i >= 0; i--)
+    {
+      _dbus_mutex_free (*global_locks[i]);
+      *global_locks[i] = NULL;
+    }
+  return FALSE;
+}
+
+/** @} */ /* end of internals */
+
+/**
+ * @defgroup DBusThreads Thread functions
+ * @ingroup  DBus
+ * @brief dbus_threads_init() and dbus_threads_init_default()
+ *
+ * Functions and macros related to threads and thread locks.
+ *
+ * If threads are initialized, the D-Bus library has locks on all
+ * global data structures.  In addition, each #DBusConnection has a
+ * lock, so only one thread at a time can touch the connection.  (See
+ * @ref DBusConnection for more on connection locking.)
+ *
+ * Most other objects, however, do not have locks - they can only be
+ * used from a single thread at a time, unless you lock them yourself.
+ * For example, a #DBusMessage can't be modified from two threads
+ * at once.
+ * 
+ * @{
+ */
+
+/**
+ * 
+ * Initializes threads. If this function is not called, the D-Bus
+ * library will not lock any data structures.  If it is called, D-Bus
+ * will do locking, at some cost in efficiency. Note that this
+ * function must be called BEFORE the second thread is started.
+ *
+ * Almost always, you should use dbus_threads_init_default() instead.
+ * The raw dbus_threads_init() is only useful if you require a
+ * particular thread implementation for some reason.
+ *
+ * A possible reason to use dbus_threads_init() rather than
+ * dbus_threads_init_default() is to insert debugging checks or print
+ * statements.
+ *
+ * dbus_threads_init() may be called more than once.  The first one
+ * wins and subsequent calls are ignored. (Unless you use
+ * dbus_shutdown() to reset libdbus, which will let you re-init
+ * threads.)
+ *
+ * Either recursive or nonrecursive mutex functions must be specified,
+ * but not both. New code should provide only the recursive functions
+ * - specifying the nonrecursive ones is deprecated.
+ *
+ * Because this function effectively sets global state, all code
+ * running in a given application must agree on the thread
+ * implementation. Most code won't care which thread implementation is
+ * used, so there's no problem. However, usually libraries should not
+ * call dbus_threads_init() or dbus_threads_init_default(), instead
+ * leaving this policy choice to applications.
+ *
+ * The exception is for application frameworks (GLib, Qt, etc.)  and
+ * D-Bus bindings based on application frameworks. These frameworks
+ * define a cross-platform thread abstraction and can assume
+ * applications using the framework are OK with using that thread
+ * abstraction.
+ *
+ * However, even these app frameworks may find it easier to simply call
+ * dbus_threads_init_default(), and there's no reason they shouldn't.
+ * 
+ * @param functions functions for using threads
+ * @returns #TRUE on success, #FALSE if no memory
+ */
+dbus_bool_t
+dbus_threads_init (const DBusThreadFunctions *functions)
+{
+  dbus_bool_t mutex_set;
+  dbus_bool_t recursive_mutex_set;
+
+  _dbus_assert (functions != NULL);
+
+  /* these base functions are required. Future additions to
+   * DBusThreadFunctions may be optional.
+   */
+  _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK);
+  _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK);
+  _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK);
+  _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK);
+  _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK);
+  _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK);
+  _dbus_assert (functions->condvar_new != NULL);
+  _dbus_assert (functions->condvar_free != NULL);
+  _dbus_assert (functions->condvar_wait != NULL);
+  _dbus_assert (functions->condvar_wait_timeout != NULL);
+  _dbus_assert (functions->condvar_wake_one != NULL);
+  _dbus_assert (functions->condvar_wake_all != NULL);
+
+  /* Either the mutex function set or recursive mutex set needs 
+   * to be available but not both
+   */
+  mutex_set = (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK) &&  
+              (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK) && 
+              (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK) &&
+              (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK) &&
+               functions->mutex_new &&
+               functions->mutex_free &&
+               functions->mutex_lock &&
+               functions->mutex_unlock;
+
+  recursive_mutex_set = 
+              (functions->mask & DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_NEW_MASK) && 
+              (functions->mask & DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_FREE_MASK) && 
+              (functions->mask & DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_LOCK_MASK) && 
+              (functions->mask & DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_UNLOCK_MASK) &&
+                functions->recursive_mutex_new &&
+                functions->recursive_mutex_free &&
+                functions->recursive_mutex_lock &&
+                functions->recursive_mutex_unlock;
+
+  if (!(mutex_set || recursive_mutex_set))
+    _dbus_assert_not_reached ("Either the nonrecusrive or recursive mutex " 
+                              "functions sets should be passed into "
+                              "dbus_threads_init. Neither sets were passed.");
+
+  if (mutex_set && recursive_mutex_set)
+    _dbus_assert_not_reached ("Either the nonrecusrive or recursive mutex " 
+                              "functions sets should be passed into "
+                              "dbus_threads_init. Both sets were passed. "
+                              "You most likely just want to set the recursive "
+                              "mutex functions to avoid deadlocks in D-Bus.");
+                          
+  /* Check that all bits in the mask actually are valid mask bits.
+   * ensures people won't write code that breaks when we add
+   * new bits.
+   */
+  _dbus_assert ((functions->mask & ~DBUS_THREAD_FUNCTIONS_ALL_MASK) == 0);
+
+  if (thread_init_generation != _dbus_current_generation)
+    thread_functions.mask = 0; /* allow re-init in new generation */
+  /* Silently allow multiple init
+   * First init wins and D-Bus will always use its threading system 
+   */ 
+  if (thread_functions.mask != 0)
+    return TRUE;
+  
+  thread_functions.mutex_new = functions->mutex_new;
+  thread_functions.mutex_free = functions->mutex_free;
+  thread_functions.mutex_lock = functions->mutex_lock;
+  thread_functions.mutex_unlock = functions->mutex_unlock;
+  
+  thread_functions.condvar_new = functions->condvar_new;
+  thread_functions.condvar_free = functions->condvar_free;
+  thread_functions.condvar_wait = functions->condvar_wait;
+  thread_functions.condvar_wait_timeout = functions->condvar_wait_timeout;
+  thread_functions.condvar_wake_one = functions->condvar_wake_one;
+  thread_functions.condvar_wake_all = functions->condvar_wake_all;
+  if (functions->mask & DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_NEW_MASK)
+    thread_functions.recursive_mutex_new = functions->recursive_mutex_new;
+  
+  if (functions->mask & DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_FREE_MASK)
+    thread_functions.recursive_mutex_free = functions->recursive_mutex_free;
+  
+  if (functions->mask & DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_LOCK_MASK)
+    thread_functions.recursive_mutex_lock = functions->recursive_mutex_lock;
+
+  if (functions->mask & DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_UNLOCK_MASK)
+    thread_functions.recursive_mutex_unlock = functions->recursive_mutex_unlock;
+
+  thread_functions.mask = functions->mask;
+
+  if (!init_locks ())
+    return FALSE;
+
+  thread_init_generation = _dbus_current_generation;
+  
+  return TRUE;
+}
+
+
+
+/* Default thread implemenation */
+
+/**
+ *
+ * Calls dbus_threads_init() with a default set of
+ * #DBusThreadFunctions appropriate for the platform.
+ *
+ * Most applications should use this rather than dbus_threads_init().
+ *
+ * It's safe to call dbus_threads_init_default() as many times as you
+ * want, but only the first time will have an effect.
+ *
+ * dbus_shutdown() reverses the effects of this function when it
+ * resets all global state in libdbus.
+ * 
+ * @returns #TRUE on success, #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_threads_init_default (void)
+{
+  return _dbus_threads_init_platform_specific ();
+}
+
+
+/** @} */
+
+#ifdef DBUS_BUILD_TESTS
+/** Fake mutex used for debugging */
+typedef struct DBusFakeMutex DBusFakeMutex;
+/** Fake mutex used for debugging */
+struct DBusFakeMutex
+{
+  dbus_bool_t locked; /**< Mutex is "locked" */
+};     
+
+static DBusMutex *  dbus_fake_mutex_new            (void);
+static void         dbus_fake_mutex_free           (DBusMutex   *mutex);
+static dbus_bool_t  dbus_fake_mutex_lock           (DBusMutex   *mutex);
+static dbus_bool_t  dbus_fake_mutex_unlock         (DBusMutex   *mutex);
+static DBusCondVar* dbus_fake_condvar_new          (void);
+static void         dbus_fake_condvar_free         (DBusCondVar *cond);
+static void         dbus_fake_condvar_wait         (DBusCondVar *cond,
+                                                    DBusMutex   *mutex);
+static dbus_bool_t  dbus_fake_condvar_wait_timeout (DBusCondVar *cond,
+                                                    DBusMutex   *mutex,
+                                                    int          timeout_msec);
+static void         dbus_fake_condvar_wake_one     (DBusCondVar *cond);
+static void         dbus_fake_condvar_wake_all     (DBusCondVar *cond);
+
+
+static const DBusThreadFunctions fake_functions =
+{
+  DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK |
+  DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK |
+  DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK |
+  DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK |
+  DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK |
+  DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK |
+  DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK |
+  DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK |
+  DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK|
+  DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK,
+  dbus_fake_mutex_new,
+  dbus_fake_mutex_free,
+  dbus_fake_mutex_lock,
+  dbus_fake_mutex_unlock,
+  dbus_fake_condvar_new,
+  dbus_fake_condvar_free,
+  dbus_fake_condvar_wait,
+  dbus_fake_condvar_wait_timeout,
+  dbus_fake_condvar_wake_one,
+  dbus_fake_condvar_wake_all
+};
+
+static DBusMutex *
+dbus_fake_mutex_new (void)
+{
+  DBusFakeMutex *mutex;
+
+  mutex = dbus_new0 (DBusFakeMutex, 1);
+
+  return (DBusMutex *)mutex;
+}
+
+static void
+dbus_fake_mutex_free (DBusMutex *mutex)
+{
+  DBusFakeMutex *fake = (DBusFakeMutex*) mutex;
+
+  _dbus_assert (!fake->locked);
+  
+  dbus_free (fake);
+}
+
+static dbus_bool_t
+dbus_fake_mutex_lock (DBusMutex *mutex)
+{
+  DBusFakeMutex *fake = (DBusFakeMutex*) mutex;
+
+  _dbus_assert (!fake->locked);
+
+  fake->locked = TRUE;
+  
+  return TRUE;
+}
+
+static dbus_bool_t
+dbus_fake_mutex_unlock (DBusMutex *mutex)
+{
+  DBusFakeMutex *fake = (DBusFakeMutex*) mutex;
+
+  _dbus_assert (fake->locked);
+
+  fake->locked = FALSE;
+  
+  return TRUE;
+}
+
+static DBusCondVar*
+dbus_fake_condvar_new (void)
+{
+  return (DBusCondVar*) _dbus_strdup ("FakeCondvar");
+}
+
+static void
+dbus_fake_condvar_free (DBusCondVar *cond)
+{
+  dbus_free (cond);
+}
+
+static void
+dbus_fake_condvar_wait (DBusCondVar *cond,
+                        DBusMutex   *mutex)
+{
+  
+}
+
+static dbus_bool_t
+dbus_fake_condvar_wait_timeout (DBusCondVar *cond,
+                                DBusMutex   *mutex,
+                                int         timeout_msec)
+{
+  return TRUE;
+}
+
+static void
+dbus_fake_condvar_wake_one (DBusCondVar *cond)
+{
+
+}
+
+static void
+dbus_fake_condvar_wake_all (DBusCondVar *cond)
+{
+
+}
+
+dbus_bool_t
+_dbus_threads_init_debug (void)
+{
+  return dbus_threads_init (&fake_functions);
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-threads.h b/src/dbus/dbus-threads.h
new file mode 100644 (file)
index 0000000..8d9687d
--- /dev/null
@@ -0,0 +1,196 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-threads.h  D-Bus threads handling
+ *
+ * Copyright (C) 2002  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_THREADS_H
+#define DBUS_THREADS_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-types.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusThreads
+ * @{
+ */
+
+/** An opaque mutex type provided by the #DBusThreadFunctions implementation installed by dbus_threads_init(). */
+typedef struct DBusMutex DBusMutex;
+/** An opaque condition variable type provided by the #DBusThreadFunctions implementation installed by dbus_threads_init(). */
+typedef struct DBusCondVar DBusCondVar;
+
+/** Deprecated, provide DBusRecursiveMutexNewFunction instead. */
+typedef DBusMutex*  (* DBusMutexNewFunction)    (void);
+/** Deprecated, provide DBusRecursiveMutexFreeFunction instead. */
+typedef void        (* DBusMutexFreeFunction)   (DBusMutex *mutex);
+/** Deprecated, provide DBusRecursiveMutexLockFunction instead. Return value is lock success, but gets ignored in practice. */
+typedef dbus_bool_t (* DBusMutexLockFunction)   (DBusMutex *mutex);
+/** Deprecated, provide DBusRecursiveMutexUnlockFunction instead. Return value is unlock success, but gets ignored in practice. */
+typedef dbus_bool_t (* DBusMutexUnlockFunction) (DBusMutex *mutex);
+
+/** Creates a new recursively-lockable mutex, or returns #NULL if not
+ * enough memory.  Can only fail due to lack of memory.  Found in
+ * #DBusThreadFunctions. Do not just use PTHREAD_MUTEX_RECURSIVE for
+ * this, because it does not save/restore the recursion count when
+ * waiting on a condition. libdbus requires the Java-style behavior
+ * where the mutex is fully unlocked to wait on a condition.
+ */
+typedef DBusMutex*  (* DBusRecursiveMutexNewFunction)    (void);
+/** Frees a recursively-lockable mutex.  Found in #DBusThreadFunctions.
+ */
+typedef void        (* DBusRecursiveMutexFreeFunction)   (DBusMutex *mutex);
+/** Locks a recursively-lockable mutex.  Found in #DBusThreadFunctions.
+ * Can only fail due to lack of memory.
+ */
+typedef void        (* DBusRecursiveMutexLockFunction)   (DBusMutex *mutex);
+/** Unlocks a recursively-lockable mutex.  Found in #DBusThreadFunctions.
+ * Can only fail due to lack of memory.
+ */
+typedef void        (* DBusRecursiveMutexUnlockFunction) (DBusMutex *mutex);
+
+/** Creates a new condition variable.  Found in #DBusThreadFunctions.
+ * Can only fail (returning #NULL) due to lack of memory.
+ */
+typedef DBusCondVar*  (* DBusCondVarNewFunction)         (void);
+/** Frees a condition variable.  Found in #DBusThreadFunctions.
+ */
+typedef void          (* DBusCondVarFreeFunction)        (DBusCondVar *cond);
+
+/** Waits on a condition variable.  Found in
+ * #DBusThreadFunctions. Must work with either a recursive or
+ * nonrecursive mutex, whichever the thread implementation
+ * provides. Note that PTHREAD_MUTEX_RECURSIVE does not work with
+ * condition variables (does not save/restore the recursion count) so
+ * don't try using simply pthread_cond_wait() and a
+ * PTHREAD_MUTEX_RECURSIVE to implement this, it won't work right.
+ *
+ * Has no error conditions. Must succeed if it returns.
+ */
+typedef void          (* DBusCondVarWaitFunction)        (DBusCondVar *cond,
+                                                         DBusMutex   *mutex);
+
+/** Waits on a condition variable with a timeout.  Found in
+ *  #DBusThreadFunctions. Returns #TRUE if the wait did not
+ *  time out, and #FALSE if it did.
+ *
+ * Has no error conditions. Must succeed if it returns. 
+ */
+typedef dbus_bool_t   (* DBusCondVarWaitTimeoutFunction) (DBusCondVar *cond,
+                                                         DBusMutex   *mutex,
+                                                         int          timeout_milliseconds);
+/** Wakes one waiting thread on a condition variable.  Found in #DBusThreadFunctions.
+ *
+ * Has no error conditions. Must succeed if it returns.
+ */
+typedef void          (* DBusCondVarWakeOneFunction) (DBusCondVar *cond);
+
+/** Wakes all waiting threads on a condition variable.  Found in #DBusThreadFunctions.
+ *
+ * Has no error conditions. Must succeed if it returns.
+ */
+typedef void          (* DBusCondVarWakeAllFunction) (DBusCondVar *cond);
+
+/**
+ * Flags indicating which functions are present in #DBusThreadFunctions. Used to allow
+ * the library to detect older callers of dbus_threads_init() if new possible functions
+ * are added to #DBusThreadFunctions.
+ */
+typedef enum 
+{
+  DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK      = 1 << 0,
+  DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK     = 1 << 1,
+  DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK     = 1 << 2,
+  DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK   = 1 << 3,
+  DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK    = 1 << 4,
+  DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK   = 1 << 5,
+  DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK   = 1 << 6,
+  DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK   = 1 << 7,
+  DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK = 1 << 8,
+  DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK = 1 << 9,
+  DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_NEW_MASK    = 1 << 10,
+  DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_FREE_MASK   = 1 << 11,
+  DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_LOCK_MASK   = 1 << 12,
+  DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_UNLOCK_MASK = 1 << 13,
+  DBUS_THREAD_FUNCTIONS_ALL_MASK     = (1 << 14) - 1
+} DBusThreadFunctionsMask;
+
+/**
+ * Functions that must be implemented to make the D-Bus library
+ * thread-aware. The recursive mutex functions should be specified
+ * rather than the old, deprecated nonrecursive ones.
+ *
+ * The condition variable functions have to work with recursive
+ * mutexes if you provide those, or with nonrecursive mutexes if you
+ * provide those.
+ *
+ * If implementing threads using pthreads, be aware that
+ * PTHREAD_MUTEX_RECURSIVE is broken in combination with condition
+ * variables. libdbus relies on the Java-style behavior that when
+ * waiting on a condition, the recursion count is saved and restored,
+ * and the mutex is completely unlocked, not just decremented one
+ * level of recursion.
+ *
+ * Thus with pthreads you probably have to roll your own emulated
+ * recursive mutexes, you can't use PTHREAD_MUTEX_RECURSIVE. This is
+ * what dbus_threads_init_default() does on platforms that use
+ * pthreads.
+ */
+typedef struct
+{
+  unsigned int mask; /**< Mask indicating which functions are present. */
+
+  DBusMutexNewFunction mutex_new; /**< Function to create a mutex; optional and deprecated. */
+  DBusMutexFreeFunction mutex_free; /**< Function to free a mutex; optional and deprecated. */
+  DBusMutexLockFunction mutex_lock; /**< Function to lock a mutex; optional and deprecated. */
+  DBusMutexUnlockFunction mutex_unlock; /**< Function to unlock a mutex; optional and deprecated. */
+
+  DBusCondVarNewFunction condvar_new; /**< Function to create a condition variable */
+  DBusCondVarFreeFunction condvar_free; /**< Function to free a condition variable */
+  DBusCondVarWaitFunction condvar_wait; /**< Function to wait on a condition */
+  DBusCondVarWaitTimeoutFunction condvar_wait_timeout; /**< Function to wait on a condition with a timeout */
+  DBusCondVarWakeOneFunction condvar_wake_one; /**< Function to wake one thread waiting on the condition */
+  DBusCondVarWakeAllFunction condvar_wake_all; /**< Function to wake all threads waiting on the condition */
+  DBusRecursiveMutexNewFunction recursive_mutex_new; /**< Function to create a recursive mutex */
+  DBusRecursiveMutexFreeFunction recursive_mutex_free; /**< Function to free a recursive mutex */
+  DBusRecursiveMutexLockFunction recursive_mutex_lock; /**< Function to lock a recursive mutex */
+  DBusRecursiveMutexUnlockFunction recursive_mutex_unlock; /**< Function to unlock a recursive mutex */
+
+  void (* padding1) (void); /**< Reserved for future expansion */
+  void (* padding2) (void); /**< Reserved for future expansion */
+  void (* padding3) (void); /**< Reserved for future expansion */
+  void (* padding4) (void); /**< Reserved for future expansion */
+  
+} DBusThreadFunctions;
+
+dbus_bool_t  dbus_threads_init         (const DBusThreadFunctions *functions);
+dbus_bool_t  dbus_threads_init_default (void);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_THREADS_H */
diff --git a/src/dbus/dbus-timeout.c b/src/dbus/dbus-timeout.c
new file mode 100644 (file)
index 0000000..5e71c4b
--- /dev/null
@@ -0,0 +1,490 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-timeout.c DBusTimeout implementation
+ *
+ * Copyright (C) 2003  CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-internals.h"
+#include "dbus-timeout.h"
+#include "dbus-list.h"
+
+/**
+ * @defgroup DBusTimeoutInternals DBusTimeout implementation details
+ * @ingroup  DBusInternals
+ * @brief implementation details for DBusTimeout
+ * 
+ * @{
+ */
+
+/**
+ * Internals of DBusTimeout
+ */
+struct DBusTimeout
+{
+  int refcount;                                /**< Reference count */
+  int interval;                                /**< Timeout interval in milliseconds. */
+
+  DBusTimeoutHandler handler;                  /**< Timeout handler. */
+  void *handler_data;                          /**< Timeout handler data. */
+  DBusFreeFunction free_handler_data_function; /**< Free the timeout handler data. */
+  
+  void *data;                                 /**< Application data. */
+  DBusFreeFunction free_data_function;         /**< Free the application data. */
+  unsigned int enabled : 1;                    /**< True if timeout is active. */
+};
+
+/**
+ * Creates a new DBusTimeout, enabled by default.
+ * @param interval the timeout interval in milliseconds.
+ * @param handler function to call when the timeout occurs.
+ * @param data data to pass to the handler
+ * @param free_data_function function to be called to free the data.
+ * @returns the new DBusTimeout object,
+ */
+DBusTimeout*
+_dbus_timeout_new (int                 interval,
+                  DBusTimeoutHandler  handler,
+                  void               *data,
+                  DBusFreeFunction    free_data_function)
+{
+  DBusTimeout *timeout;
+
+  timeout = dbus_new0 (DBusTimeout, 1);
+  if (timeout == NULL)
+    return NULL;
+  
+  timeout->refcount = 1;
+  timeout->interval = interval;
+
+  timeout->handler = handler;
+  timeout->handler_data = data;
+  timeout->free_handler_data_function = free_data_function;
+
+  timeout->enabled = TRUE;
+  
+  return timeout;
+}
+
+/**
+ * Increments the reference count of a DBusTimeout object.
+ *
+ * @param timeout the timeout object.
+ * @returns the timeout object.
+ */
+DBusTimeout *
+_dbus_timeout_ref (DBusTimeout *timeout)
+{
+  timeout->refcount += 1;
+
+  return timeout;
+}
+
+/**
+ * Decrements the reference count of a DBusTimeout object
+ * and finalizes the object if the count reaches zero.
+ *
+ * @param timeout the timeout object.
+ */
+void
+_dbus_timeout_unref (DBusTimeout *timeout)
+{
+  _dbus_assert (timeout != NULL);
+  _dbus_assert (timeout->refcount > 0);
+  
+  timeout->refcount -= 1;
+  if (timeout->refcount == 0)
+    {
+      dbus_timeout_set_data (timeout, NULL, NULL); /* call free_data_function */
+
+      if (timeout->free_handler_data_function)
+       (* timeout->free_handler_data_function) (timeout->handler_data);
+      
+      dbus_free (timeout);
+    }
+}
+
+/**
+ * Changes the timeout interval. Note that you have to disable and
+ * re-enable the timeout using the timeout toggle function
+ * (_dbus_connection_toggle_timeout_unlocked() etc.) to notify the
+ * application of this change.
+ *
+ * @param timeout the timeout
+ * @param interval the new interval
+ */
+void
+_dbus_timeout_set_interval (DBusTimeout *timeout,
+                            int          interval)
+{
+  _dbus_assert (interval >= 0);
+  
+  timeout->interval = interval;
+}
+
+/**
+ * Changes the timeout's enabled-ness. Note that you should use
+ * _dbus_connection_toggle_timeout_unlocked() etc. instead, if
+ * the timeout is passed out to an application main loop.
+ * i.e. you can't use this function in the D-Bus library, it's
+ * only used in the message bus daemon implementation.
+ *
+ * @param timeout the timeout
+ * @param enabled #TRUE if timeout should be enabled.
+ */
+void
+_dbus_timeout_set_enabled (DBusTimeout  *timeout,
+                           dbus_bool_t   enabled)
+{
+  timeout->enabled = enabled != FALSE;
+}
+
+
+/**
+ * @typedef DBusTimeoutList
+ *
+ * Opaque data type representing a list of timeouts
+ * and a set of DBusAddTimeoutFunction/DBusRemoveTimeoutFunction.
+ * Automatically handles removing/re-adding timeouts
+ * when the DBusAddTimeoutFunction is updated or changed.
+ * Holds a reference count to each timeout.
+ *
+ */
+
+/**
+ * DBusTimeoutList implementation details. All fields
+ * are private.
+ *
+ */
+struct DBusTimeoutList
+{
+  DBusList *timeouts; /**< Timeout objects. */
+
+  DBusAddTimeoutFunction add_timeout_function;       /**< Callback for adding a timeout. */
+  DBusRemoveTimeoutFunction remove_timeout_function; /**< Callback for removing a timeout. */
+  DBusTimeoutToggledFunction timeout_toggled_function; /**< Callback when timeout is enabled/disabled or changes interval */
+  void *timeout_data;                                /**< Data for timeout callbacks */
+  DBusFreeFunction timeout_free_data_function;       /**< Free function for timeout callback data */
+};
+
+/**
+ * Creates a new timeout list. Returns #NULL if insufficient
+ * memory exists.
+ *
+ * @returns the new timeout list, or #NULL on failure.
+ */
+DBusTimeoutList*
+_dbus_timeout_list_new (void)
+{
+  DBusTimeoutList *timeout_list;
+
+  timeout_list = dbus_new0 (DBusTimeoutList, 1);
+  if (timeout_list == NULL)
+    return NULL;
+
+  return timeout_list;
+}
+
+/**
+ * Frees a DBusTimeoutList.
+ *
+ * @param timeout_list the timeout list.
+ */
+void
+_dbus_timeout_list_free (DBusTimeoutList *timeout_list)
+{
+  /* free timeout_data and remove timeouts as a side effect */
+  _dbus_timeout_list_set_functions (timeout_list,
+                                   NULL, NULL, NULL, NULL, NULL);
+
+  _dbus_list_foreach (&timeout_list->timeouts,
+                     (DBusForeachFunction) _dbus_timeout_unref,
+                     NULL);
+  _dbus_list_clear (&timeout_list->timeouts);
+
+  dbus_free (timeout_list);
+}
+
+/**
+ * Sets the timeout functions. This function is the "backend"
+ * for dbus_connection_set_timeout_functions().
+ *
+ * @param timeout_list the timeout list
+ * @param add_function the add timeout function.
+ * @param remove_function the remove timeout function.
+ * @param toggled_function toggle notify function, or #NULL
+ * @param data the data for those functions.
+ * @param free_data_function the function to free the data.
+ * @returns #FALSE if no memory
+ *
+ */
+dbus_bool_t
+_dbus_timeout_list_set_functions (DBusTimeoutList           *timeout_list,
+                                 DBusAddTimeoutFunction     add_function,
+                                 DBusRemoveTimeoutFunction  remove_function,
+                                  DBusTimeoutToggledFunction toggled_function,
+                                 void                      *data,
+                                 DBusFreeFunction           free_data_function)
+{
+  /* Add timeouts with the new function, failing on OOM */
+  if (add_function != NULL)
+    {
+      DBusList *link;
+      
+      link = _dbus_list_get_first_link (&timeout_list->timeouts);
+      while (link != NULL)
+        {
+          DBusList *next = _dbus_list_get_next_link (&timeout_list->timeouts,
+                                                     link);
+      
+          if (!(* add_function) (link->data, data))
+            {
+              /* remove it all again and return FALSE */
+              DBusList *link2;
+              
+              link2 = _dbus_list_get_first_link (&timeout_list->timeouts);
+              while (link2 != link)
+                {
+                  DBusList *next = _dbus_list_get_next_link (&timeout_list->timeouts,
+                                                             link2);
+
+                  (* remove_function) (link2->data, data);
+                  
+                  link2 = next;
+                }
+
+              return FALSE;
+            }
+      
+          link = next;
+        }
+    }
+  
+  /* Remove all current timeouts from previous timeout handlers */
+
+  if (timeout_list->remove_timeout_function != NULL)
+    {
+      _dbus_list_foreach (&timeout_list->timeouts,
+                         (DBusForeachFunction) timeout_list->remove_timeout_function,
+                         timeout_list->timeout_data);
+    }
+
+  if (timeout_list->timeout_free_data_function != NULL)
+    (* timeout_list->timeout_free_data_function) (timeout_list->timeout_data);
+
+  timeout_list->add_timeout_function = add_function;
+  timeout_list->remove_timeout_function = remove_function;
+  timeout_list->timeout_toggled_function = toggled_function;
+  timeout_list->timeout_data = data;
+  timeout_list->timeout_free_data_function = free_data_function;
+
+  return TRUE;
+}
+
+/**
+ * Adds a new timeout to the timeout list, invoking the
+ * application DBusAddTimeoutFunction if appropriate.
+ *
+ * @param timeout_list the timeout list.
+ * @param timeout the timeout to add.
+ * @returns #TRUE on success, #FALSE If no memory.
+ */
+dbus_bool_t
+_dbus_timeout_list_add_timeout (DBusTimeoutList *timeout_list,
+                               DBusTimeout     *timeout)
+{
+  if (!_dbus_list_append (&timeout_list->timeouts, timeout))
+    return FALSE;
+
+  _dbus_timeout_ref (timeout);
+
+  if (timeout_list->add_timeout_function != NULL)
+    {
+      if (!(* timeout_list->add_timeout_function) (timeout,
+                                                   timeout_list->timeout_data))
+        {
+          _dbus_list_remove_last (&timeout_list->timeouts, timeout);
+          _dbus_timeout_unref (timeout);
+          return FALSE;
+        }
+    }
+
+  return TRUE;
+}
+
+/**
+ * Removes a timeout from the timeout list, invoking the
+ * application's DBusRemoveTimeoutFunction if appropriate.
+ *
+ * @param timeout_list the timeout list.
+ * @param timeout the timeout to remove.
+ */
+void
+_dbus_timeout_list_remove_timeout (DBusTimeoutList *timeout_list,
+                                  DBusTimeout     *timeout)
+{
+  if (!_dbus_list_remove (&timeout_list->timeouts, timeout))
+    _dbus_assert_not_reached ("Nonexistent timeout was removed");
+
+  if (timeout_list->remove_timeout_function != NULL)
+    (* timeout_list->remove_timeout_function) (timeout,
+                                              timeout_list->timeout_data);
+
+  _dbus_timeout_unref (timeout);
+}
+
+/**
+ * Sets a timeout to the given enabled state, invoking the
+ * application's DBusTimeoutToggledFunction if appropriate.
+ *
+ * @param timeout_list the timeout list.
+ * @param timeout the timeout to toggle.
+ * @param enabled #TRUE to enable
+ */
+void
+_dbus_timeout_list_toggle_timeout (DBusTimeoutList           *timeout_list,
+                                   DBusTimeout               *timeout,
+                                   dbus_bool_t                enabled)
+{
+  enabled = !!enabled;
+  
+  if (enabled == timeout->enabled)
+    return;
+
+  timeout->enabled = enabled;
+  
+  if (timeout_list->timeout_toggled_function != NULL)
+    (* timeout_list->timeout_toggled_function) (timeout,
+                                                timeout_list->timeout_data);
+}
+
+/** @} */
+
+/**
+ * @defgroup DBusTimeout DBusTimeout
+ * @ingroup  DBus
+ * @brief Object representing a timeout
+ *
+ * Types and functions related to DBusTimeout. A timeout
+ * represents a timeout that the main loop needs to monitor,
+ * as in Qt's QTimer or GLib's g_timeout_add().
+ *
+ * Use dbus_connection_set_timeout_functions() or dbus_server_set_timeout_functions()
+ * to be notified when libdbus needs to add or remove timeouts.
+ * 
+ * @{
+ */
+
+
+/**
+ * @typedef DBusTimeout
+ *
+ * Opaque object representing a timeout.
+ */
+
+/**
+ * Gets the timeout interval. The dbus_timeout_handle()
+ * should be called each time this interval elapses,
+ * starting after it elapses once.
+ *
+ * The interval may change during the life of the
+ * timeout; if so, the timeout will be disabled and
+ * re-enabled (calling the "timeout toggled function")
+ * to notify you of the change.
+ *
+ * @param timeout the DBusTimeout object.
+ * @returns the interval in milliseconds.
+ */
+int
+dbus_timeout_get_interval (DBusTimeout *timeout)
+{
+  return timeout->interval;
+}
+
+/**
+ * Gets data previously set with dbus_timeout_set_data()
+ * or #NULL if none.
+ *
+ * @param timeout the DBusTimeout object.
+ * @returns previously-set data.
+ */
+void*
+dbus_timeout_get_data (DBusTimeout *timeout)
+{
+  return timeout->data;
+}
+
+/**
+ * Sets data which can be retrieved with dbus_timeout_get_data().
+ * Intended for use by the DBusAddTimeoutFunction and
+ * DBusRemoveTimeoutFunction to store their own data.  For example with
+ * Qt you might store the QTimer for this timeout and with GLib
+ * you might store a g_timeout_add result id.
+ *
+ * @param timeout the DBusTimeout object.
+ * @param data the data.
+ * @param free_data_function function to be called to free the data.
+ */
+void
+dbus_timeout_set_data (DBusTimeout      *timeout,
+                      void             *data,
+                      DBusFreeFunction  free_data_function)
+{
+  if (timeout->free_data_function != NULL)
+    (* timeout->free_data_function) (timeout->data);
+
+  timeout->data = data;
+  timeout->free_data_function = free_data_function;
+}
+
+/**
+ * Calls the timeout handler for this timeout.
+ * This function should be called when the timeout
+ * occurs.
+ *
+ * If this function returns #FALSE, then there wasn't
+ * enough memory to handle the timeout. Typically just
+ * letting the timeout fire again next time it naturally
+ * times out is an adequate response to that problem,
+ * but you could try to do more if you wanted.
+ *
+ * @param timeout the DBusTimeout object.
+ * @returns #FALSE if there wasn't enough memory 
+ */
+dbus_bool_t
+dbus_timeout_handle (DBusTimeout *timeout)
+{
+  return (* timeout->handler) (timeout->handler_data);
+}
+
+
+/**
+ * Returns whether a timeout is enabled or not. If not
+ * enabled, it should not be polled by the main loop.
+ *
+ * @param timeout the DBusTimeout object
+ * @returns #TRUE if the timeout is enabled
+ */
+dbus_bool_t
+dbus_timeout_get_enabled (DBusTimeout *timeout)
+{
+  return timeout->enabled;
+}
+
+/** @} end public API docs */
diff --git a/src/dbus/dbus-timeout.h b/src/dbus/dbus-timeout.h
new file mode 100644 (file)
index 0000000..b2312ee
--- /dev/null
@@ -0,0 +1,75 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-timeout.h DBusTimeout internal interfaces
+ *
+ * Copyright (C) 2003  CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_TIMEOUT_H
+#define DBUS_TIMEOUT_H
+
+#include <dbus/dbus-connection.h>
+#include <dbus/dbus-internals.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusTimeoutInternals
+ * @{
+ */
+
+/* Public methods on DBusTimeout are in dbus-connection.h */
+
+typedef struct DBusTimeoutList DBusTimeoutList;
+
+/** function to run when the timeout is handled */
+typedef dbus_bool_t (* DBusTimeoutHandler) (void *data);
+
+DBusTimeout* _dbus_timeout_new          (int                 interval,
+                                         DBusTimeoutHandler  handler,
+                                         void               *data,
+                                         DBusFreeFunction    free_data_function);
+DBusTimeout* _dbus_timeout_ref          (DBusTimeout        *timeout);
+void         _dbus_timeout_unref        (DBusTimeout        *timeout);
+void         _dbus_timeout_set_interval (DBusTimeout        *timeout,
+                                         int                 interval);
+void         _dbus_timeout_set_enabled  (DBusTimeout        *timeout,
+                                         dbus_bool_t         enabled);
+
+DBusTimeoutList *_dbus_timeout_list_new            (void);
+void             _dbus_timeout_list_free           (DBusTimeoutList           *timeout_list);
+dbus_bool_t      _dbus_timeout_list_set_functions  (DBusTimeoutList           *timeout_list,
+                                                   DBusAddTimeoutFunction     add_function,
+                                                   DBusRemoveTimeoutFunction  remove_function,
+                                                    DBusTimeoutToggledFunction toggled_function,
+                                                   void                      *data,
+                                                   DBusFreeFunction           free_data_function);
+dbus_bool_t      _dbus_timeout_list_add_timeout    (DBusTimeoutList           *timeout_list,
+                                                   DBusTimeout               *timeout);
+void             _dbus_timeout_list_remove_timeout (DBusTimeoutList           *timeout_list,
+                                                   DBusTimeout               *timeout);
+void             _dbus_timeout_list_toggle_timeout (DBusTimeoutList           *timeout_list,
+                                                    DBusTimeout               *timeout,
+                                                    dbus_bool_t                enabled);
+
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_TIMEOUT_H */
diff --git a/src/dbus/dbus-transport-protected.h b/src/dbus/dbus-transport-protected.h
new file mode 100644 (file)
index 0000000..4d56a72
--- /dev/null
@@ -0,0 +1,143 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-transport-protected.h Used by subclasses of DBusTransport object (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2004  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_TRANSPORT_PROTECTED_H
+#define DBUS_TRANSPORT_PROTECTED_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-transport.h>
+#include <dbus/dbus-message-internal.h>
+#include <dbus/dbus-auth.h>
+#include <dbus/dbus-resources.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusTransportVTable DBusTransportVTable;
+
+/**
+ * The virtual table that must be implemented to
+ * create a new kind of transport.
+ */
+struct DBusTransportVTable
+{
+  void        (* finalize)              (DBusTransport *transport);
+  /**< The finalize method must free the transport. */
+
+  dbus_bool_t (* handle_watch)          (DBusTransport *transport,
+                                         DBusWatch     *watch,
+                                         unsigned int   flags);
+  /**< The handle_watch method handles reading/writing
+   * data as indicated by the flags.
+   */
+
+  void        (* disconnect)            (DBusTransport *transport);
+  /**< Disconnect this transport. */
+
+  dbus_bool_t (* connection_set)        (DBusTransport *transport);
+  /**< Called when transport->connection has been filled in */
+
+  void        (* do_iteration)          (DBusTransport *transport,
+                                         unsigned int   flags,
+                                         int            timeout_milliseconds);
+  /**< Called to do a single "iteration" (block on select/poll
+   * followed by reading or writing data).
+   */
+
+  void        (* live_messages_changed) (DBusTransport *transport);
+  /**< Outstanding messages counter changed */
+
+  dbus_bool_t (* get_socket_fd) (DBusTransport *transport,
+                                 int           *fd_p);
+  /**< Get socket file descriptor */
+};
+
+/**
+ * Object representing a transport such as a socket.
+ * A transport can shuttle messages from point A to point B,
+ * and is the backend for a #DBusConnection.
+ *
+ */
+struct DBusTransport
+{
+  int refcount;                               /**< Reference count. */
+
+  const DBusTransportVTable *vtable;          /**< Virtual methods for this instance. */
+
+  DBusConnection *connection;                 /**< Connection owning this transport. */
+
+  DBusMessageLoader *loader;                  /**< Message-loading buffer. */
+
+  DBusAuth *auth;                             /**< Authentication conversation */
+
+  DBusCredentials *credentials;               /**< Credentials of other end read from the socket */  
+
+  long max_live_messages_size;                /**< Max total size of received messages. */
+
+  DBusCounter *live_messages_size;            /**< Counter for size of all live messages. */
+
+
+  char *address;                              /**< Address of the server we are connecting to (#NULL for the server side of a transport) */
+
+  char *expected_guid;                        /**< GUID we expect the server to have, #NULL on server side or if we don't have an expectation */
+  
+  DBusAllowUnixUserFunction unix_user_function; /**< Function for checking whether a user is authorized. */
+  void *unix_user_data;                         /**< Data for unix_user_function */
+  
+  DBusFreeFunction free_unix_user_data;         /**< Function to free unix_user_data */
+
+  DBusAllowWindowsUserFunction windows_user_function; /**< Function for checking whether a user is authorized. */
+  void *windows_user_data;                            /**< Data for windows_user_function */
+  
+  DBusFreeFunction free_windows_user_data;            /**< Function to free windows_user_data */
+  
+  unsigned int disconnected : 1;              /**< #TRUE if we are disconnected. */
+  unsigned int authenticated : 1;             /**< Cache of auth state; use _dbus_transport_get_is_authenticated() to query value */
+  unsigned int send_credentials_pending : 1;  /**< #TRUE if we need to send credentials */
+  unsigned int receive_credentials_pending : 1; /**< #TRUE if we need to receive credentials */
+  unsigned int is_server : 1;                 /**< #TRUE if on the server side */
+  unsigned int unused_bytes_recovered : 1;    /**< #TRUE if we've recovered unused bytes from auth */
+  unsigned int allow_anonymous : 1;           /**< #TRUE if an anonymous client can connect */
+};
+
+dbus_bool_t _dbus_transport_init_base     (DBusTransport             *transport,
+                                           const DBusTransportVTable *vtable,
+                                           const DBusString          *server_guid,
+                                           const DBusString          *address);
+void        _dbus_transport_finalize_base (DBusTransport             *transport);
+
+
+typedef enum
+{
+  DBUS_TRANSPORT_OPEN_NOT_HANDLED,    /**< we aren't in charge of this address type */
+  DBUS_TRANSPORT_OPEN_OK,             /**< we set up the listen */
+  DBUS_TRANSPORT_OPEN_BAD_ADDRESS,    /**< malformed address */
+  DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT /**< well-formed address but failed to set it up */
+} DBusTransportOpenResult;
+
+DBusTransportOpenResult _dbus_transport_open_platform_specific (DBusAddressEntry  *entry,
+                                                                DBusTransport    **transport_p,
+                                                                DBusError         *error);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_TRANSPORT_PROTECTED_H */
diff --git a/src/dbus/dbus-transport-socket.c b/src/dbus/dbus-transport-socket.c
new file mode 100644 (file)
index 0000000..6d7c89c
--- /dev/null
@@ -0,0 +1,1339 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-transport-socket.c  Socket subclasses of DBusTransport
+ *
+ * Copyright (C) 2002, 2003, 2004, 2006  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-internals.h"
+#include "dbus-connection-internal.h"
+#include "dbus-transport-socket.h"
+#include "dbus-transport-protected.h"
+#include "dbus-watch.h"
+#include "dbus-credentials.h"
+
+
+/**
+ * @defgroup DBusTransportSocket DBusTransport implementations for sockets
+ * @ingroup  DBusInternals
+ * @brief Implementation details of DBusTransport on sockets
+ *
+ * @{
+ */
+
+/**
+ * Opaque object representing a socket file descriptor transport.
+ */
+typedef struct DBusTransportSocket DBusTransportSocket;
+
+/**
+ * Implementation details of DBusTransportSocket. All members are private.
+ */
+struct DBusTransportSocket
+{
+  DBusTransport base;                   /**< Parent instance */
+  int fd;                               /**< File descriptor. */
+  DBusWatch *read_watch;                /**< Watch for readability. */
+  DBusWatch *write_watch;               /**< Watch for writability. */
+
+  int max_bytes_read_per_iteration;     /**< To avoid blocking too long. */
+  int max_bytes_written_per_iteration;  /**< To avoid blocking too long. */
+
+  int message_bytes_written;            /**< Number of bytes of current
+                                         *   outgoing message that have
+                                         *   been written.
+                                         */
+  DBusString encoded_outgoing;          /**< Encoded version of current
+                                         *   outgoing message.
+                                         */
+  DBusString encoded_incoming;          /**< Encoded version of current
+                                         *   incoming data.
+                                         */
+};
+
+static void
+free_watches (DBusTransport *transport)
+{
+  DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+
+  _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME);
+  
+  if (socket_transport->read_watch)
+    {
+      if (transport->connection)
+        _dbus_connection_remove_watch_unlocked (transport->connection,
+                                                socket_transport->read_watch);
+      _dbus_watch_invalidate (socket_transport->read_watch);
+      _dbus_watch_unref (socket_transport->read_watch);
+      socket_transport->read_watch = NULL;
+    }
+
+  if (socket_transport->write_watch)
+    {
+      if (transport->connection)
+        _dbus_connection_remove_watch_unlocked (transport->connection,
+                                                socket_transport->write_watch);
+      _dbus_watch_invalidate (socket_transport->write_watch);
+      _dbus_watch_unref (socket_transport->write_watch);
+      socket_transport->write_watch = NULL;
+    }
+
+  _dbus_verbose ("%s end\n", _DBUS_FUNCTION_NAME);
+}
+
+static void
+socket_finalize (DBusTransport *transport)
+{
+  DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+
+  _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME);
+  
+  free_watches (transport);
+
+  _dbus_string_free (&socket_transport->encoded_outgoing);
+  _dbus_string_free (&socket_transport->encoded_incoming);
+  
+  _dbus_transport_finalize_base (transport);
+
+  _dbus_assert (socket_transport->read_watch == NULL);
+  _dbus_assert (socket_transport->write_watch == NULL);
+  
+  dbus_free (transport);
+}
+
+static void
+check_write_watch (DBusTransport *transport)
+{
+  DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+  dbus_bool_t needed;
+
+  if (transport->connection == NULL)
+    return;
+
+  if (transport->disconnected)
+    {
+      _dbus_assert (socket_transport->write_watch == NULL);
+      return;
+    }
+  
+  _dbus_transport_ref (transport);
+
+  if (_dbus_transport_get_is_authenticated (transport))
+    needed = _dbus_connection_has_messages_to_send_unlocked (transport->connection);
+  else
+    {
+      if (transport->send_credentials_pending)
+        needed = TRUE;
+      else
+        {
+          DBusAuthState auth_state;
+          
+          auth_state = _dbus_auth_do_work (transport->auth);
+          
+          /* If we need memory we install the write watch just in case,
+           * if there's no need for it, it will get de-installed
+           * next time we try reading.
+           */
+          if (auth_state == DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND ||
+              auth_state == DBUS_AUTH_STATE_WAITING_FOR_MEMORY)
+            needed = TRUE;
+          else
+            needed = FALSE;
+        }
+    }
+
+  _dbus_verbose ("check_write_watch(): needed = %d on connection %p watch %p fd = %d outgoing messages exist %d\n",
+                 needed, transport->connection, socket_transport->write_watch,
+                 socket_transport->fd,
+                 _dbus_connection_has_messages_to_send_unlocked (transport->connection));
+
+  _dbus_connection_toggle_watch_unlocked (transport->connection,
+                                          socket_transport->write_watch,
+                                          needed);
+
+  _dbus_transport_unref (transport);
+}
+
+static void
+check_read_watch (DBusTransport *transport)
+{
+  DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+  dbus_bool_t need_read_watch;
+
+  _dbus_verbose ("%s: fd = %d\n",
+                 _DBUS_FUNCTION_NAME, socket_transport->fd);
+  
+  if (transport->connection == NULL)
+    return;
+
+  if (transport->disconnected)
+    {
+      _dbus_assert (socket_transport->read_watch == NULL);
+      return;
+    }
+  
+  _dbus_transport_ref (transport);
+
+  if (_dbus_transport_get_is_authenticated (transport))
+    need_read_watch =
+      _dbus_counter_get_value (transport->live_messages_size) < transport->max_live_messages_size;
+  else
+    {
+      if (transport->receive_credentials_pending)
+        need_read_watch = TRUE;
+      else
+        {
+          /* The reason to disable need_read_watch when not WAITING_FOR_INPUT
+           * is to avoid spinning on the file descriptor when we're waiting
+           * to write or for some other part of the auth process
+           */
+          DBusAuthState auth_state;
+          
+          auth_state = _dbus_auth_do_work (transport->auth);
+
+          /* If we need memory we install the read watch just in case,
+           * if there's no need for it, it will get de-installed
+           * next time we try reading. If we're authenticated we
+           * install it since we normally have it installed while
+           * authenticated.
+           */
+          if (auth_state == DBUS_AUTH_STATE_WAITING_FOR_INPUT ||
+              auth_state == DBUS_AUTH_STATE_WAITING_FOR_MEMORY ||
+              auth_state == DBUS_AUTH_STATE_AUTHENTICATED)
+            need_read_watch = TRUE;
+          else
+            need_read_watch = FALSE;
+        }
+    }
+
+  _dbus_verbose ("  setting read watch enabled = %d\n", need_read_watch);
+  _dbus_connection_toggle_watch_unlocked (transport->connection,
+                                          socket_transport->read_watch,
+                                          need_read_watch);
+
+  _dbus_transport_unref (transport);
+}
+
+static void
+do_io_error (DBusTransport *transport)
+{
+  _dbus_transport_ref (transport);
+  _dbus_transport_disconnect (transport);
+  _dbus_transport_unref (transport);
+}
+
+/* return value is whether we successfully read any new data. */
+static dbus_bool_t
+read_data_into_auth (DBusTransport *transport,
+                     dbus_bool_t   *oom)
+{
+  DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+  DBusString *buffer;
+  int bytes_read;
+  
+  *oom = FALSE;
+
+  _dbus_auth_get_buffer (transport->auth, &buffer);
+  
+  bytes_read = _dbus_read_socket (socket_transport->fd,
+                                  buffer, socket_transport->max_bytes_read_per_iteration);
+
+  _dbus_auth_return_buffer (transport->auth, buffer,
+                            bytes_read > 0 ? bytes_read : 0);
+
+  if (bytes_read > 0)
+    {
+      _dbus_verbose (" read %d bytes in auth phase\n", bytes_read);
+
+      return TRUE;
+    }
+  else if (bytes_read < 0)
+    {
+      /* EINTR already handled for us */
+
+      if (_dbus_get_is_errno_enomem ())
+        {
+          *oom = TRUE;
+        }
+      else if (_dbus_get_is_errno_eagain_or_ewouldblock ())
+        ; /* do nothing, just return FALSE below */
+      else
+        {
+          _dbus_verbose ("Error reading from remote app: %s\n",
+                         _dbus_strerror_from_errno ());
+          do_io_error (transport);
+        }
+
+      return FALSE;
+    }
+  else
+    {
+      _dbus_assert (bytes_read == 0);
+      
+      _dbus_verbose ("Disconnected from remote app\n");
+      do_io_error (transport);
+
+      return FALSE;
+    }
+}
+
+/* Return value is whether we successfully wrote any bytes */
+static dbus_bool_t
+write_data_from_auth (DBusTransport *transport)
+{
+  DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+  int bytes_written;
+  const DBusString *buffer;
+
+  if (!_dbus_auth_get_bytes_to_send (transport->auth,
+                                     &buffer))
+    return FALSE;
+  
+  bytes_written = _dbus_write_socket (socket_transport->fd,
+                                      buffer,
+                                      0, _dbus_string_get_length (buffer));
+
+  if (bytes_written > 0)
+    {
+      _dbus_auth_bytes_sent (transport->auth, bytes_written);
+      return TRUE;
+    }
+  else if (bytes_written < 0)
+    {
+      /* EINTR already handled for us */
+      
+      if (_dbus_get_is_errno_eagain_or_ewouldblock ())
+        ;
+      else
+        {
+          _dbus_verbose ("Error writing to remote app: %s\n",
+                         _dbus_strerror_from_errno ());
+          do_io_error (transport);
+        }
+    }
+
+  return FALSE;
+}
+
+/* FALSE on OOM */
+static dbus_bool_t
+exchange_credentials (DBusTransport *transport,
+                      dbus_bool_t    do_reading,
+                      dbus_bool_t    do_writing)
+{
+  DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+  DBusError error = DBUS_ERROR_INIT;
+
+  _dbus_verbose ("exchange_credentials: do_reading = %d, do_writing = %d\n",
+                  do_reading, do_writing);
+
+  if (do_writing && transport->send_credentials_pending)
+    {
+      if (_dbus_send_credentials_socket (socket_transport->fd,
+                                         &error))
+        {
+          transport->send_credentials_pending = FALSE;
+        }
+      else
+        {
+          _dbus_verbose ("Failed to write credentials: %s\n", error.message);
+          dbus_error_free (&error);
+          do_io_error (transport);
+        }
+    }
+  
+  if (do_reading && transport->receive_credentials_pending)
+    {
+      /* FIXME this can fail due to IO error _or_ OOM, broken
+       * (somewhat tricky to fix since the OOM error can be set after
+       * we already read the credentials byte, so basically we need to
+       * separate reading the byte and storing it in the
+       * transport->credentials). Does not really matter for now
+       * because storing in credentials never actually fails on unix.
+       */      
+      if (_dbus_read_credentials_socket (socket_transport->fd,
+                                         transport->credentials,
+                                         &error))
+        {
+          transport->receive_credentials_pending = FALSE;
+        }
+      else
+        {
+          _dbus_verbose ("Failed to read credentials %s\n", error.message);
+          dbus_error_free (&error);
+          do_io_error (transport);
+        }
+    }
+
+  if (!(transport->send_credentials_pending ||
+        transport->receive_credentials_pending))
+    {
+      if (!_dbus_auth_set_credentials (transport->auth,
+                                       transport->credentials))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static dbus_bool_t
+do_authentication (DBusTransport *transport,
+                   dbus_bool_t    do_reading,
+                   dbus_bool_t    do_writing,
+                  dbus_bool_t   *auth_completed)
+{
+  dbus_bool_t oom;
+  dbus_bool_t orig_auth_state;
+
+  oom = FALSE;
+  
+  orig_auth_state = _dbus_transport_get_is_authenticated (transport);
+
+  /* This is essential to avoid the check_write_watch() at the end,
+   * we don't want to add a write watch in do_iteration before
+   * we try writing and get EAGAIN
+   */
+  if (orig_auth_state)
+    {
+      if (auth_completed)
+        *auth_completed = FALSE;
+      return TRUE;
+    }
+  
+  _dbus_transport_ref (transport);
+  
+  while (!_dbus_transport_get_is_authenticated (transport) &&
+         _dbus_transport_get_is_connected (transport))
+    {      
+      if (!exchange_credentials (transport, do_reading, do_writing))
+        {
+          /* OOM */
+          oom = TRUE;
+          goto out;
+        }
+      
+      if (transport->send_credentials_pending ||
+          transport->receive_credentials_pending)
+        {
+          _dbus_verbose ("send_credentials_pending = %d receive_credentials_pending = %d\n",
+                         transport->send_credentials_pending,
+                         transport->receive_credentials_pending);
+          goto out;
+        }
+
+#define TRANSPORT_SIDE(t) ((t)->is_server ? "server" : "client")
+      switch (_dbus_auth_do_work (transport->auth))
+        {
+        case DBUS_AUTH_STATE_WAITING_FOR_INPUT:
+          _dbus_verbose (" %s auth state: waiting for input\n",
+                         TRANSPORT_SIDE (transport));
+          if (!do_reading || !read_data_into_auth (transport, &oom))
+            goto out;
+          break;
+      
+        case DBUS_AUTH_STATE_WAITING_FOR_MEMORY:
+          _dbus_verbose (" %s auth state: waiting for memory\n",
+                         TRANSPORT_SIDE (transport));
+          oom = TRUE;
+          goto out;
+          break;
+      
+        case DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND:
+          _dbus_verbose (" %s auth state: bytes to send\n",
+                         TRANSPORT_SIDE (transport));
+          if (!do_writing || !write_data_from_auth (transport))
+            goto out;
+          break;
+      
+        case DBUS_AUTH_STATE_NEED_DISCONNECT:
+          _dbus_verbose (" %s auth state: need to disconnect\n",
+                         TRANSPORT_SIDE (transport));
+          do_io_error (transport);
+          break;
+      
+        case DBUS_AUTH_STATE_AUTHENTICATED:
+          _dbus_verbose (" %s auth state: authenticated\n",
+                         TRANSPORT_SIDE (transport));
+          break;
+        }
+    }
+
+ out:
+  if (auth_completed)
+    *auth_completed = (orig_auth_state != _dbus_transport_get_is_authenticated (transport));
+  
+  check_read_watch (transport);
+  check_write_watch (transport);
+  _dbus_transport_unref (transport);
+
+  if (oom)
+    return FALSE;
+  else
+    return TRUE;
+}
+
+/* returns false on oom */
+static dbus_bool_t
+do_writing (DBusTransport *transport)
+{
+  int total;
+  DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+  dbus_bool_t oom;
+  
+  /* No messages without authentication! */
+  if (!_dbus_transport_get_is_authenticated (transport))
+    {
+      _dbus_verbose ("Not authenticated, not writing anything\n");
+      return TRUE;
+    }
+
+  if (transport->disconnected)
+    {
+      _dbus_verbose ("Not connected, not writing anything\n");
+      return TRUE;
+    }
+
+#if 1
+  _dbus_verbose ("do_writing(), have_messages = %d, fd = %d\n",
+                 _dbus_connection_has_messages_to_send_unlocked (transport->connection),
+                 socket_transport->fd);
+#endif
+  
+  oom = FALSE;
+  total = 0;
+
+  while (!transport->disconnected &&
+         _dbus_connection_has_messages_to_send_unlocked (transport->connection))
+    {
+      int bytes_written;
+      DBusMessage *message;
+      const DBusString *header;
+      const DBusString *body;
+      int header_len, body_len;
+      int total_bytes_to_write;
+      
+      if (total > socket_transport->max_bytes_written_per_iteration)
+        {
+          _dbus_verbose ("%d bytes exceeds %d bytes written per iteration, returning\n",
+                         total, socket_transport->max_bytes_written_per_iteration);
+          goto out;
+        }
+      
+      message = _dbus_connection_get_message_to_send (transport->connection);
+      _dbus_assert (message != NULL);
+      dbus_message_lock (message);
+
+#if 0
+      _dbus_verbose ("writing message %p\n", message);
+#endif
+      
+      _dbus_message_get_network_data (message,
+                                      &header, &body);
+
+      header_len = _dbus_string_get_length (header);
+      body_len = _dbus_string_get_length (body);
+
+      if (_dbus_auth_needs_encoding (transport->auth))
+        {
+          if (_dbus_string_get_length (&socket_transport->encoded_outgoing) == 0)
+            {
+              if (!_dbus_auth_encode_data (transport->auth,
+                                           header, &socket_transport->encoded_outgoing))
+                {
+                  oom = TRUE;
+                  goto out;
+                }
+              
+              if (!_dbus_auth_encode_data (transport->auth,
+                                           body, &socket_transport->encoded_outgoing))
+                {
+                  _dbus_string_set_length (&socket_transport->encoded_outgoing, 0);
+                  oom = TRUE;
+                  goto out;
+                }
+            }
+          
+          total_bytes_to_write = _dbus_string_get_length (&socket_transport->encoded_outgoing);
+
+#if 0
+          _dbus_verbose ("encoded message is %d bytes\n",
+                         total_bytes_to_write);
+#endif
+          
+          bytes_written =
+            _dbus_write_socket (socket_transport->fd,
+                                &socket_transport->encoded_outgoing,
+                                socket_transport->message_bytes_written,
+                                total_bytes_to_write - socket_transport->message_bytes_written);
+        }
+      else
+        {
+          total_bytes_to_write = header_len + body_len;
+
+#if 0
+          _dbus_verbose ("message is %d bytes\n",
+                         total_bytes_to_write);          
+#endif
+          
+          if (socket_transport->message_bytes_written < header_len)
+            {
+              bytes_written =
+                _dbus_write_socket_two (socket_transport->fd,
+                                        header,
+                                        socket_transport->message_bytes_written,
+                                        header_len - socket_transport->message_bytes_written,
+                                        body,
+                                        0, body_len);
+            }
+          else
+            {
+              bytes_written =
+                _dbus_write_socket (socket_transport->fd,
+                                    body,
+                                    (socket_transport->message_bytes_written - header_len),
+                                    body_len -
+                                    (socket_transport->message_bytes_written - header_len));
+            }
+        }
+
+      if (bytes_written < 0)
+        {
+          /* EINTR already handled for us */
+          
+          if (_dbus_get_is_errno_eagain_or_ewouldblock ())
+            goto out;
+          else
+            {
+              _dbus_verbose ("Error writing to remote app: %s\n",
+                             _dbus_strerror_from_errno ());
+              do_io_error (transport);
+              goto out;
+            }
+        }
+      else
+        {
+          _dbus_verbose (" wrote %d bytes of %d\n", bytes_written,
+                         total_bytes_to_write);
+          
+          total += bytes_written;
+          socket_transport->message_bytes_written += bytes_written;
+
+          _dbus_assert (socket_transport->message_bytes_written <=
+                        total_bytes_to_write);
+          
+          if (socket_transport->message_bytes_written == total_bytes_to_write)
+            {
+              socket_transport->message_bytes_written = 0;
+              _dbus_string_set_length (&socket_transport->encoded_outgoing, 0);
+              _dbus_string_compact (&socket_transport->encoded_outgoing, 2048);
+
+              _dbus_connection_message_sent (transport->connection,
+                                             message);
+            }
+        }
+    }
+
+ out:
+  if (oom)
+    return FALSE;
+  else
+    return TRUE;
+}
+
+/* returns false on out-of-memory */
+static dbus_bool_t
+do_reading (DBusTransport *transport)
+{
+  DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+  DBusString *buffer;
+  int bytes_read;
+  int total;
+  dbus_bool_t oom;
+
+  _dbus_verbose ("%s: fd = %d\n", _DBUS_FUNCTION_NAME,
+                 socket_transport->fd);
+  
+  /* No messages without authentication! */
+  if (!_dbus_transport_get_is_authenticated (transport))
+    return TRUE;
+
+  oom = FALSE;
+  
+  total = 0;
+
+ again:
+  
+  /* See if we've exceeded max messages and need to disable reading */
+  check_read_watch (transport);
+  
+  if (total > socket_transport->max_bytes_read_per_iteration)
+    {
+      _dbus_verbose ("%d bytes exceeds %d bytes read per iteration, returning\n",
+                     total, socket_transport->max_bytes_read_per_iteration);
+      goto out;
+    }
+
+  _dbus_assert (socket_transport->read_watch != NULL ||
+                transport->disconnected);
+  
+  if (transport->disconnected)
+    goto out;
+
+  if (!dbus_watch_get_enabled (socket_transport->read_watch))
+    return TRUE;
+  
+  if (_dbus_auth_needs_decoding (transport->auth))
+    {
+      if (_dbus_string_get_length (&socket_transport->encoded_incoming) > 0)
+        bytes_read = _dbus_string_get_length (&socket_transport->encoded_incoming);
+      else
+        bytes_read = _dbus_read_socket (socket_transport->fd,
+                                        &socket_transport->encoded_incoming,
+                                        socket_transport->max_bytes_read_per_iteration);
+
+      _dbus_assert (_dbus_string_get_length (&socket_transport->encoded_incoming) ==
+                    bytes_read);
+      
+      if (bytes_read > 0)
+        {
+          int orig_len;
+          
+          _dbus_message_loader_get_buffer (transport->loader,
+                                           &buffer);
+
+          orig_len = _dbus_string_get_length (buffer);
+          
+          if (!_dbus_auth_decode_data (transport->auth,
+                                       &socket_transport->encoded_incoming,
+                                       buffer))
+            {
+              _dbus_verbose ("Out of memory decoding incoming data\n");
+              _dbus_message_loader_return_buffer (transport->loader,
+                                              buffer,
+                                              _dbus_string_get_length (buffer) - orig_len);
+
+              oom = TRUE;
+              goto out;
+            }
+
+          _dbus_message_loader_return_buffer (transport->loader,
+                                              buffer,
+                                              _dbus_string_get_length (buffer) - orig_len);
+
+          _dbus_string_set_length (&socket_transport->encoded_incoming, 0);
+          _dbus_string_compact (&socket_transport->encoded_incoming, 2048);
+        }
+    }
+  else
+    {
+      _dbus_message_loader_get_buffer (transport->loader,
+                                       &buffer);
+      
+      bytes_read = _dbus_read_socket (socket_transport->fd,
+                                      buffer, socket_transport->max_bytes_read_per_iteration);
+      
+      _dbus_message_loader_return_buffer (transport->loader,
+                                          buffer,
+                                          bytes_read < 0 ? 0 : bytes_read);
+    }
+  
+  if (bytes_read < 0)
+    {
+      /* EINTR already handled for us */
+
+      if (_dbus_get_is_errno_enomem ())
+        {
+          _dbus_verbose ("Out of memory in read()/do_reading()\n");
+          oom = TRUE;
+          goto out;
+        }
+      else if (_dbus_get_is_errno_eagain_or_ewouldblock ())
+        goto out;
+      else
+        {
+          _dbus_verbose ("Error reading from remote app: %s\n",
+                         _dbus_strerror_from_errno ());
+          do_io_error (transport);
+          goto out;
+        }
+    }
+  else if (bytes_read == 0)
+    {
+      _dbus_verbose ("Disconnected from remote app\n");
+      do_io_error (transport);
+      goto out;
+    }
+  else
+    {
+      _dbus_verbose (" read %d bytes\n", bytes_read);
+      
+      total += bytes_read;      
+
+      if (!_dbus_transport_queue_messages (transport))
+        {
+          oom = TRUE;
+          _dbus_verbose (" out of memory when queueing messages we just read in the transport\n");
+          goto out;
+        }
+      
+      /* Try reading more data until we get EAGAIN and return, or
+       * exceed max bytes per iteration.  If in blocking mode of
+       * course we'll block instead of returning.
+       */
+      goto again;
+    }
+
+ out:
+  if (oom)
+    return FALSE;
+  else
+    return TRUE;
+}
+
+static dbus_bool_t
+socket_handle_watch (DBusTransport *transport,
+                   DBusWatch     *watch,
+                   unsigned int   flags)
+{
+  DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+
+  _dbus_assert (watch == socket_transport->read_watch ||
+                watch == socket_transport->write_watch);
+  _dbus_assert (watch != NULL);
+  
+  /* Disconnect in case of an error.  In case of hangup do not
+   * disconnect the transport because data can still be in the buffer
+   * and do_reading may need several iteration to read it all (because
+   * of its max_bytes_read_per_iteration limit).  The condition where
+   * flags == HANGUP (without READABLE) probably never happen in fact.
+   */
+  if ((flags & DBUS_WATCH_ERROR) ||
+      ((flags & DBUS_WATCH_HANGUP) && !(flags & DBUS_WATCH_READABLE)))
+    {
+      _dbus_verbose ("Hang up or error on watch\n");
+      _dbus_transport_disconnect (transport);
+      return TRUE;
+    }
+  
+  if (watch == socket_transport->read_watch &&
+      (flags & DBUS_WATCH_READABLE))
+    {
+      dbus_bool_t auth_finished;
+#if 1
+      _dbus_verbose ("handling read watch %p flags = %x\n",
+                     watch, flags);
+#endif
+      if (!do_authentication (transport, TRUE, FALSE, &auth_finished))
+        return FALSE;
+
+      /* We don't want to do a read immediately following
+       * a successful authentication.  This is so we
+       * have a chance to propagate the authentication
+       * state further up.  Specifically, we need to
+       * process any pending data from the auth object.
+       */
+      if (!auth_finished)
+       {
+         if (!do_reading (transport))
+           {
+             _dbus_verbose ("no memory to read\n");
+             return FALSE;
+           }
+       }
+      else
+        {
+          _dbus_verbose ("Not reading anything since we just completed the authentication\n");
+        }
+    }
+  else if (watch == socket_transport->write_watch &&
+           (flags & DBUS_WATCH_WRITABLE))
+    {
+#if 1
+      _dbus_verbose ("handling write watch, have_outgoing_messages = %d\n",
+                     _dbus_connection_has_messages_to_send_unlocked (transport->connection));
+#endif
+      if (!do_authentication (transport, FALSE, TRUE, NULL))
+        return FALSE;
+      
+      if (!do_writing (transport))
+        {
+          _dbus_verbose ("no memory to write\n");
+          return FALSE;
+        }
+
+      /* See if we still need the write watch */
+      check_write_watch (transport);
+    }
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+  else
+    {
+      if (watch == socket_transport->read_watch)
+        _dbus_verbose ("asked to handle read watch with non-read condition 0x%x\n",
+                       flags);
+      else if (watch == socket_transport->write_watch)
+        _dbus_verbose ("asked to handle write watch with non-write condition 0x%x\n",
+                       flags);
+      else
+        _dbus_verbose ("asked to handle watch %p on fd %d that we don't recognize\n",
+                       watch, dbus_watch_get_socket (watch));
+    }
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
+
+  return TRUE;
+}
+
+static void
+socket_disconnect (DBusTransport *transport)
+{
+  DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+
+  _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME);
+  
+  free_watches (transport);
+  
+  _dbus_close_socket (socket_transport->fd, NULL);
+  socket_transport->fd = -1;
+}
+
+static dbus_bool_t
+socket_connection_set (DBusTransport *transport)
+{
+  DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+
+  _dbus_watch_set_handler (socket_transport->write_watch,
+                           _dbus_connection_handle_watch,
+                           transport->connection, NULL);
+
+  _dbus_watch_set_handler (socket_transport->read_watch,
+                           _dbus_connection_handle_watch,
+                           transport->connection, NULL);
+  
+  if (!_dbus_connection_add_watch_unlocked (transport->connection,
+                                            socket_transport->write_watch))
+    return FALSE;
+
+  if (!_dbus_connection_add_watch_unlocked (transport->connection,
+                                            socket_transport->read_watch))
+    {
+      _dbus_connection_remove_watch_unlocked (transport->connection,
+                                              socket_transport->write_watch);
+      return FALSE;
+    }
+
+  check_read_watch (transport);
+  check_write_watch (transport);
+
+  return TRUE;
+}
+
+/**
+ * @todo We need to have a way to wake up the select sleep if
+ * a new iteration request comes in with a flag (read/write) that
+ * we're not currently serving. Otherwise a call that just reads
+ * could block a write call forever (if there are no incoming
+ * messages).
+ */
+static  void
+socket_do_iteration (DBusTransport *transport,
+                   unsigned int   flags,
+                   int            timeout_milliseconds)
+{
+  DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+  DBusPollFD poll_fd;
+  int poll_res;
+  int poll_timeout;
+
+  _dbus_verbose (" iteration flags = %s%s timeout = %d read_watch = %p write_watch = %p fd = %d\n",
+                 flags & DBUS_ITERATION_DO_READING ? "read" : "",
+                 flags & DBUS_ITERATION_DO_WRITING ? "write" : "",
+                 timeout_milliseconds,
+                 socket_transport->read_watch,
+                 socket_transport->write_watch,
+                 socket_transport->fd);
+  
+  /* the passed in DO_READING/DO_WRITING flags indicate whether to
+   * read/write messages, but regardless of those we may need to block
+   * for reading/writing to do auth.  But if we do reading for auth,
+   * we don't want to read any messages yet if not given DO_READING.
+   */
+
+  poll_fd.fd = socket_transport->fd;
+  poll_fd.events = 0;
+  
+  if (_dbus_transport_get_is_authenticated (transport))
+    {
+      /* This is kind of a hack; if we have stuff to write, then try
+       * to avoid the poll. This is probably about a 5% speedup on an
+       * echo client/server.
+       *
+       * If both reading and writing were requested, we want to avoid this
+       * since it could have funky effects:
+       *   - both ends spinning waiting for the other one to read
+       *     data so they can finish writing
+       *   - prioritizing all writing ahead of reading
+       */
+      if ((flags & DBUS_ITERATION_DO_WRITING) &&
+          !(flags & (DBUS_ITERATION_DO_READING | DBUS_ITERATION_BLOCK)) &&
+          !transport->disconnected &&
+          _dbus_connection_has_messages_to_send_unlocked (transport->connection))
+        {
+          do_writing (transport);
+
+          if (transport->disconnected ||
+              !_dbus_connection_has_messages_to_send_unlocked (transport->connection))
+            goto out;
+        }
+
+      /* If we get here, we decided to do the poll() after all */
+      _dbus_assert (socket_transport->read_watch);
+      if (flags & DBUS_ITERATION_DO_READING)
+       poll_fd.events |= _DBUS_POLLIN;
+
+      _dbus_assert (socket_transport->write_watch);
+      if (flags & DBUS_ITERATION_DO_WRITING)
+        poll_fd.events |= _DBUS_POLLOUT;
+    }
+  else
+    {
+      DBusAuthState auth_state;
+      
+      auth_state = _dbus_auth_do_work (transport->auth);
+
+      if (transport->receive_credentials_pending ||
+          auth_state == DBUS_AUTH_STATE_WAITING_FOR_INPUT)
+       poll_fd.events |= _DBUS_POLLIN;
+
+      if (transport->send_credentials_pending ||
+          auth_state == DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND)
+       poll_fd.events |= _DBUS_POLLOUT;
+    }
+
+  if (poll_fd.events)
+    {
+      if (flags & DBUS_ITERATION_BLOCK)
+       poll_timeout = timeout_milliseconds;
+      else
+       poll_timeout = 0;
+
+      /* For blocking selects we drop the connection lock here
+       * to avoid blocking out connection access during a potentially
+       * indefinite blocking call. The io path is still protected
+       * by the io_path_cond condvar, so we won't reenter this.
+       */
+      if (flags & DBUS_ITERATION_BLOCK)
+        {
+          _dbus_verbose ("unlock %s pre poll\n", _DBUS_FUNCTION_NAME);
+          _dbus_connection_unlock (transport->connection);
+        }
+      
+    again:
+      poll_res = _dbus_poll (&poll_fd, 1, poll_timeout);
+
+      if (poll_res < 0 && _dbus_get_is_errno_eintr ())
+       goto again;
+
+      if (flags & DBUS_ITERATION_BLOCK)
+        {
+          _dbus_verbose ("lock %s post poll\n", _DBUS_FUNCTION_NAME);
+          _dbus_connection_lock (transport->connection);
+        }
+      
+      if (poll_res >= 0)
+        {
+          if (poll_res == 0)
+            poll_fd.revents = 0; /* some concern that posix does not guarantee this;
+                                  * valgrind flags it as an error. though it probably
+                                  * is guaranteed on linux at least.
+                                  */
+          
+          if (poll_fd.revents & _DBUS_POLLERR)
+            do_io_error (transport);
+          else
+            {
+              dbus_bool_t need_read = (poll_fd.revents & _DBUS_POLLIN) > 0;
+              dbus_bool_t need_write = (poll_fd.revents & _DBUS_POLLOUT) > 0;
+             dbus_bool_t authentication_completed;
+
+              _dbus_verbose ("in iteration, need_read=%d need_write=%d\n",
+                             need_read, need_write);
+              do_authentication (transport, need_read, need_write,
+                                &authentication_completed);
+
+             /* See comment in socket_handle_watch. */
+             if (authentication_completed)
+                goto out;
+                                 
+              if (need_read && (flags & DBUS_ITERATION_DO_READING))
+                do_reading (transport);
+              if (need_write && (flags & DBUS_ITERATION_DO_WRITING))
+                do_writing (transport);
+            }
+        }
+      else
+        {
+          _dbus_verbose ("Error from _dbus_poll(): %s\n",
+                         _dbus_strerror_from_errno ());
+        }
+    }
+
+
+ out:
+  /* We need to install the write watch only if we did not
+   * successfully write everything. Note we need to be careful that we
+   * don't call check_write_watch *before* do_writing, since it's
+   * inefficient to add the write watch, and we can avoid it most of
+   * the time since we can write immediately.
+   * 
+   * However, we MUST always call check_write_watch(); DBusConnection code
+   * relies on the fact that running an iteration will notice that
+   * messages are pending.
+   */
+  check_write_watch (transport);
+
+  _dbus_verbose (" ... leaving do_iteration()\n");
+}
+
+static void
+socket_live_messages_changed (DBusTransport *transport)
+{
+  /* See if we should look for incoming messages again */
+  check_read_watch (transport);
+}
+
+
+static dbus_bool_t
+socket_get_socket_fd (DBusTransport *transport,
+                      int           *fd_p)
+{
+  DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+  
+  *fd_p = socket_transport->fd;
+  
+  return TRUE;
+}
+
+static const DBusTransportVTable socket_vtable = {
+  socket_finalize,
+  socket_handle_watch,
+  socket_disconnect,
+  socket_connection_set,
+  socket_do_iteration,
+  socket_live_messages_changed,
+  socket_get_socket_fd
+};
+
+/**
+ * Creates a new transport for the given socket file descriptor.  The file
+ * descriptor must be nonblocking (use _dbus_set_fd_nonblocking() to
+ * make it so). This function is shared by various transports that
+ * boil down to a full duplex file descriptor.
+ *
+ * @param fd the file descriptor.
+ * @param server_guid non-#NULL if this transport is on the server side of a connection
+ * @param address the transport's address
+ * @returns the new transport, or #NULL if no memory.
+ */
+DBusTransport*
+_dbus_transport_new_for_socket (int               fd,
+                                const DBusString *server_guid,
+                                const DBusString *address)
+{
+  DBusTransportSocket *socket_transport;
+  
+  socket_transport = dbus_new0 (DBusTransportSocket, 1);
+  if (socket_transport == NULL)
+    return NULL;
+
+  if (!_dbus_string_init (&socket_transport->encoded_outgoing))
+    goto failed_0;
+
+  if (!_dbus_string_init (&socket_transport->encoded_incoming))
+    goto failed_1;
+  
+  socket_transport->write_watch = _dbus_watch_new (fd,
+                                                 DBUS_WATCH_WRITABLE,
+                                                 FALSE,
+                                                 NULL, NULL, NULL);
+  if (socket_transport->write_watch == NULL)
+    goto failed_2;
+  
+  socket_transport->read_watch = _dbus_watch_new (fd,
+                                                DBUS_WATCH_READABLE,
+                                                FALSE,
+                                                NULL, NULL, NULL);
+  if (socket_transport->read_watch == NULL)
+    goto failed_3;
+
+  if (!_dbus_transport_init_base (&socket_transport->base,
+                                  &socket_vtable,
+                                  server_guid, address))
+    goto failed_4;
+  
+  socket_transport->fd = fd;
+  socket_transport->message_bytes_written = 0;
+  
+  /* These values should probably be tunable or something. */     
+  socket_transport->max_bytes_read_per_iteration = 2048;
+  socket_transport->max_bytes_written_per_iteration = 2048;
+  
+  return (DBusTransport*) socket_transport;
+
+ failed_4:
+  _dbus_watch_unref (socket_transport->read_watch);
+ failed_3:
+  _dbus_watch_unref (socket_transport->write_watch);
+ failed_2:
+  _dbus_string_free (&socket_transport->encoded_incoming);
+ failed_1:
+  _dbus_string_free (&socket_transport->encoded_outgoing);
+ failed_0:
+  dbus_free (socket_transport);
+  return NULL;
+}
+
+/**
+ * Creates a new transport for the given hostname and port.
+ * If host is NULL, it will default to localhost
+ *
+ * @param host the host to connect to
+ * @param port the port to connect to
+ * @param family the address family to connect to
+ * @param error location to store reason for failure.
+ * @returns a new transport, or #NULL on failure.
+ */
+DBusTransport*
+_dbus_transport_new_for_tcp_socket (const char     *host,
+                                    const char     *port,
+                                    const char     *family,
+                                    DBusError      *error)
+{
+  int fd;
+  DBusTransport *transport;
+  DBusString address;
+  
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  if (!_dbus_string_init (&address))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      return NULL;
+    }
+
+  if (host == NULL)
+    host = "localhost";
+
+  if (!_dbus_string_append (&address, "tcp:"))
+    goto error;
+
+  if (!_dbus_string_append (&address, "host=") ||
+      !_dbus_string_append (&address, host))
+    goto error;
+
+  if (!_dbus_string_append (&address, ",port=") ||
+      !_dbus_string_append (&address, port))
+    goto error;
+
+  if (family != NULL &&
+      (!_dbus_string_append (&address, "family=") ||
+       !_dbus_string_append (&address, family)))
+    goto error;
+
+  fd = _dbus_connect_tcp_socket (host, port, family, error);
+  if (fd < 0)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      _dbus_string_free (&address);
+      return NULL;
+    }
+
+  _dbus_fd_set_close_on_exec (fd);
+  
+  _dbus_verbose ("Successfully connected to tcp socket %s:%s\n",
+                 host, port);
+  
+  transport = _dbus_transport_new_for_socket (fd, NULL, &address);
+  if (transport == NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      _dbus_close_socket (fd, NULL);
+      _dbus_string_free (&address);
+      fd = -1;
+    }
+
+  _dbus_string_free (&address);
+  
+  return transport;
+
+error:
+  _dbus_string_free (&address);
+  dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+  return NULL;
+}
+
+/**
+ * Opens a TCP socket transport.
+ * 
+ * @param entry the address entry to try opening as a tcp transport.
+ * @param transport_p return location for the opened transport
+ * @param error error to be set
+ * @returns result of the attempt
+ */
+DBusTransportOpenResult
+_dbus_transport_open_socket(DBusAddressEntry  *entry,
+                            DBusTransport    **transport_p,                            
+                            DBusError         *error)
+{
+  const char *method;
+  
+  method = dbus_address_entry_get_method (entry);
+  _dbus_assert (method != NULL);
+
+  if (strcmp (method, "tcp") == 0)
+    {
+      const char *host = dbus_address_entry_get_value (entry, "host");
+      const char *port = dbus_address_entry_get_value (entry, "port");
+      const char *family = dbus_address_entry_get_value (entry, "family");
+
+      if (port == NULL)
+        {
+          _dbus_set_bad_address (error, "tcp", "port", NULL);
+          return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
+        }
+
+      *transport_p = _dbus_transport_new_for_tcp_socket (host, port, family, error);
+      if (*transport_p == NULL)
+        {
+          _DBUS_ASSERT_ERROR_IS_SET (error);
+          return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+        }
+      else
+        {
+          _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+          return DBUS_TRANSPORT_OPEN_OK;
+        }
+    }
+  else
+    {
+      _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+      return DBUS_TRANSPORT_OPEN_NOT_HANDLED;
+    }
+}
+
+/** @} */
+
diff --git a/src/dbus/dbus-transport-socket.h b/src/dbus/dbus-transport-socket.h
new file mode 100644 (file)
index 0000000..8a00ab5
--- /dev/null
@@ -0,0 +1,45 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-transport-socket.h Socket subclasses of DBusTransport
+ *
+ * Copyright (C) 2002, 2006  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_TRANSPORT_SOCKET_H
+#define DBUS_TRANSPORT_SOCKET_H
+
+#include <dbus/dbus-transport-protected.h>
+
+DBUS_BEGIN_DECLS
+
+DBusTransport*          _dbus_transport_new_for_socket     (int                fd,
+                                                            const DBusString  *server_guid,
+                                                            const DBusString  *address);
+DBusTransport*          _dbus_transport_new_for_tcp_socket (const char        *host,
+                                                            const char        *port,
+                                                            const char        *family,
+                                                            DBusError         *error);
+DBusTransportOpenResult _dbus_transport_open_socket        (DBusAddressEntry  *entry,
+                                                            DBusTransport    **transport_p,
+                                                            DBusError         *error);
+
+
+
+DBUS_END_DECLS
+
+#endif /* DBUS_TRANSPORT_SOCKET_H */
diff --git a/src/dbus/dbus-transport-unix.c b/src/dbus/dbus-transport-unix.c
new file mode 100644 (file)
index 0000000..a13fde1
--- /dev/null
@@ -0,0 +1,181 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-transport-unix.c UNIX socket subclasses of DBusTransport
+ *
+ * Copyright (C) 2002, 2003, 2004  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-internals.h"
+#include "dbus-connection-internal.h"
+#include "dbus-transport-unix.h"
+#include "dbus-transport-socket.h"
+#include "dbus-transport-protected.h"
+#include "dbus-watch.h"
+#include "dbus-sysdeps-unix.h"
+
+/**
+ * @defgroup DBusTransportUnix DBusTransport implementations for UNIX
+ * @ingroup  DBusInternals
+ * @brief Implementation details of DBusTransport on UNIX
+ *
+ * @{
+ */
+
+/**
+ * Creates a new transport for the given Unix domain socket
+ * path. This creates a client-side of a transport.
+ *
+ * @todo once we add a way to escape paths in a dbus
+ * address, this function needs to do escaping.
+ *
+ * @param path the path to the domain socket.
+ * @param abstract #TRUE to use abstract socket namespace
+ * @param error address where an error can be returned.
+ * @returns a new transport, or #NULL on failure.
+ */
+DBusTransport*
+_dbus_transport_new_for_domain_socket (const char     *path,
+                                       dbus_bool_t     abstract,
+                                       DBusError      *error)
+{
+  int fd;
+  DBusTransport *transport;
+  DBusString address;
+  
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  if (!_dbus_string_init (&address))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      return NULL;
+    }
+
+  fd = -1;
+
+  if ((abstract &&
+       !_dbus_string_append (&address, "unix:abstract=")) ||
+      (!abstract &&
+       !_dbus_string_append (&address, "unix:path=")) ||
+      !_dbus_string_append (&address, path))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto failed_0;
+    }
+  
+  fd = _dbus_connect_unix_socket (path, abstract, error);
+  if (fd < 0)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      goto failed_0;
+    }
+
+  _dbus_fd_set_close_on_exec (fd);
+  
+  _dbus_verbose ("Successfully connected to unix socket %s\n",
+                 path);
+
+  transport = _dbus_transport_new_for_socket (fd, NULL, &address);
+  if (transport == NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto failed_1;
+    }
+  
+  _dbus_string_free (&address);
+  
+  return transport;
+
+ failed_1:
+  _dbus_close_socket (fd, NULL);
+ failed_0:
+  _dbus_string_free (&address);
+  return NULL;
+}
+
+/**
+ * Opens platform specific transport types.
+ * 
+ * @param entry the address entry to try opening
+ * @param transport_p return location for the opened transport
+ * @param error error to be set
+ * @returns result of the attempt
+ */
+DBusTransportOpenResult
+_dbus_transport_open_platform_specific (DBusAddressEntry  *entry,
+                                        DBusTransport    **transport_p,
+                                        DBusError         *error)
+{
+  const char *method;
+  
+  method = dbus_address_entry_get_method (entry);
+  _dbus_assert (method != NULL);
+
+  if (strcmp (method, "unix") == 0)
+    {
+      const char *path = dbus_address_entry_get_value (entry, "path");
+      const char *tmpdir = dbus_address_entry_get_value (entry, "tmpdir");
+      const char *abstract = dbus_address_entry_get_value (entry, "abstract");
+          
+      if (tmpdir != NULL)
+        {
+          _dbus_set_bad_address (error, NULL, NULL,
+                                 "cannot use the \"tmpdir\" option for an address to connect to, only in an address to listen on");
+          return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
+        }
+          
+      if (path == NULL && abstract == NULL)
+        {
+          _dbus_set_bad_address (error, "unix",
+                                 "path or abstract",
+                                 NULL);
+          return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
+        }
+
+      if (path != NULL && abstract != NULL)
+        {
+          _dbus_set_bad_address (error, NULL, NULL,
+                                 "can't specify both \"path\" and \"abstract\" options in an address");
+          return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
+        }
+
+      if (path)
+        *transport_p = _dbus_transport_new_for_domain_socket (path, FALSE,
+                                                           error);
+      else
+        *transport_p = _dbus_transport_new_for_domain_socket (abstract, TRUE,
+                                                           error);
+      if (*transport_p == NULL)
+        {
+          _DBUS_ASSERT_ERROR_IS_SET (error);
+          return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+        }
+      else
+        {
+          _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+          return DBUS_TRANSPORT_OPEN_OK;
+        }      
+    }
+  else
+    {
+      _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+      return DBUS_TRANSPORT_OPEN_NOT_HANDLED;
+    }
+}
+
+/** @} */
diff --git a/src/dbus/dbus-transport-unix.h b/src/dbus/dbus-transport-unix.h
new file mode 100644 (file)
index 0000000..3807ed2
--- /dev/null
@@ -0,0 +1,37 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-transport-unix.h UNIX socket subclasses of DBusTransport
+ *
+ * Copyright (C) 2002  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_TRANSPORT_UNIX_H
+#define DBUS_TRANSPORT_UNIX_H
+
+#include <dbus/dbus-transport.h>
+
+DBUS_BEGIN_DECLS
+
+DBusTransport* _dbus_transport_new_for_domain_socket (const char       *path,
+                                                      dbus_bool_t       abstract,
+                                                      DBusError        *error);
+
+
+DBUS_END_DECLS
+
+#endif /* DBUS_TRANSPORT_UNIX_H */
diff --git a/src/dbus/dbus-transport.c b/src/dbus/dbus-transport.c
new file mode 100644 (file)
index 0000000..35b7027
--- /dev/null
@@ -0,0 +1,1411 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-transport.c DBusTransport object (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-transport-protected.h"
+#include "dbus-transport-unix.h"
+#include "dbus-transport-socket.h"
+#include "dbus-connection-internal.h"
+#include "dbus-watch.h"
+#include "dbus-auth.h"
+#include "dbus-address.h"
+#include "dbus-credentials.h"
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-server-debug-pipe.h"
+#endif
+
+/**
+ * @defgroup DBusTransport DBusTransport object
+ * @ingroup  DBusInternals
+ * @brief "Backend" for a DBusConnection.
+ *
+ * Types and functions related to DBusTransport.  A transport is an
+ * abstraction that can send and receive data via various kinds of
+ * network connections or other IPC mechanisms.
+ * 
+ * @{
+ */
+
+/**
+ * @typedef DBusTransport
+ *
+ * Opaque object representing a way message stream.
+ * DBusTransport abstracts various kinds of actual
+ * transport mechanism, such as different network protocols,
+ * or encryption schemes.
+ */
+
+static void
+live_messages_size_notify (DBusCounter *counter,
+                           void        *user_data)
+{
+  DBusTransport *transport = user_data;
+
+  _dbus_transport_ref (transport);
+
+#if 0
+  _dbus_verbose ("Counter value is now %d\n",
+                 (int) _dbus_counter_get_value (counter));
+#endif
+  
+  /* disable or re-enable the read watch for the transport if
+   * required.
+   */
+  if (transport->vtable->live_messages_changed)
+    (* transport->vtable->live_messages_changed) (transport);
+
+  _dbus_transport_unref (transport);
+}
+
+/**
+ * Initializes the base class members of DBusTransport.  Chained up to
+ * by subclasses in their constructor.  The server GUID is the
+ * globally unique ID for the server creating this connection
+ * and will be #NULL for the client side of a connection. The GUID
+ * is in hex format.
+ *
+ * @param transport the transport being created.
+ * @param vtable the subclass vtable.
+ * @param server_guid non-#NULL if this transport is on the server side of a connection
+ * @param address the address of the transport
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_transport_init_base (DBusTransport             *transport,
+                           const DBusTransportVTable *vtable,
+                           const DBusString          *server_guid,
+                           const DBusString          *address)
+{
+  DBusMessageLoader *loader;
+  DBusAuth *auth;
+  DBusCounter *counter;
+  char *address_copy;
+  DBusCredentials *creds;
+  
+  loader = _dbus_message_loader_new ();
+  if (loader == NULL)
+    return FALSE;
+  
+  if (server_guid)
+    auth = _dbus_auth_server_new (server_guid);
+  else
+    auth = _dbus_auth_client_new ();
+  if (auth == NULL)
+    {
+      _dbus_message_loader_unref (loader);
+      return FALSE;
+    }
+
+  counter = _dbus_counter_new ();
+  if (counter == NULL)
+    {
+      _dbus_auth_unref (auth);
+      _dbus_message_loader_unref (loader);
+      return FALSE;
+    }  
+
+  creds = _dbus_credentials_new ();
+  if (creds == NULL)
+    {
+      _dbus_counter_unref (counter);
+      _dbus_auth_unref (auth);
+      _dbus_message_loader_unref (loader);
+      return FALSE;
+    }
+  
+  if (server_guid)
+    {
+      _dbus_assert (address == NULL);
+      address_copy = NULL;
+    }
+  else
+    {
+      _dbus_assert (address != NULL);
+
+      if (!_dbus_string_copy_data (address, &address_copy))
+        {
+          _dbus_credentials_unref (creds);
+          _dbus_counter_unref (counter);
+          _dbus_auth_unref (auth);
+          _dbus_message_loader_unref (loader);
+          return FALSE;
+        }
+    }
+  
+  transport->refcount = 1;
+  transport->vtable = vtable;
+  transport->loader = loader;
+  transport->auth = auth;
+  transport->live_messages_size = counter;
+  transport->authenticated = FALSE;
+  transport->disconnected = FALSE;
+  transport->is_server = (server_guid != NULL);
+  transport->send_credentials_pending = !transport->is_server;
+  transport->receive_credentials_pending = transport->is_server;
+  transport->address = address_copy;
+  
+  transport->unix_user_function = NULL;
+  transport->unix_user_data = NULL;
+  transport->free_unix_user_data = NULL;
+
+  transport->windows_user_function = NULL;
+  transport->windows_user_data = NULL;
+  transport->free_windows_user_data = NULL;
+  
+  transport->expected_guid = NULL;
+  
+  /* Try to default to something that won't totally hose the system,
+   * but doesn't impose too much of a limitation.
+   */
+  transport->max_live_messages_size = _DBUS_ONE_MEGABYTE * 63;
+
+  /* credentials read from socket if any */
+  transport->credentials = creds;
+  
+  _dbus_counter_set_notify (transport->live_messages_size,
+                            transport->max_live_messages_size,
+                            live_messages_size_notify,
+                            transport);
+
+  if (transport->address)
+    _dbus_verbose ("Initialized transport on address %s\n", transport->address);
+  
+  return TRUE;
+}
+
+/**
+ * Finalizes base class members of DBusTransport.
+ * Chained up to from subclass finalizers.
+ *
+ * @param transport the transport.
+ */
+void
+_dbus_transport_finalize_base (DBusTransport *transport)
+{
+  if (!transport->disconnected)
+    _dbus_transport_disconnect (transport);
+
+  if (transport->free_unix_user_data != NULL)
+    (* transport->free_unix_user_data) (transport->unix_user_data);
+
+  if (transport->free_windows_user_data != NULL)
+    (* transport->free_windows_user_data) (transport->windows_user_data);
+  
+  _dbus_message_loader_unref (transport->loader);
+  _dbus_auth_unref (transport->auth);
+  _dbus_counter_set_notify (transport->live_messages_size,
+                            0, NULL, NULL);
+  _dbus_counter_unref (transport->live_messages_size);
+  dbus_free (transport->address);
+  dbus_free (transport->expected_guid);
+  if (transport->credentials)
+    _dbus_credentials_unref (transport->credentials);
+}
+
+
+/**
+ * Verifies if a given D-Bus address is a valid address
+ * by attempting to connect to it. If it is, returns the
+ * opened DBusTransport object. If it isn't, returns #NULL
+ * and sets @p error.
+ *
+ * @param error address where an error can be returned.
+ * @returns a new transport, or #NULL on failure.
+ */
+static DBusTransport*
+check_address (const char *address, DBusError *error)
+{
+  DBusAddressEntry **entries;
+  DBusTransport *transport = NULL;
+  int len, i;
+
+  _dbus_assert (address != NULL);
+  _dbus_assert (*address != '\0');
+
+  if (!dbus_parse_address (address, &entries, &len, error))
+    return NULL;              /* not a valid address */
+
+  for (i = 0; i < len; i++)
+    {
+      transport = _dbus_transport_open (entries[i], error);
+      if (transport != NULL)
+        break;
+    }
+
+  dbus_address_entries_free (entries);
+  return transport;
+}
+
+/**
+ * Creates a new transport for the "autostart" method.
+ * This creates a client-side of a transport.
+ *
+ * @param error address where an error can be returned.
+ * @returns a new transport, or #NULL on failure.
+ */
+static DBusTransport*
+_dbus_transport_new_for_autolaunch (DBusError      *error)
+{
+  DBusString address;
+  DBusTransport *result = NULL;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  if (!_dbus_string_init (&address))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      return NULL;
+    }
+
+  if (!_dbus_get_autolaunch_address (&address, error))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      goto out;
+    }
+
+  result = check_address (_dbus_string_get_const_data (&address), error);
+  if (result == NULL)
+    _DBUS_ASSERT_ERROR_IS_SET (error);
+  else
+    _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ out:
+  _dbus_string_free (&address);
+  return result;
+}
+
+static DBusTransportOpenResult
+_dbus_transport_open_autolaunch (DBusAddressEntry  *entry,
+                                 DBusTransport    **transport_p,
+                                 DBusError         *error)
+{
+  const char *method;
+  
+  method = dbus_address_entry_get_method (entry);
+  _dbus_assert (method != NULL);
+
+  if (strcmp (method, "autolaunch") == 0)
+    {
+      *transport_p = _dbus_transport_new_for_autolaunch (error);
+
+      if (*transport_p == NULL)
+        {
+          _DBUS_ASSERT_ERROR_IS_SET (error);
+          return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+        }
+      else
+        {
+          _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+          return DBUS_TRANSPORT_OPEN_OK;
+        }      
+    }
+  else
+    {
+      _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+      return DBUS_TRANSPORT_OPEN_NOT_HANDLED;
+    }
+}
+
+static const struct {
+  DBusTransportOpenResult (* func) (DBusAddressEntry *entry,
+                                    DBusTransport   **transport_p,
+                                    DBusError        *error);
+} open_funcs[] = {
+  { _dbus_transport_open_socket },
+  { _dbus_transport_open_platform_specific },
+  { _dbus_transport_open_autolaunch }
+#ifdef DBUS_BUILD_TESTS
+  , { _dbus_transport_open_debug_pipe }
+#endif
+};
+
+/**
+ * Try to open a new transport for the given address entry.  (This
+ * opens a client-side-of-the-connection transport.)
+ * 
+ * @param entry the address entry
+ * @param error location to store reason for failure.
+ * @returns new transport of #NULL on failure.
+ */
+DBusTransport*
+_dbus_transport_open (DBusAddressEntry *entry,
+                      DBusError        *error)
+{
+  DBusTransport *transport;
+  const char *expected_guid_orig;
+  char *expected_guid;
+  int i;
+  DBusError tmp_error = DBUS_ERROR_INIT;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  transport = NULL;
+  expected_guid_orig = dbus_address_entry_get_value (entry, "guid");
+  expected_guid = _dbus_strdup (expected_guid_orig);
+
+  if (expected_guid_orig != NULL && expected_guid == NULL)
+    {
+      _DBUS_SET_OOM (error);
+      return NULL;
+    }
+
+  for (i = 0; i < (int) _DBUS_N_ELEMENTS (open_funcs); ++i)
+    {
+      DBusTransportOpenResult result;
+
+      _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
+      result = (* open_funcs[i].func) (entry, &transport, &tmp_error);
+
+      switch (result)
+        {
+        case DBUS_TRANSPORT_OPEN_OK:
+          _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
+          goto out;
+          break;
+        case DBUS_TRANSPORT_OPEN_NOT_HANDLED:
+          _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
+          /* keep going through the loop of open funcs */
+          break;
+        case DBUS_TRANSPORT_OPEN_BAD_ADDRESS:
+          _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
+          goto out;
+          break;
+        case DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT:
+          _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
+          goto out;
+          break;
+        }
+    }
+
+ out:
+  
+  if (transport == NULL)
+    {
+      if (!dbus_error_is_set (&tmp_error))
+        _dbus_set_bad_address (&tmp_error,
+                               NULL, NULL,
+                               "Unknown address type (examples of valid types are \"tcp\" and on UNIX \"unix\")");
+      
+      _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
+      dbus_move_error(&tmp_error, error);
+      dbus_free (expected_guid);
+    }
+  else
+    {
+      _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
+
+      /* In the case of autostart the initial guid is NULL
+       * and the autostart transport recursively calls
+       * _dbus_open_transport wich returns a transport
+       * with a guid.  That guid is the definitive one.
+       *
+       * FIXME: if more transports are added they may have
+       * an effect on the expected_guid semantics (i.e. 
+       * expected_guid and transport->expected_guid may
+       * both have values).  This is very unlikely though
+       * we should either throw asserts here for those 
+       * corner cases or refactor the code so it is 
+       * clearer on what is expected and what is not
+       */
+      if(expected_guid)
+        transport->expected_guid = expected_guid;
+    }
+
+  return transport;
+}
+
+/**
+ * Increments the reference count for the transport.
+ *
+ * @param transport the transport.
+ * @returns the transport.
+ */
+DBusTransport *
+_dbus_transport_ref (DBusTransport *transport)
+{
+  _dbus_assert (transport->refcount > 0);
+  
+  transport->refcount += 1;
+
+  return transport;
+}
+
+/**
+ * Decrements the reference count for the transport.
+ * Disconnects and finalizes the transport if
+ * the reference count reaches zero.
+ *
+ * @param transport the transport.
+ */
+void
+_dbus_transport_unref (DBusTransport *transport)
+{
+  _dbus_assert (transport != NULL);
+  _dbus_assert (transport->refcount > 0);
+  
+  transport->refcount -= 1;
+  if (transport->refcount == 0)
+    {
+      _dbus_verbose ("%s: finalizing\n", _DBUS_FUNCTION_NAME);
+      
+      _dbus_assert (transport->vtable->finalize != NULL);
+      
+      (* transport->vtable->finalize) (transport);
+    }
+}
+
+/**
+ * Closes our end of the connection to a remote application. Further
+ * attempts to use this transport will fail. Only the first call to
+ * _dbus_transport_disconnect() will have an effect.
+ *
+ * @param transport the transport.
+ * 
+ */
+void
+_dbus_transport_disconnect (DBusTransport *transport)
+{
+  _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME);
+  
+  _dbus_assert (transport->vtable->disconnect != NULL);
+  
+  if (transport->disconnected)
+    return;
+
+  (* transport->vtable->disconnect) (transport);
+  
+  transport->disconnected = TRUE;
+
+  _dbus_verbose ("%s end\n", _DBUS_FUNCTION_NAME);
+}
+
+/**
+ * Returns #TRUE if the transport has not been disconnected.
+ * Disconnection can result from _dbus_transport_disconnect()
+ * or because the server drops its end of the connection.
+ *
+ * @param transport the transport.
+ * @returns whether we're connected
+ */
+dbus_bool_t
+_dbus_transport_get_is_connected (DBusTransport *transport)
+{
+  return !transport->disconnected;
+}
+
+static dbus_bool_t
+auth_via_unix_user_function (DBusTransport *transport)
+{
+  DBusCredentials *auth_identity;
+  dbus_bool_t allow;
+  DBusConnection *connection;
+  DBusAllowUnixUserFunction unix_user_function;
+  void *unix_user_data;
+  dbus_uid_t uid;
+
+  /* Dropping the lock here probably isn't that safe. */
+  
+  auth_identity = _dbus_auth_get_identity (transport->auth);
+  _dbus_assert (auth_identity != NULL);
+
+  connection = transport->connection;
+  unix_user_function = transport->unix_user_function;
+  unix_user_data = transport->unix_user_data;
+  uid = _dbus_credentials_get_unix_uid (auth_identity);
+              
+  _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME);
+  _dbus_connection_unlock (connection);
+
+  allow = (* unix_user_function) (connection,
+                                  uid,
+                                  unix_user_data);
+              
+  _dbus_verbose ("lock %s post unix user function\n", _DBUS_FUNCTION_NAME);
+  _dbus_connection_lock (connection);
+
+  if (allow)
+    {
+      _dbus_verbose ("Client UID "DBUS_UID_FORMAT" authorized\n", uid);
+    }
+  else
+    {
+      _dbus_verbose ("Client UID "DBUS_UID_FORMAT
+                     " was rejected, disconnecting\n",
+                     _dbus_credentials_get_unix_uid (auth_identity));
+      _dbus_transport_disconnect (transport);
+    }
+
+  return allow;
+}
+
+static dbus_bool_t
+auth_via_windows_user_function (DBusTransport *transport)
+{
+  DBusCredentials *auth_identity;  
+  dbus_bool_t allow;
+  DBusConnection *connection;
+  DBusAllowWindowsUserFunction windows_user_function;
+  void *windows_user_data;
+  char *windows_sid;
+
+  /* Dropping the lock here probably isn't that safe. */
+  
+  auth_identity = _dbus_auth_get_identity (transport->auth);
+  _dbus_assert (auth_identity != NULL);
+
+  connection = transport->connection;
+  windows_user_function = transport->windows_user_function;
+  windows_user_data = transport->unix_user_data;
+  windows_sid = _dbus_strdup (_dbus_credentials_get_windows_sid (auth_identity));
+
+  if (windows_sid == NULL)
+    {
+      /* OOM */
+      return FALSE;
+    }
+                
+  _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME);
+  _dbus_connection_unlock (connection);
+
+  allow = (* windows_user_function) (connection,
+                                     windows_sid,
+                                     windows_user_data);
+              
+  _dbus_verbose ("lock %s post windows user function\n", _DBUS_FUNCTION_NAME);
+  _dbus_connection_lock (connection);
+
+  if (allow)
+    {
+      _dbus_verbose ("Client SID '%s' authorized\n", windows_sid);
+    }
+  else
+    {
+      _dbus_verbose ("Client SID '%s' was rejected, disconnecting\n",
+                     _dbus_credentials_get_windows_sid (auth_identity));
+      _dbus_transport_disconnect (transport);
+    }
+
+  return allow;
+}
+
+static dbus_bool_t
+auth_via_default_rules (DBusTransport *transport)
+{
+  DBusCredentials *auth_identity;
+  DBusCredentials *our_identity;
+  dbus_bool_t allow;
+  
+  auth_identity = _dbus_auth_get_identity (transport->auth);
+  _dbus_assert (auth_identity != NULL);
+
+  /* By default, connection is allowed if the client is 1) root or 2)
+   * has the same UID as us or 3) anonymous is allowed.
+   */
+  
+  our_identity = _dbus_credentials_new_from_current_process ();
+  if (our_identity == NULL)
+    {
+      /* OOM */
+      return FALSE;
+    }
+              
+  if (transport->allow_anonymous ||
+      _dbus_credentials_get_unix_uid (auth_identity) == 0 ||
+      _dbus_credentials_same_user (our_identity,
+                                   auth_identity))
+    {
+      if (_dbus_credentials_include(our_identity,DBUS_CREDENTIAL_WINDOWS_SID))
+          _dbus_verbose ("Client authorized as SID '%s'"
+                         "matching our SID '%s'\n",
+                         _dbus_credentials_get_windows_sid(auth_identity),
+                         _dbus_credentials_get_windows_sid(our_identity));
+      else
+          _dbus_verbose ("Client authorized as UID "DBUS_UID_FORMAT
+                         " matching our UID "DBUS_UID_FORMAT"\n",
+                         _dbus_credentials_get_unix_uid(auth_identity),
+                         _dbus_credentials_get_unix_uid(our_identity));
+      /* We have authenticated! */
+      allow = TRUE;
+    }
+  else
+    {
+      if (_dbus_credentials_include(our_identity,DBUS_CREDENTIAL_WINDOWS_SID))
+          _dbus_verbose ("Client authorized as SID '%s'"
+                         " but our SID is '%s', disconnecting\n",
+                         _dbus_credentials_get_windows_sid(our_identity),
+                         _dbus_credentials_get_windows_sid(our_identity));
+      else
+          _dbus_verbose ("Client authorized as UID "DBUS_UID_FORMAT
+                         " but our UID is "DBUS_UID_FORMAT", disconnecting\n",
+                         _dbus_credentials_get_unix_uid(our_identity),
+                         _dbus_credentials_get_unix_uid(our_identity));
+      _dbus_transport_disconnect (transport);
+      allow = FALSE;
+    }  
+
+  _dbus_credentials_unref (our_identity);
+  
+  return allow;
+}
+
+
+/**
+ * Returns #TRUE if we have been authenticated.  Will return #TRUE
+ * even if the transport is disconnected.
+ *
+ * @todo we drop connection->mutex when calling the unix_user_function,
+ * and windows_user_function, which may not be safe really.
+ *
+ * @param transport the transport
+ * @returns whether we're authenticated
+ */
+dbus_bool_t
+_dbus_transport_get_is_authenticated (DBusTransport *transport)
+{  
+  if (transport->authenticated)
+    return TRUE;
+  else
+    {
+      dbus_bool_t maybe_authenticated;
+      
+      if (transport->disconnected)
+        return FALSE;
+
+      /* paranoia ref since we call user callbacks sometimes */
+      _dbus_connection_ref_unlocked (transport->connection);
+      
+      maybe_authenticated =
+        (!(transport->send_credentials_pending ||
+           transport->receive_credentials_pending));
+
+      if (maybe_authenticated)
+        {
+          switch (_dbus_auth_do_work (transport->auth))
+            {
+            case DBUS_AUTH_STATE_AUTHENTICATED:
+              /* leave as maybe_authenticated */
+              break;
+            default:
+              maybe_authenticated = FALSE;
+            }
+        }
+
+      /* If we're the client, verify the GUID
+       */
+      if (maybe_authenticated && !transport->is_server)
+        {
+          const char *server_guid;
+
+          server_guid = _dbus_auth_get_guid_from_server (transport->auth);
+          _dbus_assert (server_guid != NULL);
+
+          if (transport->expected_guid &&
+              strcmp (transport->expected_guid, server_guid) != 0)
+            {
+              _dbus_verbose ("Client expected GUID '%s' and we got '%s' from the server\n",
+                             transport->expected_guid, server_guid);
+              _dbus_transport_disconnect (transport);
+              _dbus_connection_unref_unlocked (transport->connection);
+              return FALSE;
+            }
+
+          if (transport->expected_guid == NULL)
+            {
+              transport->expected_guid = _dbus_strdup (server_guid);
+
+              if (transport->expected_guid == NULL)
+                {
+                  _dbus_verbose ("No memory to complete auth in %s\n", _DBUS_FUNCTION_NAME);
+                  return FALSE;
+                }
+            }
+        }
+
+      /* If we're the server, see if we want to allow this identity to proceed.
+       */
+      if (maybe_authenticated && transport->is_server)
+        {
+          dbus_bool_t allow;
+          DBusCredentials *auth_identity;
+          
+          auth_identity = _dbus_auth_get_identity (transport->auth);
+          _dbus_assert (auth_identity != NULL);
+          
+          /* If we have an auth'd user and a user function, delegate
+           * deciding whether auth credentials are good enough to the
+           * app; otherwise, use our default decision process.
+           */
+          if (transport->unix_user_function != NULL &&
+              _dbus_credentials_include (auth_identity, DBUS_CREDENTIAL_UNIX_USER_ID))
+            {
+              allow = auth_via_unix_user_function (transport);
+            }
+          else if (transport->windows_user_function != NULL &&
+                   _dbus_credentials_include (auth_identity, DBUS_CREDENTIAL_WINDOWS_SID))
+            {
+              allow = auth_via_windows_user_function (transport);
+            }      
+          else
+            {
+              allow = auth_via_default_rules (transport);
+            }
+          
+          if (!allow)
+            maybe_authenticated = FALSE;
+        }
+
+      transport->authenticated = maybe_authenticated;
+
+      _dbus_connection_unref_unlocked (transport->connection);
+      return maybe_authenticated;
+    }
+}
+
+/**
+ * See dbus_connection_get_is_anonymous().
+ *
+ * @param transport the transport
+ * @returns #TRUE if not authenticated or authenticated as anonymous
+ */
+dbus_bool_t
+_dbus_transport_get_is_anonymous (DBusTransport *transport)
+{
+  DBusCredentials *auth_identity;
+  
+  if (!transport->authenticated)
+    return TRUE;
+  
+  auth_identity = _dbus_auth_get_identity (transport->auth);
+
+  if (_dbus_credentials_are_anonymous (auth_identity))
+    return TRUE;
+  else
+    return FALSE;
+}
+
+/**
+ * Gets the address of a transport. It will be
+ * #NULL for a server-side transport.
+ *
+ * @param transport the transport
+ * @returns transport's address
+ */
+const char*
+_dbus_transport_get_address (DBusTransport *transport)
+{
+  return transport->address;
+}
+
+/**
+ * Gets the id of the server we are connected to (see
+ * dbus_server_get_id()). Only works on client side.
+ *
+ * @param transport the transport
+ * @returns transport's server's id or #NULL if we are the server side
+ */
+const char*
+_dbus_transport_get_server_id (DBusTransport *transport)
+{
+  if (transport->is_server)
+    return NULL;
+  else
+    return transport->expected_guid;
+}
+
+/**
+ * Handles a watch by reading data, writing data, or disconnecting
+ * the transport, as appropriate for the given condition.
+ *
+ * @param transport the transport.
+ * @param watch the watch.
+ * @param condition the current state of the watched file descriptor.
+ * @returns #FALSE if not enough memory to fully handle the watch
+ */
+dbus_bool_t
+_dbus_transport_handle_watch (DBusTransport           *transport,
+                              DBusWatch               *watch,
+                              unsigned int             condition)
+{
+  dbus_bool_t retval;
+  
+  _dbus_assert (transport->vtable->handle_watch != NULL);
+
+  if (transport->disconnected)
+    return TRUE;
+
+  if (dbus_watch_get_socket (watch) < 0)
+    {
+      _dbus_warn_check_failed ("Tried to handle an invalidated watch; this watch should have been removed\n");
+      return TRUE;
+    }
+  
+  _dbus_watch_sanitize_condition (watch, &condition);
+
+  _dbus_transport_ref (transport);
+  _dbus_watch_ref (watch);
+  retval = (* transport->vtable->handle_watch) (transport, watch, condition);
+  _dbus_watch_unref (watch);
+  _dbus_transport_unref (transport);
+
+  return retval;
+}
+
+/**
+ * Sets the connection using this transport. Allows the transport
+ * to add watches to the connection, queue incoming messages,
+ * and pull outgoing messages.
+ *
+ * @param transport the transport.
+ * @param connection the connection.
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_transport_set_connection (DBusTransport  *transport,
+                                DBusConnection *connection)
+{
+  _dbus_assert (transport->vtable->connection_set != NULL);
+  _dbus_assert (transport->connection == NULL);
+  
+  transport->connection = connection;
+
+  _dbus_transport_ref (transport);
+  if (!(* transport->vtable->connection_set) (transport))
+    transport->connection = NULL;
+  _dbus_transport_unref (transport);
+
+  return transport->connection != NULL;
+}
+
+/**
+ * Get the socket file descriptor, if any.
+ *
+ * @param transport the transport
+ * @param fd_p pointer to fill in with the descriptor
+ * @returns #TRUE if a descriptor was available
+ */
+dbus_bool_t
+_dbus_transport_get_socket_fd (DBusTransport *transport,
+                               int           *fd_p)
+{
+  dbus_bool_t retval;
+  
+  if (transport->vtable->get_socket_fd == NULL)
+    return FALSE;
+
+  if (transport->disconnected)
+    return FALSE;
+
+  _dbus_transport_ref (transport);
+
+  retval = (* transport->vtable->get_socket_fd) (transport,
+                                                 fd_p);
+  
+  _dbus_transport_unref (transport);
+
+  return retval;
+}
+
+/**
+ * Performs a single poll()/select() on the transport's file
+ * descriptors and then reads/writes data as appropriate,
+ * queueing incoming messages and sending outgoing messages.
+ * This is the backend for _dbus_connection_do_iteration().
+ * See _dbus_connection_do_iteration() for full details.
+ *
+ * @param transport the transport.
+ * @param flags indicates whether to read or write, and whether to block.
+ * @param timeout_milliseconds if blocking, timeout or -1 for no timeout.
+ */
+void
+_dbus_transport_do_iteration (DBusTransport  *transport,
+                              unsigned int    flags,
+                              int             timeout_milliseconds)
+{
+  _dbus_assert (transport->vtable->do_iteration != NULL);
+
+  _dbus_verbose ("Transport iteration flags 0x%x timeout %d connected = %d\n",
+                 flags, timeout_milliseconds, !transport->disconnected);
+  
+  if ((flags & (DBUS_ITERATION_DO_WRITING |
+                DBUS_ITERATION_DO_READING)) == 0)
+    return; /* Nothing to do */
+
+  if (transport->disconnected)
+    return;
+
+  _dbus_transport_ref (transport);
+  (* transport->vtable->do_iteration) (transport, flags,
+                                       timeout_milliseconds);
+  _dbus_transport_unref (transport);
+
+  _dbus_verbose ("%s end\n", _DBUS_FUNCTION_NAME);
+}
+
+static dbus_bool_t
+recover_unused_bytes (DBusTransport *transport)
+{
+  if (_dbus_auth_needs_decoding (transport->auth))
+    {
+      DBusString plaintext;
+      const DBusString *encoded;
+      DBusString *buffer;
+      int orig_len;
+      
+      if (!_dbus_string_init (&plaintext))
+        goto nomem;
+      
+      _dbus_auth_get_unused_bytes (transport->auth,
+                                   &encoded);
+
+      if (!_dbus_auth_decode_data (transport->auth,
+                                   encoded, &plaintext))
+        {
+          _dbus_string_free (&plaintext);
+          goto nomem;
+        }
+      
+      _dbus_message_loader_get_buffer (transport->loader,
+                                       &buffer);
+      
+      orig_len = _dbus_string_get_length (buffer);
+      
+      if (!_dbus_string_move (&plaintext, 0, buffer,
+                              orig_len))
+        {
+          _dbus_string_free (&plaintext);
+          goto nomem;
+        }
+      
+      _dbus_verbose (" %d unused bytes sent to message loader\n", 
+                     _dbus_string_get_length (buffer) -
+                     orig_len);
+      
+      _dbus_message_loader_return_buffer (transport->loader,
+                                          buffer,
+                                          _dbus_string_get_length (buffer) -
+                                          orig_len);
+
+      _dbus_auth_delete_unused_bytes (transport->auth);
+      
+      _dbus_string_free (&plaintext);
+    }
+  else
+    {
+      const DBusString *bytes;
+      DBusString *buffer;
+      int orig_len;
+      dbus_bool_t succeeded;
+
+      _dbus_message_loader_get_buffer (transport->loader,
+                                       &buffer);
+                
+      orig_len = _dbus_string_get_length (buffer);
+                
+      _dbus_auth_get_unused_bytes (transport->auth,
+                                   &bytes);
+
+      succeeded = TRUE;
+      if (!_dbus_string_copy (bytes, 0, buffer, _dbus_string_get_length (buffer)))
+        succeeded = FALSE;
+      
+      _dbus_verbose (" %d unused bytes sent to message loader\n", 
+                     _dbus_string_get_length (buffer) -
+                     orig_len);
+      
+      _dbus_message_loader_return_buffer (transport->loader,
+                                          buffer,
+                                          _dbus_string_get_length (buffer) -
+                                          orig_len);
+
+      if (succeeded)
+        _dbus_auth_delete_unused_bytes (transport->auth);
+      else
+        goto nomem;
+    }
+
+  return TRUE;
+
+ nomem:
+  _dbus_verbose ("Not enough memory to transfer unused bytes from auth conversation\n");
+  return FALSE;
+}
+
+/**
+ * Reports our current dispatch status (whether there's buffered
+ * data to be queued as messages, or not, or we need memory).
+ *
+ * @param transport the transport
+ * @returns current status
+ */
+DBusDispatchStatus
+_dbus_transport_get_dispatch_status (DBusTransport *transport)
+{
+  if (_dbus_counter_get_value (transport->live_messages_size) >= transport->max_live_messages_size)
+    return DBUS_DISPATCH_COMPLETE; /* complete for now */
+
+  if (!_dbus_transport_get_is_authenticated (transport))
+    {
+      if (_dbus_auth_do_work (transport->auth) ==
+          DBUS_AUTH_STATE_WAITING_FOR_MEMORY)
+        return DBUS_DISPATCH_NEED_MEMORY;
+      else if (!_dbus_transport_get_is_authenticated (transport))
+        return DBUS_DISPATCH_COMPLETE;
+    }
+
+  if (!transport->unused_bytes_recovered &&
+      !recover_unused_bytes (transport))
+    return DBUS_DISPATCH_NEED_MEMORY;
+
+  transport->unused_bytes_recovered = TRUE;
+  
+  if (!_dbus_message_loader_queue_messages (transport->loader))
+    return DBUS_DISPATCH_NEED_MEMORY;
+
+  if (_dbus_message_loader_peek_message (transport->loader) != NULL)
+    return DBUS_DISPATCH_DATA_REMAINS;
+  else
+    return DBUS_DISPATCH_COMPLETE;
+}
+
+/**
+ * Processes data we've read while handling a watch, potentially
+ * converting some of it to messages and queueing those messages on
+ * the connection.
+ *
+ * @param transport the transport
+ * @returns #TRUE if we had enough memory to queue all messages
+ */
+dbus_bool_t
+_dbus_transport_queue_messages (DBusTransport *transport)
+{
+  DBusDispatchStatus status;
+
+#if 0
+  _dbus_verbose ("_dbus_transport_queue_messages()\n");
+#endif
+  
+  /* Queue any messages */
+  while ((status = _dbus_transport_get_dispatch_status (transport)) == DBUS_DISPATCH_DATA_REMAINS)
+    {
+      DBusMessage *message;
+      DBusList *link;
+
+      link = _dbus_message_loader_pop_message_link (transport->loader);
+      _dbus_assert (link != NULL);
+      
+      message = link->data;
+      
+      _dbus_verbose ("queueing received message %p\n", message);
+
+      if (!_dbus_message_add_size_counter (message, transport->live_messages_size))
+        {
+          _dbus_message_loader_putback_message_link (transport->loader,
+                                                     link);
+          status = DBUS_DISPATCH_NEED_MEMORY;
+          break;
+        }
+      else
+        {
+          /* pass ownership of link and message ref to connection */
+          _dbus_connection_queue_received_message_link (transport->connection,
+                                                        link);
+        }
+    }
+
+  if (_dbus_message_loader_get_is_corrupted (transport->loader))
+    {
+      _dbus_verbose ("Corrupted message stream, disconnecting\n");
+      _dbus_transport_disconnect (transport);
+    }
+
+  return status != DBUS_DISPATCH_NEED_MEMORY;
+}
+
+/**
+ * See dbus_connection_set_max_message_size().
+ *
+ * @param transport the transport
+ * @param size the max size of a single message
+ */
+void
+_dbus_transport_set_max_message_size (DBusTransport  *transport,
+                                      long            size)
+{
+  _dbus_message_loader_set_max_message_size (transport->loader, size);
+}
+
+/**
+ * See dbus_connection_get_max_message_size().
+ *
+ * @param transport the transport
+ * @returns max message size
+ */
+long
+_dbus_transport_get_max_message_size (DBusTransport  *transport)
+{
+  return _dbus_message_loader_get_max_message_size (transport->loader);
+}
+
+/**
+ * See dbus_connection_set_max_received_size().
+ *
+ * @param transport the transport
+ * @param size the max size of all incoming messages
+ */
+void
+_dbus_transport_set_max_received_size (DBusTransport  *transport,
+                                       long            size)
+{
+  transport->max_live_messages_size = size;
+  _dbus_counter_set_notify (transport->live_messages_size,
+                            transport->max_live_messages_size,
+                            live_messages_size_notify,
+                            transport);
+}
+
+
+/**
+ * See dbus_connection_get_max_received_size().
+ *
+ * @param transport the transport
+ * @returns max bytes for all live messages
+ */
+long
+_dbus_transport_get_max_received_size (DBusTransport  *transport)
+{
+  return transport->max_live_messages_size;
+}
+
+/**
+ * See dbus_connection_get_unix_user().
+ *
+ * @param transport the transport
+ * @param uid return location for the user ID
+ * @returns #TRUE if uid is filled in with a valid user ID
+ */
+dbus_bool_t
+_dbus_transport_get_unix_user (DBusTransport *transport,
+                               unsigned long *uid)
+{
+  DBusCredentials *auth_identity;
+
+  *uid = _DBUS_INT32_MAX; /* better than some root or system user in
+                           * case of bugs in the caller. Caller should
+                           * never use this value on purpose, however.
+                           */
+  
+  if (!transport->authenticated)
+    return FALSE;
+  
+  auth_identity = _dbus_auth_get_identity (transport->auth);
+
+  if (_dbus_credentials_include (auth_identity,
+                                 DBUS_CREDENTIAL_UNIX_USER_ID))
+    {
+      *uid = _dbus_credentials_get_unix_uid (auth_identity);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+/**
+ * See dbus_connection_get_unix_process_id().
+ *
+ * @param transport the transport
+ * @param pid return location for the process ID
+ * @returns #TRUE if uid is filled in with a valid process ID
+ */
+dbus_bool_t
+_dbus_transport_get_unix_process_id (DBusTransport *transport,
+                                    unsigned long *pid)
+{
+  DBusCredentials *auth_identity;
+
+  *pid = DBUS_PID_UNSET; /* Caller should never use this value on purpose,
+                         * but we set it to a safe number, INT_MAX,
+                         * just to root out possible bugs in bad callers.
+                         */
+  
+  if (!transport->authenticated)
+    return FALSE;
+  
+  auth_identity = _dbus_auth_get_identity (transport->auth);
+
+  if (_dbus_credentials_include (auth_identity,
+                                 DBUS_CREDENTIAL_UNIX_PROCESS_ID))
+    {
+      *pid = _dbus_credentials_get_unix_pid (auth_identity);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+/**
+ * See dbus_connection_get_adt_audit_session_data().
+ *
+ * @param transport the transport
+ * @param data return location for the ADT audit data 
+ * @param data_size return length of audit data
+ * @returns #TRUE if audit data is filled in with a valid ucred
+ */
+dbus_bool_t
+_dbus_transport_get_adt_audit_session_data (DBusTransport      *transport,
+                                            void              **data,
+                                            int                *data_size)
+{
+  DBusCredentials *auth_identity;
+
+  *data = NULL;
+  *data_size = 0;
+  
+  if (!transport->authenticated)
+    return FALSE;
+  
+  auth_identity = _dbus_auth_get_identity (transport->auth);
+
+  if (_dbus_credentials_include (auth_identity,
+                                 DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID))
+    {
+      *data = (void *) _dbus_credentials_get_adt_audit_data (auth_identity);
+      *data_size = _dbus_credentials_get_adt_audit_data_size (auth_identity);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+/**
+ * See dbus_connection_set_unix_user_function().
+ *
+ * @param transport the transport
+ * @param function the predicate
+ * @param data data to pass to the predicate
+ * @param free_data_function function to free the data
+ * @param old_data the old user data to be freed
+ * @param old_free_data_function old free data function to free it with
+ */
+void
+_dbus_transport_set_unix_user_function (DBusTransport             *transport,
+                                        DBusAllowUnixUserFunction  function,
+                                        void                      *data,
+                                        DBusFreeFunction           free_data_function,
+                                        void                     **old_data,
+                                        DBusFreeFunction          *old_free_data_function)
+{  
+  *old_data = transport->unix_user_data;
+  *old_free_data_function = transport->free_unix_user_data;
+
+  transport->unix_user_function = function;
+  transport->unix_user_data = data;
+  transport->free_unix_user_data = free_data_function;
+}
+
+/**
+ * See dbus_connection_get_windows_user().
+ *
+ * @param transport the transport
+ * @param windows_sid_p return location for the user ID
+ * @returns #TRUE if user is available; the returned value may still be #NULL if no memory to copy it
+ */
+dbus_bool_t
+_dbus_transport_get_windows_user (DBusTransport              *transport,
+                                  char                      **windows_sid_p)
+{
+  DBusCredentials *auth_identity;
+
+  *windows_sid_p = NULL;
+  
+  if (!transport->authenticated)
+    return FALSE;
+  
+  auth_identity = _dbus_auth_get_identity (transport->auth);
+
+  if (_dbus_credentials_include (auth_identity,
+                                 DBUS_CREDENTIAL_WINDOWS_SID))
+    {
+      /* If no memory, we are supposed to return TRUE and set NULL */
+      *windows_sid_p = _dbus_strdup (_dbus_credentials_get_windows_sid (auth_identity));
+
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+/**
+ * See dbus_connection_set_windows_user_function().
+ *
+ * @param transport the transport
+ * @param function the predicate
+ * @param data data to pass to the predicate
+ * @param free_data_function function to free the data
+ * @param old_data the old user data to be freed
+ * @param old_free_data_function old free data function to free it with
+ */
+
+void
+_dbus_transport_set_windows_user_function (DBusTransport              *transport,
+                                           DBusAllowWindowsUserFunction   function,
+                                           void                       *data,
+                                           DBusFreeFunction            free_data_function,
+                                           void                      **old_data,
+                                           DBusFreeFunction           *old_free_data_function)
+{
+  *old_data = transport->windows_user_data;
+  *old_free_data_function = transport->free_windows_user_data;
+
+  transport->windows_user_function = function;
+  transport->windows_user_data = data;
+  transport->free_windows_user_data = free_data_function;
+}
+
+/**
+ * Sets the SASL authentication mechanisms supported by this transport.
+ *
+ * @param transport the transport
+ * @param mechanisms the #NULL-terminated array of mechanisms
+ *
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_transport_set_auth_mechanisms (DBusTransport  *transport,
+                                     const char    **mechanisms)
+{
+  return _dbus_auth_set_mechanisms (transport->auth, mechanisms);
+}
+
+/**
+ * See dbus_connection_set_allow_anonymous()
+ *
+ * @param transport the transport
+ * @param value #TRUE to allow anonymous connection
+ */
+void
+_dbus_transport_set_allow_anonymous (DBusTransport              *transport,
+                                     dbus_bool_t                 value)
+{
+  transport->allow_anonymous = value != FALSE;
+}
+
+/** @} */
diff --git a/src/dbus/dbus-transport.h b/src/dbus/dbus-transport.h
new file mode 100644 (file)
index 0000000..691763c
--- /dev/null
@@ -0,0 +1,92 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-transport.h DBusTransport object (internal to D-BUS implementation)
+ *
+ * Copyright (C) 2002, 2004  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_TRANSPORT_H
+#define DBUS_TRANSPORT_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-connection.h>
+#include <dbus/dbus-protocol.h>
+#include <dbus/dbus-address.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusTransport DBusTransport;
+
+DBusTransport*     _dbus_transport_open                   (DBusAddressEntry           *entry,
+                                                           DBusError                  *error);
+DBusTransport*     _dbus_transport_ref                    (DBusTransport              *transport);
+void               _dbus_transport_unref                  (DBusTransport              *transport);
+void               _dbus_transport_disconnect             (DBusTransport              *transport);
+dbus_bool_t        _dbus_transport_get_is_connected       (DBusTransport              *transport);
+dbus_bool_t        _dbus_transport_get_is_authenticated   (DBusTransport              *transport);
+dbus_bool_t        _dbus_transport_get_is_anonymous       (DBusTransport              *transport);
+const char*        _dbus_transport_get_address            (DBusTransport              *transport);
+const char*        _dbus_transport_get_server_id          (DBusTransport              *transport);
+dbus_bool_t        _dbus_transport_handle_watch           (DBusTransport              *transport,
+                                                           DBusWatch                  *watch,
+                                                           unsigned int                condition);
+dbus_bool_t        _dbus_transport_set_connection         (DBusTransport              *transport,
+                                                           DBusConnection             *connection);
+void               _dbus_transport_do_iteration           (DBusTransport              *transport,
+                                                           unsigned int                flags,
+                                                           int                         timeout_milliseconds);
+DBusDispatchStatus _dbus_transport_get_dispatch_status    (DBusTransport              *transport);
+dbus_bool_t        _dbus_transport_queue_messages         (DBusTransport              *transport);
+void               _dbus_transport_set_max_message_size   (DBusTransport              *transport,
+                                                           long                        size);
+long               _dbus_transport_get_max_message_size   (DBusTransport              *transport);
+void               _dbus_transport_set_max_received_size  (DBusTransport              *transport,
+                                                           long                        size);
+long               _dbus_transport_get_max_received_size  (DBusTransport              *transport);
+dbus_bool_t        _dbus_transport_get_socket_fd          (DBusTransport              *transport,
+                                                           int                        *fd_p);
+dbus_bool_t        _dbus_transport_get_unix_user          (DBusTransport              *transport,
+                                                           unsigned long              *uid);
+dbus_bool_t        _dbus_transport_get_unix_process_id     (DBusTransport              *transport,
+                                                           unsigned long              *pid);
+dbus_bool_t        _dbus_transport_get_adt_audit_session_data (DBusTransport              *transport,
+                                                               void                      **data,
+                                                               int                        *data_size);
+void               _dbus_transport_set_unix_user_function (DBusTransport              *transport,
+                                                           DBusAllowUnixUserFunction   function,
+                                                           void                       *data,
+                                                           DBusFreeFunction            free_data_function,
+                                                           void                      **old_data,
+                                                           DBusFreeFunction           *old_free_data_function);
+dbus_bool_t        _dbus_transport_get_windows_user       (DBusTransport              *transport,
+                                                           char                      **windows_sid_p);
+void               _dbus_transport_set_windows_user_function (DBusTransport              *transport,
+                                                              DBusAllowWindowsUserFunction   function,
+                                                              void                       *data,
+                                                              DBusFreeFunction            free_data_function,
+                                                              void                      **old_data,
+                                                              DBusFreeFunction           *old_free_data_function);
+dbus_bool_t        _dbus_transport_set_auth_mechanisms    (DBusTransport              *transport,
+                                                           const char                **mechanisms);
+void               _dbus_transport_set_allow_anonymous    (DBusTransport              *transport,
+                                                           dbus_bool_t                 value);
+
+
+DBUS_END_DECLS
+
+#endif /* DBUS_TRANSPORT_H */
diff --git a/src/dbus/dbus-types.h b/src/dbus/dbus-types.h
new file mode 100644 (file)
index 0000000..0a272d4
--- /dev/null
@@ -0,0 +1,139 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-types.h  types such as dbus_bool_t
+ *
+ * Copyright (C) 2002  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_TYPES_H
+#define DBUS_TYPES_H
+
+#include <stddef.h>
+#include <dbus/dbus-arch-deps.h>
+
+typedef dbus_uint32_t  dbus_unichar_t;
+/* boolean size must be fixed at 4 bytes due to wire protocol! */
+typedef dbus_uint32_t  dbus_bool_t;
+
+/* Normally docs are in .c files, but there isn't a .c file for this. */
+/**
+ * @defgroup DBusTypes Basic types
+ * @ingroup  DBus
+ * @brief dbus_bool_t, dbus_int32_t, etc.
+ *
+ * Typedefs for common primitive types.
+ *
+ * @{
+ */
+
+/**
+ * @typedef dbus_bool_t
+ *
+ * A boolean, valid values are #TRUE and #FALSE.
+ */
+
+/**
+ * @typedef dbus_uint32_t
+ *
+ * A 32-bit unsigned integer on all platforms.
+ */
+
+/**
+ * @typedef dbus_int32_t
+ *
+ * A 32-bit signed integer on all platforms.
+ */
+
+/**
+ * @typedef dbus_uint16_t
+ *
+ * A 16-bit unsigned integer on all platforms.
+ */
+
+/**
+ * @typedef dbus_int16_t
+ *
+ * A 16-bit signed integer on all platforms.
+ */
+
+
+/**
+ * @typedef dbus_uint64_t
+ *
+ * A 64-bit unsigned integer on all platforms that support it.
+ * If supported, #DBUS_HAVE_INT64 will be defined.
+ *
+ * C99 requires a 64-bit type and most likely all interesting
+ * compilers support one. GLib for example flat-out requires
+ * a 64-bit type.
+ *
+ * You probably want to just assume #DBUS_HAVE_INT64 is always defined.
+ */
+
+/**
+ * @typedef dbus_int64_t
+ *
+ * A 64-bit signed integer on all platforms that support it.
+ * If supported, #DBUS_HAVE_INT64 will be defined.
+ *
+ * C99 requires a 64-bit type and most likely all interesting
+ * compilers support one. GLib for example flat-out requires
+ * a 64-bit type.
+ * 
+ * You probably want to just assume #DBUS_HAVE_INT64 is always defined.
+ */
+
+/**
+ * @def DBUS_HAVE_INT64
+ *
+ * Defined if 64-bit integers are available. Will be defined
+ * on any platform you care about, unless you care about
+ * some truly ancient UNIX, or some bizarre embedded platform.
+ *
+ * C99 requires a 64-bit type and most likely all interesting
+ * compilers support one. GLib for example flat-out requires
+ * a 64-bit type.
+ *
+ * You should feel comfortable ignoring this macro and just using
+ * int64 unconditionally.
+ * 
+ */
+
+/**
+ * @def DBUS_INT64_CONSTANT
+ *
+ * Declare a 64-bit signed integer constant. The macro
+ * adds the necessary "LL" or whatever after the integer,
+ * giving a literal such as "325145246765LL"
+ */
+
+/**
+ * @def DBUS_UINT64_CONSTANT
+ *
+ * Declare a 64-bit unsigned integer constant. The macro
+ * adds the necessary "ULL" or whatever after the integer,
+ * giving a literal such as "325145246765ULL"
+ */
+
+/** @} */
+
+#endif /* DBUS_TYPES_H */
diff --git a/src/dbus/dbus-userdb-util.c b/src/dbus/dbus-userdb-util.c
new file mode 100644 (file)
index 0000000..d03a7c7
--- /dev/null
@@ -0,0 +1,442 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-userdb-util.c Would be in dbus-userdb.c, but not used in libdbus
+ * 
+ * Copyright (C) 2003, 2004, 2005  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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
+ *
+ */
+#define DBUS_USERDB_INCLUDES_PRIVATE 1
+#include "dbus-userdb.h"
+#include "dbus-test.h"
+#include "dbus-internals.h"
+#include "dbus-protocol.h"
+#include <string.h>
+
+/**
+ * @addtogroup DBusInternalsUtils
+ * @{
+ */
+
+/**
+ * Checks to see if the UID sent in is the console user
+ *
+ * @param uid UID of person to check 
+ * @param error return location for errors
+ * @returns #TRUE if the UID is the same as the console user and there are no errors
+ */
+dbus_bool_t
+_dbus_is_console_user (dbus_uid_t uid,
+                      DBusError *error)
+{
+
+  DBusUserDatabase *db;
+  const DBusUserInfo *info;
+  dbus_bool_t result = FALSE; 
+
+#ifdef HAVE_CONSOLE_OWNER_FILE
+
+  DBusString f;
+  DBusStat st;
+
+  if (!_dbus_string_init (&f))
+    {
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+
+  if (!_dbus_string_append(&f, DBUS_CONSOLE_OWNER_FILE))
+    {
+      _dbus_string_free(&f);
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+
+  if (_dbus_stat(&f, &st, NULL) && (st.uid == uid))
+    {
+      _dbus_string_free(&f);
+      return TRUE;
+    }
+
+  _dbus_string_free(&f);
+
+#endif /* HAVE_CONSOLE_OWNER_FILE */
+
+  _dbus_user_database_lock_system ();
+
+  db = _dbus_user_database_get_system ();
+  if (db == NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_FAILED, "Could not get system database.");
+      _dbus_user_database_unlock_system ();
+      return FALSE;
+    }
+
+  /* TPTD: this should be cache-safe, we've locked the DB and
+    _dbus_user_at_console doesn't pass it on. */
+  info = _dbus_user_database_lookup (db, uid, NULL, error);
+
+  if (info == NULL)
+    {
+      _dbus_user_database_unlock_system ();
+       return FALSE;
+    }
+
+  result = _dbus_user_at_console (info->username, error);
+
+  _dbus_user_database_unlock_system ();
+
+  return result;
+}
+
+/**
+ * Gets user ID given username
+ *
+ * @param username the username
+ * @param uid return location for UID
+ * @returns #TRUE if username existed and we got the UID
+ */
+dbus_bool_t
+_dbus_get_user_id (const DBusString  *username,
+                   dbus_uid_t        *uid)
+{
+  return _dbus_get_user_id_and_primary_group (username, uid, NULL);
+}
+
+/**
+ * Gets group ID given groupname
+ *
+ * @param groupname the groupname
+ * @param gid return location for GID
+ * @returns #TRUE if group name existed and we got the GID
+ */
+dbus_bool_t
+_dbus_get_group_id (const DBusString  *groupname,
+                    dbus_gid_t        *gid)
+{
+  DBusUserDatabase *db;
+  const DBusGroupInfo *info;
+  _dbus_user_database_lock_system ();
+
+  db = _dbus_user_database_get_system ();
+  if (db == NULL)
+    {
+      _dbus_user_database_unlock_system ();
+      return FALSE;
+    }
+
+  if (!_dbus_user_database_get_groupname (db, groupname,
+                                          &info, NULL))
+    {
+      _dbus_user_database_unlock_system ();
+      return FALSE;
+    }
+
+  *gid = info->gid;
+  
+  _dbus_user_database_unlock_system ();
+  return TRUE;
+}
+
+/**
+ * Gets user ID and primary group given username
+ *
+ * @param username the username
+ * @param uid_p return location for UID
+ * @param gid_p return location for GID
+ * @returns #TRUE if username existed and we got the UID and GID
+ */
+dbus_bool_t
+_dbus_get_user_id_and_primary_group (const DBusString  *username,
+                                     dbus_uid_t        *uid_p,
+                                     dbus_gid_t        *gid_p)
+{
+  DBusUserDatabase *db;
+  const DBusUserInfo *info;
+  _dbus_user_database_lock_system ();
+
+  db = _dbus_user_database_get_system ();
+  if (db == NULL)
+    {
+      _dbus_user_database_unlock_system ();
+      return FALSE;
+    }
+
+  if (!_dbus_user_database_get_username (db, username,
+                                         &info, NULL))
+    {
+      _dbus_user_database_unlock_system ();
+      return FALSE;
+    }
+
+  if (uid_p)
+    *uid_p = info->uid;
+  if (gid_p)
+    *gid_p = info->primary_gid;
+  
+  _dbus_user_database_unlock_system ();
+  return TRUE;
+}
+
+/**
+ * Looks up a gid or group name in the user database.  Only one of
+ * name or GID can be provided. There are wrapper functions for this
+ * that are better to use, this one does no locking or anything on the
+ * database and otherwise sort of sucks.
+ *
+ * @param db the database
+ * @param gid the group ID or #DBUS_GID_UNSET
+ * @param groupname group name or #NULL 
+ * @param error error to fill in
+ * @returns the entry in the database
+ */
+DBusGroupInfo*
+_dbus_user_database_lookup_group (DBusUserDatabase *db,
+                                  dbus_gid_t        gid,
+                                  const DBusString *groupname,
+                                  DBusError        *error)
+{
+  DBusGroupInfo *info;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+   /* See if the group is really a number */
+   if (gid == DBUS_UID_UNSET)
+    {
+      unsigned long n;
+
+      if (_dbus_is_a_number (groupname, &n))
+        gid = n;
+    }
+
+#ifdef DBUS_ENABLE_USERDB_CACHE
+  if (gid != DBUS_GID_UNSET)
+    info = _dbus_hash_table_lookup_ulong (db->groups, gid);
+  else
+    info = _dbus_hash_table_lookup_string (db->groups_by_name,
+                                           _dbus_string_get_const_data (groupname));
+  if (info)
+    {
+      _dbus_verbose ("Using cache for GID "DBUS_GID_FORMAT" information\n",
+                     info->gid);
+      return info;
+    }
+  else
+#else
+  if (1)
+#endif
+    {
+      if (gid != DBUS_GID_UNSET)
+       _dbus_verbose ("No cache for GID "DBUS_GID_FORMAT"\n",
+                      gid);
+      else
+       _dbus_verbose ("No cache for groupname \"%s\"\n",
+                      _dbus_string_get_const_data (groupname));
+      
+      info = dbus_new0 (DBusGroupInfo, 1);
+      if (info == NULL)
+        {
+          dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+          return NULL;
+        }
+
+      if (gid != DBUS_GID_UNSET)
+        {
+          if (!_dbus_group_info_fill_gid (info, gid, error))
+            {
+              _DBUS_ASSERT_ERROR_IS_SET (error);
+              _dbus_group_info_free_allocated (info);
+              return NULL;
+            }
+        }
+      else
+        {
+          if (!_dbus_group_info_fill (info, groupname, error))
+            {
+              _DBUS_ASSERT_ERROR_IS_SET (error);
+              _dbus_group_info_free_allocated (info);
+              return NULL;
+            }
+        }
+
+      /* don't use these past here */
+      gid = DBUS_GID_UNSET;
+      groupname = NULL;
+
+      if (!_dbus_hash_table_insert_ulong (db->groups, info->gid, info))
+        {
+          dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+          _dbus_group_info_free_allocated (info);
+          return NULL;
+        }
+
+
+      if (!_dbus_hash_table_insert_string (db->groups_by_name,
+                                           info->groupname,
+                                           info))
+        {
+          _dbus_hash_table_remove_ulong (db->groups, info->gid);
+          dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+          return NULL;
+        }
+      
+      return info;
+    }
+}
+
+
+/**
+ * Gets the user information for the given group name,
+ * returned group info should not be freed. 
+ *
+ * @param db user database
+ * @param groupname the group name
+ * @param info return location for const ref to group info
+ * @param error error location
+ * @returns #FALSE if error is set
+ */
+dbus_bool_t
+_dbus_user_database_get_groupname (DBusUserDatabase     *db,
+                                   const DBusString     *groupname,
+                                   const DBusGroupInfo **info,
+                                   DBusError            *error)
+{
+  *info = _dbus_user_database_lookup_group (db, DBUS_GID_UNSET, groupname, error);
+  return *info != NULL;
+}
+
+/**
+ * Gets the user information for the given GID,
+ * returned group info should not be freed. 
+ *
+ * @param db user database
+ * @param gid the group ID
+ * @param info return location for const ref to group info
+ * @param error error location
+ * @returns #FALSE if error is set
+ */
+dbus_bool_t
+_dbus_user_database_get_gid (DBusUserDatabase     *db,
+                             dbus_gid_t            gid,
+                             const DBusGroupInfo **info,
+                             DBusError            *error)
+{
+  *info = _dbus_user_database_lookup_group (db, gid, NULL, error);
+  return *info != NULL;
+}
+
+
+/**
+ * Gets all groups  corresponding to the given UID. Returns #FALSE
+ * if no memory, or user isn't known, but always initializes
+ * group_ids to a NULL array. 
+ *
+ * @param uid the UID
+ * @param group_ids return location for array of group IDs
+ * @param n_group_ids return location for length of returned array
+ * @returns #TRUE if the UID existed and we got some credentials
+ */
+dbus_bool_t
+_dbus_groups_from_uid (dbus_uid_t         uid,
+                       dbus_gid_t       **group_ids,
+                       int               *n_group_ids)
+{
+  DBusUserDatabase *db;
+  const DBusUserInfo *info;
+  *group_ids = NULL;
+  *n_group_ids = 0;
+
+  _dbus_user_database_lock_system ();
+
+  db = _dbus_user_database_get_system ();
+  if (db == NULL)
+    {
+      _dbus_user_database_unlock_system ();
+      return FALSE;
+    }
+
+  if (!_dbus_user_database_get_uid (db, uid,
+                                    &info, NULL))
+    {
+      _dbus_user_database_unlock_system ();
+      return FALSE;
+    }
+
+  _dbus_assert (info->uid == uid);
+  
+  if (info->n_group_ids > 0)
+    {
+      *group_ids = dbus_new (dbus_gid_t, info->n_group_ids);
+      if (*group_ids == NULL)
+        {
+         _dbus_user_database_unlock_system ();
+          return FALSE;
+        }
+
+      *n_group_ids = info->n_group_ids;
+
+      memcpy (*group_ids, info->group_ids, info->n_group_ids * sizeof (dbus_gid_t));
+    }
+
+  _dbus_user_database_unlock_system ();
+  return TRUE;
+}
+/** @} */
+
+#ifdef DBUS_BUILD_TESTS
+#include <stdio.h>
+
+/**
+ * Unit test for dbus-userdb.c.
+ * 
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_userdb_test (const char *test_data_dir)
+{
+  const DBusString *username;
+  const DBusString *homedir;
+  dbus_uid_t uid;
+  unsigned long *group_ids;
+  int n_group_ids, i;
+
+  if (!_dbus_username_from_current_process (&username))
+    _dbus_assert_not_reached ("didn't get username");
+
+  if (!_dbus_homedir_from_current_process (&homedir))
+    _dbus_assert_not_reached ("didn't get homedir");  
+
+  if (!_dbus_get_user_id (username, &uid))
+    _dbus_assert_not_reached ("didn't get uid");
+
+  if (!_dbus_groups_from_uid (uid, &group_ids, &n_group_ids))
+    _dbus_assert_not_reached ("didn't get groups");
+
+  printf ("    Current user: %s homedir: %s gids:",
+          _dbus_string_get_const_data (username),
+          _dbus_string_get_const_data (homedir));
+
+  for (i=0; i<n_group_ids; i++)
+      printf(" %ld", group_ids[i]);
+
+  printf ("\n");
+  dbus_free (group_ids);
+
+  return TRUE;
+}
+#endif /* DBUS_BUILD_TESTS */
diff --git a/src/dbus/dbus-userdb.c b/src/dbus/dbus-userdb.c
new file mode 100644 (file)
index 0000000..03d263f
--- /dev/null
@@ -0,0 +1,665 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-userdb.c User database abstraction
+ * 
+ * Copyright (C) 2003, 2004  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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
+ *
+ */
+#define DBUS_USERDB_INCLUDES_PRIVATE 1
+#include "dbus-userdb.h"
+#include "dbus-hash.h"
+#include "dbus-test.h"
+#include "dbus-internals.h"
+#include "dbus-protocol.h"
+#include "dbus-credentials.h"
+#include <string.h>
+
+/**
+ * @addtogroup DBusInternalsUtils
+ * @{
+ */
+
+/**
+ * Frees the given #DBusUserInfo's members with _dbus_user_info_free()
+ * and also calls dbus_free() on the block itself
+ *
+ * @param info the info
+ */
+void
+_dbus_user_info_free_allocated (DBusUserInfo *info)
+{
+  if (info == NULL) /* hash table will pass NULL */
+    return;
+
+  _dbus_user_info_free (info);
+  dbus_free (info);
+}
+
+/**
+ * Frees the given #DBusGroupInfo's members with _dbus_group_info_free()
+ * and also calls dbus_free() on the block itself
+ *
+ * @param info the info
+ */
+void
+_dbus_group_info_free_allocated (DBusGroupInfo *info)
+{
+  if (info == NULL) /* hash table will pass NULL */
+    return;
+
+  _dbus_group_info_free (info);
+  dbus_free (info);
+}
+
+/**
+ * Frees the members of info
+ * (but not info itself)
+ * @param info the user info struct
+ */
+void
+_dbus_user_info_free (DBusUserInfo *info)
+{
+  dbus_free (info->group_ids);
+  dbus_free (info->username);
+  dbus_free (info->homedir);
+}
+
+/**
+ * Frees the members of info (but not info itself).
+ *
+ * @param info the group info
+ */
+void
+_dbus_group_info_free (DBusGroupInfo    *info)
+{
+  dbus_free (info->groupname);
+}
+
+/**
+ * Checks if a given string is actually a number 
+ * and converts it if it is 
+ *
+ * @param str the string to check
+ * @param num the memory location of the unsigned long to fill in
+ * @returns TRUE if str is a number and num is filled in 
+ */
+dbus_bool_t
+_dbus_is_a_number (const DBusString *str,
+                   unsigned long    *num)
+{
+  int end;
+
+  if (_dbus_string_parse_uint (str, 0, num, &end) &&
+      end == _dbus_string_get_length (str))
+    return TRUE;
+  else
+    return FALSE;
+}
+
+/**
+ * Looks up a uid or username in the user database.  Only one of name
+ * or UID can be provided. There are wrapper functions for this that
+ * are better to use, this one does no locking or anything on the
+ * database and otherwise sort of sucks.
+ *
+ * @param db the database
+ * @param uid the user ID or #DBUS_UID_UNSET
+ * @param username username or #NULL 
+ * @param error error to fill in
+ * @returns the entry in the database
+ */
+DBusUserInfo*
+_dbus_user_database_lookup (DBusUserDatabase *db,
+                            dbus_uid_t        uid,
+                            const DBusString *username,
+                            DBusError        *error)
+{
+  DBusUserInfo *info;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  _dbus_assert (uid != DBUS_UID_UNSET || username != NULL);
+
+  /* See if the username is really a number */
+  if (uid == DBUS_UID_UNSET)
+    {
+      unsigned long n;
+
+      if (_dbus_is_a_number (username, &n))
+        uid = n;
+    }
+
+#ifdef DBUS_ENABLE_USERDB_CACHE  
+  if (uid != DBUS_UID_UNSET)
+    info = _dbus_hash_table_lookup_ulong (db->users, uid);
+  else
+    info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username));
+
+  if (info)
+    {
+      _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n",
+                     info->uid);
+      return info;
+    }
+  else
+#else 
+  if (1)
+#endif
+    {
+      if (uid != DBUS_UID_UNSET)
+       _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
+                      uid);
+      else
+       _dbus_verbose ("No cache for user \"%s\"\n",
+                      _dbus_string_get_const_data (username));
+      
+      info = dbus_new0 (DBusUserInfo, 1);
+      if (info == NULL)
+        {
+          dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+          return NULL;
+        }
+
+      if (uid != DBUS_UID_UNSET)
+        {
+          if (!_dbus_user_info_fill_uid (info, uid, error))
+            {
+              _DBUS_ASSERT_ERROR_IS_SET (error);
+              _dbus_user_info_free_allocated (info);
+              return NULL;
+            }
+        }
+      else
+        {
+          if (!_dbus_user_info_fill (info, username, error))
+            {
+              _DBUS_ASSERT_ERROR_IS_SET (error);
+              _dbus_user_info_free_allocated (info);
+              return NULL;
+            }
+        }
+
+      /* be sure we don't use these after here */
+      uid = DBUS_UID_UNSET;
+      username = NULL;
+
+      /* insert into hash */
+      if (!_dbus_hash_table_insert_ulong (db->users, info->uid, info))
+        {
+          dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+          _dbus_user_info_free_allocated (info);
+          return NULL;
+        }
+
+      if (!_dbus_hash_table_insert_string (db->users_by_name,
+                                           info->username,
+                                           info))
+        {
+          _dbus_hash_table_remove_ulong (db->users, info->uid);
+          dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+          return NULL;
+        }
+      
+      return info;
+    }
+}
+
+static dbus_bool_t database_locked = FALSE;
+static DBusUserDatabase *system_db = NULL;
+static DBusString process_username;
+static DBusString process_homedir;
+      
+static void
+shutdown_system_db (void *data)
+{
+  _dbus_user_database_unref (system_db);
+  system_db = NULL;
+  _dbus_string_free (&process_username);
+  _dbus_string_free (&process_homedir);
+}
+
+static dbus_bool_t
+init_system_db (void)
+{
+  _dbus_assert (database_locked);
+    
+  if (system_db == NULL)
+    {
+      DBusError error = DBUS_ERROR_INIT;
+      const DBusUserInfo *info;
+      
+      system_db = _dbus_user_database_new ();
+      if (system_db == NULL)
+        return FALSE;
+
+      if (!_dbus_user_database_get_uid (system_db,
+                                        _dbus_getuid (),
+                                        &info,
+                                        &error))
+        {
+          _dbus_user_database_unref (system_db);
+          system_db = NULL;
+          
+          if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+            {
+              dbus_error_free (&error);
+              return FALSE;
+            }
+          else
+            {
+              /* This really should not happen. */
+              _dbus_warn ("Could not get password database information for UID of current process: %s\n",
+                          error.message);
+              dbus_error_free (&error);
+              return FALSE;
+            }
+        }
+
+      if (!_dbus_string_init (&process_username))
+        {
+          _dbus_user_database_unref (system_db);
+          system_db = NULL;
+          return FALSE;
+        }
+
+      if (!_dbus_string_init (&process_homedir))
+        {
+          _dbus_string_free (&process_username);
+          _dbus_user_database_unref (system_db);
+          system_db = NULL;
+          return FALSE;
+        }
+
+      if (!_dbus_string_append (&process_username,
+                                info->username) ||
+          !_dbus_string_append (&process_homedir,
+                                info->homedir) ||
+          !_dbus_register_shutdown_func (shutdown_system_db, NULL))
+        {
+          _dbus_string_free (&process_username);
+          _dbus_string_free (&process_homedir);
+          _dbus_user_database_unref (system_db);
+          system_db = NULL;
+          return FALSE;
+        }
+    }
+
+  return TRUE;
+}
+
+/**
+ * Locks global system user database.
+ */
+void
+_dbus_user_database_lock_system (void)
+{
+  _DBUS_LOCK (system_users);
+  database_locked = TRUE;
+}
+
+/**
+ * Unlocks global system user database.
+ */
+void
+_dbus_user_database_unlock_system (void)
+{
+  database_locked = FALSE;
+  _DBUS_UNLOCK (system_users);
+}
+
+/**
+ * Gets the system global user database;
+ * must be called with lock held (_dbus_user_database_lock_system()).
+ *
+ * @returns the database or #NULL if no memory
+ */
+DBusUserDatabase*
+_dbus_user_database_get_system (void)
+{
+  _dbus_assert (database_locked);
+
+  init_system_db ();
+  
+  return system_db;
+}
+
+/**
+ * Flushes the system global user database;
+ */
+void
+_dbus_user_database_flush_system (void)
+{
+  _dbus_user_database_lock_system ();
+   
+  _dbus_user_database_flush (system_db);
+
+  _dbus_user_database_unlock_system ();
+}
+
+/**
+ * Gets username of user owning current process.  The returned string
+ * is valid until dbus_shutdown() is called.
+ *
+ * @param username place to store pointer to username
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_username_from_current_process (const DBusString **username)
+{
+  _dbus_user_database_lock_system ();
+  if (!init_system_db ())
+    {
+      _dbus_user_database_unlock_system ();
+      return FALSE;
+    }
+  *username = &process_username;
+  _dbus_user_database_unlock_system ();  
+
+  return TRUE;
+}
+
+/**
+ * Gets homedir of user owning current process.  The returned string
+ * is valid until dbus_shutdown() is called.
+ *
+ * @param homedir place to store pointer to homedir
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_homedir_from_current_process (const DBusString  **homedir)
+{
+  _dbus_user_database_lock_system ();
+  if (!init_system_db ())
+    {
+      _dbus_user_database_unlock_system ();
+      return FALSE;
+    }
+  *homedir = &process_homedir;
+  _dbus_user_database_unlock_system ();
+
+  return TRUE;
+}
+
+/**
+ * Gets the home directory for the given user.
+ *
+ * @param username the username
+ * @param homedir string to append home directory to
+ * @returns #TRUE if user existed and we appended their homedir
+ */
+dbus_bool_t
+_dbus_homedir_from_username (const DBusString *username,
+                             DBusString       *homedir)
+{
+  DBusUserDatabase *db;
+  const DBusUserInfo *info;
+  _dbus_user_database_lock_system ();
+
+  db = _dbus_user_database_get_system ();
+  if (db == NULL)
+    {
+      _dbus_user_database_unlock_system ();
+      return FALSE;
+    }
+
+  if (!_dbus_user_database_get_username (db, username,
+                                         &info, NULL))
+    {
+      _dbus_user_database_unlock_system ();
+      return FALSE;
+    }
+
+  if (!_dbus_string_append (homedir, info->homedir))
+    {
+      _dbus_user_database_unlock_system ();
+      return FALSE;
+    }
+  
+  _dbus_user_database_unlock_system ();
+  return TRUE;
+}
+
+/**
+ * Gets the home directory for the given user.
+ *
+ * @param uid the uid
+ * @param homedir string to append home directory to
+ * @returns #TRUE if user existed and we appended their homedir
+ */
+dbus_bool_t
+_dbus_homedir_from_uid (dbus_uid_t         uid,
+                        DBusString        *homedir)
+{
+  DBusUserDatabase *db;
+  const DBusUserInfo *info;
+  _dbus_user_database_lock_system ();
+
+  db = _dbus_user_database_get_system ();
+  if (db == NULL)
+    {
+      _dbus_user_database_unlock_system ();
+      return FALSE;
+    }
+
+  if (!_dbus_user_database_get_uid (db, uid,
+                                    &info, NULL))
+    {
+      _dbus_user_database_unlock_system ();
+      return FALSE;
+    }
+
+  if (!_dbus_string_append (homedir, info->homedir))
+    {
+      _dbus_user_database_unlock_system ();
+      return FALSE;
+    }
+  
+  _dbus_user_database_unlock_system ();
+  return TRUE;
+}
+
+/**
+ * Adds the credentials corresponding to the given username.
+ *
+ * Used among other purposes to parses a desired identity provided
+ * from a client in the auth protocol. On UNIX this means parsing a
+ * UID, on Windows probably parsing an SID string.
+ * 
+ * @todo this is broken because it treats OOM and parse error
+ * the same way. Needs a #DBusError.
+ * 
+ * @param credentials credentials to fill in 
+ * @param username the username
+ * @returns #TRUE if the username existed and we got some credentials
+ */
+dbus_bool_t
+_dbus_credentials_add_from_user (DBusCredentials  *credentials,
+                                 const DBusString *username)
+{
+  DBusUserDatabase *db;
+  const DBusUserInfo *info;
+
+  _dbus_user_database_lock_system ();
+
+  db = _dbus_user_database_get_system ();
+  if (db == NULL)
+    {
+      _dbus_user_database_unlock_system ();
+      return FALSE;
+    }
+
+  if (!_dbus_user_database_get_username (db, username,
+                                         &info, NULL))
+    {
+      _dbus_user_database_unlock_system ();
+      return FALSE;
+    }
+
+  if (!_dbus_credentials_add_unix_uid(credentials, info->uid))
+    {
+      _dbus_user_database_unlock_system ();
+      return FALSE;
+    }
+  
+  _dbus_user_database_unlock_system ();
+  return TRUE;
+}
+
+/**
+ * Creates a new user database object used to look up and
+ * cache user information.
+ * @returns new database, or #NULL on out of memory
+ */
+DBusUserDatabase*
+_dbus_user_database_new (void)
+{
+  DBusUserDatabase *db;
+  
+  db = dbus_new0 (DBusUserDatabase, 1);
+  if (db == NULL)
+    return NULL;
+
+  db->refcount = 1;
+
+  db->users = _dbus_hash_table_new (DBUS_HASH_ULONG,
+                                    NULL, (DBusFreeFunction) _dbus_user_info_free_allocated);
+  
+  if (db->users == NULL)
+    goto failed;
+
+  db->groups = _dbus_hash_table_new (DBUS_HASH_ULONG,
+                                     NULL, (DBusFreeFunction) _dbus_group_info_free_allocated);
+  
+  if (db->groups == NULL)
+    goto failed;
+
+  db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
+                                            NULL, NULL);
+  if (db->users_by_name == NULL)
+    goto failed;
+  
+  db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
+                                             NULL, NULL);
+  if (db->groups_by_name == NULL)
+    goto failed;
+  
+  return db;
+  
+ failed:
+  _dbus_user_database_unref (db);
+  return NULL;
+}
+
+/**
+ * Flush all information out of the user database. 
+ */
+void
+_dbus_user_database_flush (DBusUserDatabase *db) 
+{
+  _dbus_hash_table_remove_all(db->users_by_name);
+  _dbus_hash_table_remove_all(db->groups_by_name);
+  _dbus_hash_table_remove_all(db->users);
+  _dbus_hash_table_remove_all(db->groups);
+}
+
+#ifdef DBUS_BUILD_TESTS
+/**
+ * Increments refcount of user database.
+ * @param db the database
+ * @returns the database
+ */
+DBusUserDatabase *
+_dbus_user_database_ref (DBusUserDatabase  *db)
+{
+  _dbus_assert (db->refcount > 0);
+
+  db->refcount += 1;
+
+  return db;
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * Decrements refcount of user database.
+ * @param db the database
+ */
+void
+_dbus_user_database_unref (DBusUserDatabase  *db)
+{
+  _dbus_assert (db->refcount > 0);
+
+  db->refcount -= 1;
+  if (db->refcount == 0)
+    {
+      if (db->users)
+        _dbus_hash_table_unref (db->users);
+
+      if (db->groups)
+        _dbus_hash_table_unref (db->groups);
+
+      if (db->users_by_name)
+        _dbus_hash_table_unref (db->users_by_name);
+
+      if (db->groups_by_name)
+        _dbus_hash_table_unref (db->groups_by_name);
+      
+      dbus_free (db);
+    }
+}
+
+/**
+ * Gets the user information for the given UID,
+ * returned user info should not be freed. 
+ *
+ * @param db user database
+ * @param uid the user ID
+ * @param info return location for const ref to user info
+ * @param error error location
+ * @returns #FALSE if error is set
+ */
+dbus_bool_t
+_dbus_user_database_get_uid (DBusUserDatabase    *db,
+                             dbus_uid_t           uid,
+                             const DBusUserInfo **info,
+                             DBusError           *error)
+{
+  *info = _dbus_user_database_lookup (db, uid, NULL, error);
+  return *info != NULL;
+}
+
+/**
+ * Gets the user information for the given username.
+ *
+ * @param db user database
+ * @param username the user name
+ * @param info return location for const ref to user info
+ * @param error error location
+ * @returns #FALSE if error is set
+ */
+dbus_bool_t
+_dbus_user_database_get_username  (DBusUserDatabase     *db,
+                                   const DBusString     *username,
+                                   const DBusUserInfo  **info,
+                                   DBusError            *error)
+{
+  *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
+  return *info != NULL;
+}
+
+/** @} */
+
+/* Tests in dbus-userdb-util.c */
diff --git a/src/dbus/dbus-userdb.h b/src/dbus/dbus-userdb.h
new file mode 100644 (file)
index 0000000..a1153ee
--- /dev/null
@@ -0,0 +1,121 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-userdb.h User database abstraction
+ * 
+ * Copyright (C) 2003  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_USERDB_H
+#define DBUS_USERDB_H
+
+#include <dbus/dbus-sysdeps-unix.h>
+
+#ifdef DBUS_WIN
+#error "Don't include this on Windows"
+#endif
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusUserDatabase DBusUserDatabase;
+
+#ifdef DBUS_USERDB_INCLUDES_PRIVATE
+#include <dbus/dbus-hash.h>
+
+/**
+ * Internals of DBusUserDatabase
+ */
+struct DBusUserDatabase
+{
+  int refcount; /**< Reference count */
+
+  DBusHashTable *users; /**< Users in the database by UID */
+  DBusHashTable *groups; /**< Groups in the database by GID */
+  DBusHashTable *users_by_name; /**< Users in the database by name */
+  DBusHashTable *groups_by_name; /**< Groups in the database by name */
+
+};
+
+
+DBusUserDatabase* _dbus_user_database_new           (void);
+DBusUserDatabase* _dbus_user_database_ref           (DBusUserDatabase     *db);
+void              _dbus_user_database_flush         (DBusUserDatabase     *db);
+void              _dbus_user_database_unref         (DBusUserDatabase     *db);
+dbus_bool_t       _dbus_user_database_get_uid       (DBusUserDatabase     *db,
+                                                     dbus_uid_t            uid,
+                                                     const DBusUserInfo  **info,
+                                                     DBusError            *error);
+dbus_bool_t       _dbus_user_database_get_gid       (DBusUserDatabase     *db,
+                                                     dbus_gid_t            gid,
+                                                     const DBusGroupInfo **info,
+                                                     DBusError            *error);
+dbus_bool_t       _dbus_user_database_get_username  (DBusUserDatabase     *db,
+                                                     const DBusString     *username,
+                                                     const DBusUserInfo  **info,
+                                                     DBusError            *error);
+dbus_bool_t       _dbus_user_database_get_groupname (DBusUserDatabase     *db,
+                                                     const DBusString     *groupname,
+                                                     const DBusGroupInfo **info,
+                                                     DBusError            *error);
+
+DBusUserInfo*  _dbus_user_database_lookup       (DBusUserDatabase *db,
+                                                 dbus_uid_t        uid,
+                                                 const DBusString *username,
+                                                 DBusError        *error);
+DBusGroupInfo* _dbus_user_database_lookup_group (DBusUserDatabase *db,
+                                                 dbus_gid_t        gid,
+                                                 const DBusString *groupname,
+                                                 DBusError        *error);
+void           _dbus_user_info_free_allocated   (DBusUserInfo     *info);
+void           _dbus_group_info_free_allocated  (DBusGroupInfo    *info);
+#endif /* DBUS_USERDB_INCLUDES_PRIVATE */
+
+DBusUserDatabase* _dbus_user_database_get_system    (void);
+void              _dbus_user_database_lock_system   (void);
+void              _dbus_user_database_unlock_system (void);
+void              _dbus_user_database_flush_system  (void);
+
+dbus_bool_t _dbus_get_user_id                   (const DBusString  *username,
+                                                 dbus_uid_t        *uid);
+dbus_bool_t _dbus_get_group_id                  (const DBusString  *group_name,
+                                                 dbus_gid_t        *gid);
+dbus_bool_t _dbus_get_user_id_and_primary_group (const DBusString  *username,
+                                                 dbus_uid_t        *uid_p,
+                                                 dbus_gid_t        *gid_p);
+dbus_bool_t _dbus_credentials_from_uid          (dbus_uid_t         user_id,
+                                                 DBusCredentials   *credentials);
+dbus_bool_t _dbus_groups_from_uid              (dbus_uid_t            uid,
+                                                 dbus_gid_t          **group_ids,
+                                                 int                  *n_group_ids);
+dbus_bool_t _dbus_is_console_user               (dbus_uid_t         uid,
+                                                 DBusError         *error);
+
+dbus_bool_t _dbus_is_a_number                   (const DBusString *str, 
+                                                 unsigned long    *num);
+
+dbus_bool_t _dbus_username_from_current_process (const DBusString **username);
+dbus_bool_t _dbus_homedir_from_current_process  (const DBusString **homedir);
+dbus_bool_t _dbus_homedir_from_username         (const DBusString  *username,
+                                                 DBusString        *homedir);
+
+dbus_bool_t _dbus_homedir_from_uid              (dbus_uid_t         uid,
+                                                 DBusString        *homedir);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_USERDB_H */
diff --git a/src/dbus/dbus-uuidgen.c b/src/dbus/dbus-uuidgen.c
new file mode 100644 (file)
index 0000000..6f226bc
--- /dev/null
@@ -0,0 +1,129 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-uuidgen.c  The guts of the dbus-uuidgen binary live in libdbus, in this file.
+ *
+ * Copyright (C) 2006  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-uuidgen.h"
+#include "dbus-internals.h"
+#include "dbus-string.h"
+#include "dbus-protocol.h"
+
+#ifdef DBUS_WIN
+#error "dbus-uuidgen should not be needed on Windows"
+#endif
+
+/**
+ * @defgroup DBusInternalsUuidgen dbus-uuidgen implementation
+ * @ingroup DBusInternals
+ * @brief Functions for dbus-uuidgen binary
+ *
+ * These are not considered part of the ABI, and if you call them
+ * you will get screwed by future changes.
+ * 
+ * @{
+ */
+
+static dbus_bool_t
+return_uuid (DBusGUID   *uuid,
+             char      **uuid_p,
+             DBusError  *error)
+{
+  if (uuid_p)
+    {
+      DBusString encoded;
+
+      if (!_dbus_string_init (&encoded))
+        {
+          _DBUS_SET_OOM (error);
+          return FALSE;
+        }
+
+      if (!_dbus_uuid_encode (uuid, &encoded) ||
+          !_dbus_string_steal_data (&encoded, uuid_p))
+        {
+          _DBUS_SET_OOM (error);
+          _dbus_string_free (&encoded);
+          return FALSE;
+        }
+      _dbus_string_free (&encoded);
+    }
+  return TRUE;
+}
+
+/**
+ * For use by the dbus-uuidgen binary ONLY, do not call this.
+ * We can and will change this function without modifying
+ * the libdbus soname.
+ *
+ * @param filename the file or #NULL for the machine ID file
+ * @param uuid_p out param to return the uuid
+ * @param create_if_not_found whether to create it if not already there
+ * @param error error return
+ * @returns #FALSE if error is set
+ */
+dbus_bool_t
+dbus_internal_do_not_use_get_uuid (const char *filename,
+                                   char      **uuid_p,
+                                   dbus_bool_t create_if_not_found,
+                                   DBusError  *error)
+{
+  DBusGUID uuid;
+  
+  if (filename)
+    {
+      DBusString filename_str;
+      _dbus_string_init_const (&filename_str, filename);
+      if (!_dbus_read_uuid_file (&filename_str, &uuid, create_if_not_found, error))
+        goto error;
+    }
+  else
+    {
+      if (!_dbus_read_local_machine_uuid (&uuid, create_if_not_found, error))
+        goto error;
+    }
+
+  if (!return_uuid(&uuid, uuid_p, error))
+    goto error;
+
+  return TRUE;
+  
+ error:
+  _DBUS_ASSERT_ERROR_IS_SET (error);
+  return FALSE;
+}
+
+/**
+ * For use by the dbus-uuidgen binary ONLY, do not call this.
+ * We can and will change this function without modifying
+ * the libdbus soname.
+ *
+ * @param uuid_p out param to return the uuid
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+dbus_internal_do_not_use_create_uuid (char      **uuid_p)
+{
+  DBusGUID uuid;
+
+  _dbus_generate_uuid (&uuid);
+  return return_uuid (&uuid, uuid_p, NULL);
+}
+
+/** @} */
diff --git a/src/dbus/dbus-uuidgen.h b/src/dbus/dbus-uuidgen.h
new file mode 100644 (file)
index 0000000..3e30b99
--- /dev/null
@@ -0,0 +1,47 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-uuidgen.h  The guts of the dbus-uuidgen binary live in libdbus, in this file.
+ *
+ * Copyright (C) 2006  Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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
+ *
+ */
+#ifdef DBUS_INSIDE_DBUS_H
+#error "You can't include dbus-uuidgen.h in the public header dbus.h"
+#endif
+
+#ifndef DBUS_UUIDGEN_H
+#define DBUS_UUIDGEN_H
+
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-errors.h>
+
+DBUS_BEGIN_DECLS
+
+dbus_bool_t dbus_internal_do_not_use_get_uuid    (const char *filename,
+                                                  char      **uuid_p,
+                                                  dbus_bool_t create_if_not_found,
+                                                  DBusError  *error);
+dbus_bool_t dbus_internal_do_not_use_ensure_uuid (const char *filename,
+                                                  char      **uuid_p,
+                                                  DBusError  *error);
+dbus_bool_t dbus_internal_do_not_use_create_uuid (char      **uuid_p);
+
+
+DBUS_END_DECLS
+
+#endif /* DBUS_UUIDGEN_H */
diff --git a/src/dbus/dbus-watch.c b/src/dbus/dbus-watch.c
new file mode 100644 (file)
index 0000000..9d6ab7c
--- /dev/null
@@ -0,0 +1,668 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-watch.c DBusWatch implementation
+ *
+ * Copyright (C) 2002, 2003  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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-internals.h"
+#include "dbus-watch.h"
+#include "dbus-list.h"
+
+/**
+ * @defgroup DBusWatchInternals DBusWatch implementation details
+ * @ingroup  DBusInternals
+ * @brief implementation details for DBusWatch
+ * 
+ * @{
+ */
+
+/**
+ * Implementation of DBusWatch
+ */
+struct DBusWatch
+{
+  int refcount;                        /**< Reference count */
+  int fd;                              /**< File descriptor. */
+  unsigned int flags;                  /**< Conditions to watch. */
+
+  DBusWatchHandler handler;                    /**< Watch handler. */
+  void *handler_data;                          /**< Watch handler data. */
+  DBusFreeFunction free_handler_data_function; /**< Free the watch handler data. */
+  
+  void *data;                          /**< Application data. */
+  DBusFreeFunction free_data_function; /**< Free the application data. */
+  unsigned int enabled : 1;            /**< Whether it's enabled. */
+};
+
+/**
+ * Creates a new DBusWatch. Used to add a file descriptor to be polled
+ * by a main loop.
+ * 
+ * @param fd the file descriptor to be watched.
+ * @param flags the conditions to watch for on the descriptor.
+ * @param enabled the initial enabled state
+ * @param handler the handler function
+ * @param data data for handler function
+ * @param free_data_function function to free the data
+ * @returns the new DBusWatch object.
+ */
+DBusWatch*
+_dbus_watch_new (int               fd,
+                 unsigned int      flags,
+                 dbus_bool_t       enabled,
+                 DBusWatchHandler  handler,
+                 void             *data,
+                 DBusFreeFunction  free_data_function)
+{
+  DBusWatch *watch;
+
+#define VALID_WATCH_FLAGS (DBUS_WATCH_WRITABLE | DBUS_WATCH_READABLE)
+  
+  _dbus_assert ((flags & VALID_WATCH_FLAGS) == flags);
+  
+  watch = dbus_new0 (DBusWatch, 1);
+  if (watch == NULL)
+    return NULL;
+  
+  watch->refcount = 1;
+  watch->fd = fd;
+  watch->flags = flags;
+  watch->enabled = enabled;
+
+  watch->handler = handler;
+  watch->handler_data = data;
+  watch->free_handler_data_function = free_data_function;
+  
+  return watch;
+}
+
+/**
+ * Increments the reference count of a DBusWatch object.
+ *
+ * @param watch the watch object.
+ * @returns the watch object.
+ */
+DBusWatch *
+_dbus_watch_ref (DBusWatch *watch)
+{
+  watch->refcount += 1;
+
+  return watch;
+}
+
+/**
+ * Decrements the reference count of a DBusWatch object
+ * and finalizes the object if the count reaches zero.
+ *
+ * @param watch the watch object.
+ */
+void
+_dbus_watch_unref (DBusWatch *watch)
+{
+  _dbus_assert (watch != NULL);
+  _dbus_assert (watch->refcount > 0);
+
+  watch->refcount -= 1;
+  if (watch->refcount == 0)
+    {
+      dbus_watch_set_data (watch, NULL, NULL); /* call free_data_function */
+
+      if (watch->free_handler_data_function)
+       (* watch->free_handler_data_function) (watch->handler_data);
+      
+      dbus_free (watch);
+    }
+}
+
+/**
+ * Clears the file descriptor from a now-invalid watch object so that
+ * no one tries to use it.  This is because a watch may stay alive due
+ * to reference counts after the file descriptor is closed.
+ * Invalidation makes it easier to catch bugs. It also
+ * keeps people from doing dorky things like assuming file descriptors
+ * are unique (never recycled).
+ *
+ * @param watch the watch object.
+ */
+void
+_dbus_watch_invalidate (DBusWatch *watch)
+{
+  watch->fd = -1;
+  watch->flags = 0;
+}
+
+/**
+ * Sanitizes the given condition so that it only contains
+ * flags that the DBusWatch requested. e.g. if the
+ * watch is a DBUS_WATCH_READABLE watch then
+ * DBUS_WATCH_WRITABLE will be stripped from the condition.
+ *
+ * @param watch the watch object.
+ * @param condition address of the condition to sanitize.
+ */
+void
+_dbus_watch_sanitize_condition (DBusWatch    *watch,
+                                unsigned int *condition)
+{
+  if (!(watch->flags & DBUS_WATCH_READABLE))
+    *condition &= ~DBUS_WATCH_READABLE;
+  if (!(watch->flags & DBUS_WATCH_WRITABLE))
+    *condition &= ~DBUS_WATCH_WRITABLE;
+}
+
+
+/**
+ * @typedef DBusWatchList
+ *
+ * Opaque data type representing a list of watches
+ * and a set of DBusAddWatchFunction/DBusRemoveWatchFunction.
+ * Automatically handles removing/re-adding watches
+ * when the DBusAddWatchFunction is updated or changed.
+ * Holds a reference count to each watch.
+ *
+ * Used in the implementation of both DBusServer and
+ * DBusClient.
+ *
+ */
+
+/**
+ * DBusWatchList implementation details. All fields
+ * are private.
+ *
+ */
+struct DBusWatchList
+{
+  DBusList *watches;           /**< Watch objects. */
+
+  DBusAddWatchFunction add_watch_function;    /**< Callback for adding a watch. */
+  DBusRemoveWatchFunction remove_watch_function; /**< Callback for removing a watch. */
+  DBusWatchToggledFunction watch_toggled_function; /**< Callback on toggling enablement */
+  void *watch_data;                           /**< Data for watch callbacks */
+  DBusFreeFunction watch_free_data_function;  /**< Free function for watch callback data */
+};
+
+/**
+ * Creates a new watch list. Returns #NULL if insufficient
+ * memory exists.
+ *
+ * @returns the new watch list, or #NULL on failure.
+ */
+DBusWatchList*
+_dbus_watch_list_new (void)
+{
+  DBusWatchList *watch_list;
+
+  watch_list = dbus_new0 (DBusWatchList, 1);
+  if (watch_list == NULL)
+    return NULL;
+
+  return watch_list;
+}
+
+/**
+ * Frees a DBusWatchList.
+ *
+ * @param watch_list the watch list.
+ */
+void
+_dbus_watch_list_free (DBusWatchList *watch_list)
+{
+  /* free watch_data and removes watches as a side effect */
+  _dbus_watch_list_set_functions (watch_list,
+                                  NULL, NULL, NULL, NULL, NULL);
+  _dbus_list_foreach (&watch_list->watches,
+                      (DBusForeachFunction) _dbus_watch_unref,
+                      NULL);
+  _dbus_list_clear (&watch_list->watches);
+
+  dbus_free (watch_list);
+}
+
+/**
+ * Sets the watch functions. This function is the "backend"
+ * for dbus_connection_set_watch_functions() and
+ * dbus_server_set_watch_functions().
+ *
+ * @param watch_list the watch list.
+ * @param add_function the add watch function.
+ * @param remove_function the remove watch function.
+ * @param toggled_function function on toggling enabled flag, or #NULL
+ * @param data the data for those functions.
+ * @param free_data_function the function to free the data.
+ * @returns #FALSE if not enough memory
+ *
+ */
+dbus_bool_t
+_dbus_watch_list_set_functions (DBusWatchList           *watch_list,
+                                DBusAddWatchFunction     add_function,
+                                DBusRemoveWatchFunction  remove_function,
+                                DBusWatchToggledFunction toggled_function,
+                                void                    *data,
+                                DBusFreeFunction         free_data_function)
+{
+  /* Add watches with the new watch function, failing on OOM */
+  if (add_function != NULL)
+    {
+      DBusList *link;
+      
+      link = _dbus_list_get_first_link (&watch_list->watches);
+      while (link != NULL)
+        {
+          DBusList *next = _dbus_list_get_next_link (&watch_list->watches,
+                                                     link);
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+          {
+            const char *watch_type;
+            int flags;
+
+            flags = dbus_watch_get_flags (link->data);
+            if ((flags & DBUS_WATCH_READABLE) &&
+                (flags & DBUS_WATCH_WRITABLE))
+              watch_type = "readwrite";
+            else if (flags & DBUS_WATCH_READABLE)
+              watch_type = "read";
+            else if (flags & DBUS_WATCH_WRITABLE)
+              watch_type = "write";
+            else
+              watch_type = "not read or write";
+            
+            _dbus_verbose ("Adding a %s watch on fd %d using newly-set add watch function\n",
+                           watch_type,
+                           dbus_watch_get_socket (link->data));
+          }
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
+          
+          if (!(* add_function) (link->data, data))
+            {
+              /* remove it all again and return FALSE */
+              DBusList *link2;
+              
+              link2 = _dbus_list_get_first_link (&watch_list->watches);
+              while (link2 != link)
+                {
+                  DBusList *next = _dbus_list_get_next_link (&watch_list->watches,
+                                                             link2);
+                  
+                  _dbus_verbose ("Removing watch on fd %d using newly-set remove function because initial add failed\n",
+                                 dbus_watch_get_socket (link2->data));
+                  
+                  (* remove_function) (link2->data, data);
+                  
+                  link2 = next;
+                }
+
+              return FALSE;
+            }
+      
+          link = next;
+        }
+    }
+  
+  /* Remove all current watches from previous watch handlers */
+
+  if (watch_list->remove_watch_function != NULL)
+    {
+      _dbus_verbose ("Removing all pre-existing watches\n");
+      
+      _dbus_list_foreach (&watch_list->watches,
+                          (DBusForeachFunction) watch_list->remove_watch_function,
+                          watch_list->watch_data);
+    }
+
+  if (watch_list->watch_free_data_function != NULL)
+    (* watch_list->watch_free_data_function) (watch_list->watch_data);
+  
+  watch_list->add_watch_function = add_function;
+  watch_list->remove_watch_function = remove_function;
+  watch_list->watch_toggled_function = toggled_function;
+  watch_list->watch_data = data;
+  watch_list->watch_free_data_function = free_data_function;
+
+  return TRUE;
+}
+
+/**
+ * Adds a new watch to the watch list, invoking the
+ * application DBusAddWatchFunction if appropriate.
+ *
+ * @param watch_list the watch list.
+ * @param watch the watch to add.
+ * @returns #TRUE on success, #FALSE if no memory.
+ */
+dbus_bool_t
+_dbus_watch_list_add_watch (DBusWatchList *watch_list,
+                            DBusWatch     *watch)
+{
+  if (!_dbus_list_append (&watch_list->watches, watch))
+    return FALSE;
+  
+  _dbus_watch_ref (watch);
+
+  if (watch_list->add_watch_function != NULL)
+    {
+      _dbus_verbose ("Adding watch on fd %d\n",
+                     dbus_watch_get_socket (watch));
+      
+      if (!(* watch_list->add_watch_function) (watch,
+                                               watch_list->watch_data))
+        {
+          _dbus_list_remove_last (&watch_list->watches, watch);
+          _dbus_watch_unref (watch);
+          return FALSE;
+        }
+    }
+  
+  return TRUE;
+}
+
+/**
+ * Removes a watch from the watch list, invoking the
+ * application's DBusRemoveWatchFunction if appropriate.
+ *
+ * @param watch_list the watch list.
+ * @param watch the watch to remove.
+ */
+void
+_dbus_watch_list_remove_watch  (DBusWatchList *watch_list,
+                                DBusWatch     *watch)
+{
+  if (!_dbus_list_remove (&watch_list->watches, watch))
+    _dbus_assert_not_reached ("Nonexistent watch was removed");
+  
+  if (watch_list->remove_watch_function != NULL)
+    {
+      _dbus_verbose ("Removing watch on fd %d\n",
+                     dbus_watch_get_socket (watch));
+      
+      (* watch_list->remove_watch_function) (watch,
+                                             watch_list->watch_data);
+    }
+  
+  _dbus_watch_unref (watch);
+}
+
+/**
+ * Sets a watch to the given enabled state, invoking the
+ * application's DBusWatchToggledFunction if appropriate.
+ *
+ * @param watch_list the watch list.
+ * @param watch the watch to toggle.
+ * @param enabled #TRUE to enable
+ */
+void
+_dbus_watch_list_toggle_watch (DBusWatchList           *watch_list,
+                               DBusWatch               *watch,
+                               dbus_bool_t              enabled)
+{
+  enabled = !!enabled;
+  
+  if (enabled == watch->enabled)
+    return;
+
+  watch->enabled = enabled;
+  
+  if (watch_list->watch_toggled_function != NULL)
+    {
+      _dbus_verbose ("Toggling watch %p on fd %d to %d\n",
+                     watch, dbus_watch_get_socket (watch), watch->enabled);
+      
+      (* watch_list->watch_toggled_function) (watch,
+                                              watch_list->watch_data);
+    }
+}
+
+/**
+ * Sets the handler for the watch.
+ *
+ * @todo this function only exists because of the weird
+ * way connection watches are done, see the note
+ * in docs for _dbus_connection_handle_watch().
+ *
+ * @param watch the watch
+ * @param handler the new handler
+ * @param data the data
+ * @param free_data_function free data with this
+ */
+void
+_dbus_watch_set_handler (DBusWatch        *watch,
+                         DBusWatchHandler  handler,
+                         void             *data,
+                         DBusFreeFunction  free_data_function)
+{
+  if (watch->free_handler_data_function)
+    (* watch->free_handler_data_function) (watch->handler_data);
+
+  watch->handler = handler;
+  watch->handler_data = data;
+  watch->free_handler_data_function = free_data_function;
+}
+
+/** @} */
+
+/**
+ * @defgroup DBusWatch DBusWatch
+ * @ingroup  DBus
+ * @brief Object representing a file descriptor to be watched.
+ *
+ * Types and functions related to DBusWatch. A watch represents
+ * a file descriptor that the main loop needs to monitor,
+ * as in Qt's QSocketNotifier or GLib's g_io_add_watch().
+ *
+ * Use dbus_connection_set_watch_functions() or dbus_server_set_watch_functions()
+ * to be notified when libdbus needs to add or remove watches.
+ * 
+ * @{
+ */
+
+/**
+ * @typedef DBusWatch
+ *
+ * Opaque object representing a file descriptor
+ * to be watched for changes in readability,
+ * writability, or hangup.
+ */
+
+/**
+ * Deprecated former name of dbus_watch_get_unix_fd().
+ * 
+ * @param watch the DBusWatch object.
+ * @returns the file descriptor to watch.
+ */
+int
+dbus_watch_get_fd (DBusWatch *watch)
+{
+  return dbus_watch_get_unix_fd(watch);
+}
+
+/**
+ * Returns a UNIX file descriptor to be watched,
+ * which may be a pipe, socket, or other type of
+ * descriptor. On UNIX this is preferred to
+ * dbus_watch_get_socket() since it works with
+ * more kinds of #DBusWatch.
+ *
+ * Always returns -1 on Windows. On Windows you use
+ * dbus_watch_get_socket() to get a Winsock socket to watch.
+ * 
+ * @param watch the DBusWatch object.
+ * @returns the file descriptor to watch.
+ */
+int
+dbus_watch_get_unix_fd (DBusWatch *watch)
+{
+  /* FIXME remove #ifdef and do this on a lower level
+   * (watch should have set_socket and set_unix_fd and track
+   * which it has, and the transport should provide the
+   * appropriate watch type)
+   */
+#ifdef DBUS_UNIX
+  return watch->fd;
+#else
+  return -1;
+#endif
+}
+
+/**
+ * Returns a socket to be watched, on UNIX this will return -1 if our
+ * transport is not socket-based so dbus_watch_get_unix_fd() is
+ * preferred.
+ *
+ * On Windows, dbus_watch_get_unix_fd() returns -1 but this function
+ * returns a Winsock socket (assuming the transport is socket-based,
+ * as it always is for now).
+ * 
+ * @param watch the DBusWatch object.
+ * @returns the socket to watch.
+ */
+int
+dbus_watch_get_socket (DBusWatch *watch)
+{
+  return watch->fd;
+}
+
+/**
+ * Gets flags from DBusWatchFlags indicating
+ * what conditions should be monitored on the
+ * file descriptor.
+ * 
+ * The flags returned will only contain DBUS_WATCH_READABLE
+ * and DBUS_WATCH_WRITABLE, never DBUS_WATCH_HANGUP or
+ * DBUS_WATCH_ERROR; all watches implicitly include a watch
+ * for hangups, errors, and other exceptional conditions.
+ *
+ * @param watch the DBusWatch object.
+ * @returns the conditions to watch.
+ */
+unsigned int
+dbus_watch_get_flags (DBusWatch *watch)
+{
+  _dbus_assert ((watch->flags & VALID_WATCH_FLAGS) == watch->flags);
+
+  return watch->flags;
+}
+
+/**
+ * Gets data previously set with dbus_watch_set_data()
+ * or #NULL if none.
+ *
+ * @param watch the DBusWatch object.
+ * @returns previously-set data.
+ */
+void*
+dbus_watch_get_data (DBusWatch *watch)
+{
+  return watch->data;
+}
+
+/**
+ * Sets data which can be retrieved with dbus_watch_get_data().
+ * Intended for use by the DBusAddWatchFunction and
+ * DBusRemoveWatchFunction to store their own data.  For example with
+ * Qt you might store the QSocketNotifier for this watch and with GLib
+ * you might store a GSource.
+ *
+ * @param watch the DBusWatch object.
+ * @param data the data.
+ * @param free_data_function function to be called to free the data.
+ */
+void
+dbus_watch_set_data (DBusWatch        *watch,
+                     void             *data,
+                     DBusFreeFunction  free_data_function)
+{
+  _dbus_verbose ("Setting watch fd %d data to data = %p function = %p from data = %p function = %p\n",
+                 dbus_watch_get_socket (watch),
+                 data, free_data_function, watch->data, watch->free_data_function);
+  
+  if (watch->free_data_function != NULL)
+    (* watch->free_data_function) (watch->data);
+  
+  watch->data = data;
+  watch->free_data_function = free_data_function;
+}
+
+/**
+ * Returns whether a watch is enabled or not. If not
+ * enabled, it should not be polled by the main loop.
+ *
+ * @param watch the DBusWatch object
+ * @returns #TRUE if the watch is enabled
+ */
+dbus_bool_t
+dbus_watch_get_enabled (DBusWatch *watch)
+{
+  _dbus_assert (watch != NULL);
+  return watch->enabled;
+}
+
+
+/**
+ * Called to notify the D-Bus library when a previously-added watch is
+ * ready for reading or writing, or has an exception such as a hangup.
+ * 
+ * If this function returns #FALSE, then the file descriptor may still
+ * be ready for reading or writing, but more memory is needed in order
+ * to do the reading or writing. If you ignore the #FALSE return, your
+ * application may spin in a busy loop on the file descriptor until
+ * memory becomes available, but nothing more catastrophic should
+ * happen.
+ *
+ * dbus_watch_handle() cannot be called during the
+ * DBusAddWatchFunction, as the connection will not be ready to handle
+ * that watch yet.
+ * 
+ * It is not allowed to reference a DBusWatch after it has been passed
+ * to remove_function.
+ *
+ * @param watch the DBusWatch object.
+ * @param flags the poll condition using #DBusWatchFlags values
+ * @returns #FALSE if there wasn't enough memory 
+ */
+dbus_bool_t
+dbus_watch_handle (DBusWatch    *watch,
+                   unsigned int  flags)
+{
+#ifndef DBUS_DISABLE_CHECKS
+  if (watch->fd < 0 || watch->flags == 0)
+    {
+      _dbus_warn_check_failed ("%s: Watch is invalid, it should have been removed\n",
+                               _DBUS_FUNCTION_NAME);
+      return TRUE;
+    }
+#endif
+    
+  _dbus_return_val_if_fail (watch->fd >= 0 /* fails if watch was removed */, TRUE);
+  
+  _dbus_watch_sanitize_condition (watch, &flags);
+
+  if (flags == 0)
+    {
+      _dbus_verbose ("After sanitization, watch flags on fd %d were 0\n",
+                     watch->fd);
+      return TRUE;
+    }
+  else
+    return (* watch->handler) (watch, flags,
+                               watch->handler_data);
+}
+
+
+/** @} */
diff --git a/src/dbus/dbus-watch.h b/src/dbus/dbus-watch.h
new file mode 100644 (file)
index 0000000..1d8d327
--- /dev/null
@@ -0,0 +1,82 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-watch.h DBusWatch internal interfaces
+ *
+ * Copyright (C) 2002  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_WATCH_H
+#define DBUS_WATCH_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-connection.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusWatchInternals
+ * @{
+ */
+
+/* Public methods on DBusWatch are in dbus-connection.h */
+
+typedef struct DBusWatchList DBusWatchList;
+
+/** function to run when the watch is handled */
+typedef dbus_bool_t (* DBusWatchHandler) (DBusWatch    *watch,
+                                          unsigned int  flags,
+                                          void         *data);
+
+DBusWatch* _dbus_watch_new                (int               fd,
+                                           unsigned int      flags,
+                                           dbus_bool_t       enabled,
+                                           DBusWatchHandler  handler,
+                                           void             *data,
+                                           DBusFreeFunction  free_data_function);
+DBusWatch* _dbus_watch_ref                (DBusWatch        *watch);
+void       _dbus_watch_unref              (DBusWatch        *watch);
+void       _dbus_watch_invalidate         (DBusWatch        *watch);
+void       _dbus_watch_sanitize_condition (DBusWatch        *watch,
+                                           unsigned int     *condition);
+void       _dbus_watch_set_handler        (DBusWatch        *watch,
+                                           DBusWatchHandler  handler,
+                                           void             *data,
+                                           DBusFreeFunction  free_data_function);
+
+
+DBusWatchList* _dbus_watch_list_new           (void);
+void           _dbus_watch_list_free          (DBusWatchList           *watch_list);
+dbus_bool_t    _dbus_watch_list_set_functions (DBusWatchList           *watch_list,
+                                               DBusAddWatchFunction     add_function,
+                                               DBusRemoveWatchFunction  remove_function,
+                                               DBusWatchToggledFunction toggled_function,
+                                               void                    *data,
+                                               DBusFreeFunction         free_data_function);
+dbus_bool_t    _dbus_watch_list_add_watch     (DBusWatchList           *watch_list,
+                                               DBusWatch               *watch);
+void           _dbus_watch_list_remove_watch  (DBusWatchList           *watch_list,
+                                               DBusWatch               *watch);
+void           _dbus_watch_list_toggle_watch  (DBusWatchList           *watch_list,
+                                               DBusWatch               *watch,
+                                               dbus_bool_t              enabled);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_WATCH_H */
diff --git a/src/dbus/dbus.h b/src/dbus/dbus.h
new file mode 100644 (file)
index 0000000..880f21d
--- /dev/null
@@ -0,0 +1,103 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus.h  Convenience header including all other headers
+ *
+ * Copyright (C) 2002, 2003  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * 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_H
+#define DBUS_H
+
+#define DBUS_INSIDE_DBUS_H 1
+
+#include <dbus/dbus-arch-deps.h>
+#include <dbus/dbus-address.h>
+#include <dbus/dbus-bus.h>
+#include <dbus/dbus-connection.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-message.h>
+#include <dbus/dbus-misc.h>
+#include <dbus/dbus-pending-call.h>
+#include <dbus/dbus-protocol.h>
+#include <dbus/dbus-server.h>
+#include <dbus/dbus-shared.h>
+#include <dbus/dbus-signature.h>
+#include <dbus/dbus-threads.h>
+#include <dbus/dbus-types.h>
+
+#undef DBUS_INSIDE_DBUS_H
+
+/**
+ * @defgroup DBus D-Bus low-level public API
+ * @brief The low-level public API of the D-Bus library
+ *
+ * libdbus provides a low-level C API intended primarily for use by
+ * bindings to specific object systems and languages.  D-Bus is most
+ * convenient when used with the GLib bindings, Python bindings, Qt
+ * bindings, Mono bindings, and so forth.  This low-level API has a
+ * lot of complexity useful only for bindings.
+ * 
+ * @{
+ */
+
+/** @} */
+
+/**
+ * @mainpage
+ *
+ * This manual documents the <em>low-level</em> D-Bus C API. <b>If you use
+ * this low-level API directly, you're signing up for some pain.</b>
+ *
+ * Caveats aside, you might get started learning the low-level API by reading
+ * about @ref DBusConnection and @ref DBusMessage.
+ * 
+ * There are several other places to look for D-Bus information, such
+ * as the tutorial and the specification; those can be found at <a
+ * href="http://www.freedesktop.org/wiki/Software/dbus">the D-Bus
+ * website</a>. If you're interested in a sysadmin or package
+ * maintainer's perspective on the dbus-daemon itself and its
+ * configuration, be sure to check out the man pages as well.
+ *
+ * The low-level API documented in this manual deliberately lacks
+ * most convenience functions - those are left up to higher-level libraries
+ * based on frameworks such as GLib, Qt, Python, Mono, Java,
+ * etc. These higher-level libraries (often called "D-Bus bindings")
+ * have features such as object systems and main loops that allow a
+ * <em>much</em> more convenient API.
+ * 
+ * The low-level API also contains plenty of clutter to support
+ * integration with arbitrary object systems, languages, main loops,
+ * and so forth. These features add a lot of noise to the API that you
+ * probably don't care about unless you're coding a binding.
+ *
+ * This manual also contains docs for @ref DBusInternals "D-Bus internals",
+ * so you can use it to get oriented to the D-Bus source code if you're
+ * interested in patching the code. You should also read the
+ * file HACKING which comes with the source code if you plan to contribute to
+ * D-Bus.
+ *
+ * As you read the code, you can identify internal D-Bus functions
+ * because they start with an underscore ('_') character. Also, any
+ * identifier or macro that lacks a DBus, dbus_, or DBUS_ namepace
+ * prefix is internal, with a couple of exceptions such as #NULL,
+ * #TRUE, and #FALSE.
+ */
+
+#endif /* DBUS_H */
diff --git a/src/defconfig.h b/src/defconfig.h
deleted file mode 100644 (file)
index 3767fe9..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-#ifndef __DEFCONFIG_H
-#define __DEFCONFIG_H
-
-#define defconfig { \
-"# Conky, a system monitor, based on torsmo\n", \
-"#\n", \
-"# Any original torsmo code is licensed under the BSD license\n", \
-"#\n", \
-"# All code written since the fork of torsmo is licensed under the GPL\n", \
-"#\n", \
-"# Please see COPYING for details\n", \
-"#\n", \
-"# Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen\n", \
-"# Copyright (c) 2005-2010 Brenden Matthews, Philip Kovacs, et. al. (see AUTHORS)\n", \
-"# All rights reserved.\n", \
-"#\n", \
-"# This program is free software: you can redistribute it and/or modify\n", \
-"# it under the terms of the GNU General Public License as published by\n", \
-"# the Free Software Foundation, either version 3 of the License, or\n", \
-"# (at your option) any later version.\n", \
-"#\n", \
-"# This program is distributed in the hope that it will be useful,\n", \
-"# but WITHOUT ANY WARRANTY; without even the implied warranty of\n", \
-"# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n", \
-"# GNU General Public License for more details.\n", \
-"# You should have received a copy of the GNU General Public License\n", \
-"# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n", \
-"#\n", \
-"\n", \
-"alignment top_left\n", \
-"background no\n", \
-"border_width 1\n", \
-"cpu_avg_samples 2\n", \
-"default_color white\n", \
-"default_outline_color white\n", \
-"default_shade_color white\n", \
-"draw_borders no\n", \
-"draw_graph_borders yes\n", \
-"draw_outline no\n", \
-"draw_shades no\n", \
-"use_xft yes\n", \
-"xftfont DejaVu Sans Mono:size=12\n", \
-"gap_x 5\n", \
-"gap_y 60\n", \
-"minimum_size 5 5\n", \
-"net_avg_samples 2\n", \
-"no_buffers yes\n", \
-"out_to_console no\n", \
-"out_to_stderr no\n", \
-"extra_newline no\n", \
-"own_window yes\n", \
-"own_window_class Conky\n", \
-"own_window_type desktop\n", \
-"stippled_borders 0\n", \
-"update_interval 1.0\n", \
-"uppercase no\n", \
-"use_spacer none\n", \
-"show_graph_scale no\n", \
-"show_graph_range no\n", \
-"\n", \
-"TEXT\n", \
-"${scroll 16 $nodename - $sysname $kernel on $machine | }\n", \
-"$hr\n", \
-"${color grey}Uptime:$color $uptime\n", \
-"${color grey}Frequency (in MHz):$color $freq\n", \
-"${color grey}Frequency (in GHz):$color $freq_g\n", \
-"${color grey}RAM Usage:$color $mem/$memmax - $memperc% ${membar 4}\n", \
-"${color grey}Swap Usage:$color $swap/$swapmax - $swapperc% ${swapbar 4}\n", \
-"${color grey}CPU Usage:$color $cpu% ${cpubar 4}\n", \
-"${color grey}Processes:$color $processes  ${color grey}Running:$color $running_processes\n", \
-"$hr\n", \
-"${color grey}File systems:\n", \
-" / $color${fs_used /}/${fs_size /} ${fs_bar 6 /}\n", \
-"${color grey}Networking:\n", \
-"Up:$color ${upspeed eth0} ${color grey} - Down:$color ${downspeed eth0}\n", \
-"$hr\n", \
-"${color grey}Name              PID   CPU%   MEM%\n", \
-"${color lightgrey} ${top name 1} ${top pid 1} ${top cpu 1} ${top mem 1}\n", \
-"${color lightgrey} ${top name 2} ${top pid 2} ${top cpu 2} ${top mem 2}\n", \
-"${color lightgrey} ${top name 3} ${top pid 3} ${top cpu 3} ${top mem 3}\n", \
-"${color lightgrey} ${top name 4} ${top pid 4} ${top cpu 4} ${top mem 4}\n", \
-NULL }
-
-#define print_defconfig() { \
-       const char **__sp, *__s[] = defconfig; \
-       for (__sp = __s; *__sp; __sp++) \
-               printf("%s", *__sp); \
-}
-
-#endif /* __DEFCONFIG_H */
index 49e4768..abd60e6 100644 (file)
@@ -84,6 +84,8 @@
 #include <iwlib.h>
 #endif
 
+#include <dbus/dbus.h>
+
 struct sysfs {
        int fd;
        int arg;
@@ -1581,6 +1583,12 @@ static int acpi_design_capacity[MAX_BATTERY_COUNT];
 //eg 4100
 static int last_battery_volts[MAX_BATTERY_COUNT];
 
+//eg 78
+static unsigned char last_cell_radio_dbm;
+
+//eg 100
+static unsigned char last_cell_radio_percent;
+
 //eg 35
 static int last_battery_temp[MAX_BATTERY_COUNT];
 
@@ -1628,6 +1636,134 @@ int get_battery_idx(const char *bat)
        return idx;
 }
 
+//void set_return_value(char *buffer, unsigned int n, int item, int idx);
+
+static int dbus_queue = 0;
+
+void set_dbus_retval(char *buffer, unsigned int n, int item);
+
+DBusConnection *connection;
+DBusError error;
+DBusMessage *message;
+DBusMessageIter iter;
+DBusBusType type;
+int message_type;
+DBusMessage *reply;
+void get_dbus_stuff(char *buffer,unsigned int intMax_length, int item)
+{
+       char method[128];
+       char path[128];
+       char dest[128];
+       if (dbus_queue > 0)
+       {
+               set_dbus_retval(buffer, intMax_length, item);
+               //snprintf(buffer,intMax_length,"%i",last_cell_radio_dbm);
+               return;
+       }
+       dbus_queue++;//prevent a queue from forming on these requests...
+//fetch data from dbus, store in here as last_cell_radio_dbm
+//return into buffer
+
+       type = DBUS_BUS_SYSTEM;
+       message_type = DBUS_MESSAGE_TYPE_METHOD_CALL;
+//     print_reply = TRUE;
+//     print_reply_literal = FALSE;
+       int reply_timeout_ms = 5000;
+       dbus_error_init (&error);
+       connection = dbus_bus_get (type, &error);
+       if (connection == NULL)
+    {
+               fprintf (stderr, "Failed to open connection to %s message bus: %s\n",
+               (type == DBUS_BUS_SYSTEM) ? "system" : "session",
+               error.message);
+      dbus_error_free (&error);
+      exit (1);
+    }
+       switch(item){
+       case DBUS_CELL_DBM:
+               snprintf(method,127,"get_signal_strength");
+               snprintf(path,127,"/com/nokia/phone/net");
+               snprintf(dest,127,"com.nokia.phone.net");
+               message = dbus_message_new_method_call (dest,path,"Phone.Net",method);
+               dbus_message_set_auto_start (message, TRUE);
+               break;
+       case DBUS_CELL_PERCENT:
+               snprintf(method,127,"get_signal_strength");
+               snprintf(path,127,"/com/nokia/phone/net");
+               snprintf(dest,127,"com.nokia.phone.net");
+               message = dbus_message_new_method_call (dest,path,"Phone.Net",method);
+               dbus_message_set_auto_start (message, TRUE);
+               break;
+       default:
+               fprintf (stderr, "invalid item type in get_dbus_stuff");
+               break;
+       }
+       if (message == NULL)
+       {
+         fprintf (stderr, "Couldn't allocate D-Bus message\n");
+         exit (1);
+       }
+       if (!dbus_message_set_destination (message, dest))
+       {
+         fprintf (stderr, "Not enough memory\n");
+         exit (1);
+       }
+       dbus_message_iter_init_append (message, &iter);
+       dbus_error_init (&error);
+       reply = dbus_connection_send_with_reply_and_block (connection, message, reply_timeout_ms, &error);
+       if (dbus_error_is_set (&error))
+       {
+         fprintf (stderr, "Error %s: %s\n",error.name,error.message);
+         //exit (1);//if we set timeout to 30s or something i guess it's okay to exit on "no reply" cuz something is fu*ked;
+       }
+       if (reply)
+       {
+               DBusMessageIter iter;
+               dbus_message_iter_init (reply, &iter);
+               //int type = dbus_message_iter_get_arg_type(&iter);
+               int current_fieldnumber = 0;
+               while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID)
+               {
+                       //fprintf (stderr,"dbus-monitor too dumb to decipher arg type '%c'\n", type);
+                       current_fieldnumber++;
+                       if (current_fieldnumber == 1)
+                       {
+                               unsigned char val;
+                               dbus_message_iter_get_basic(&iter, &val);
+                               last_cell_radio_percent = val;
+                       }
+                       if (current_fieldnumber == 2)
+                       {
+                               unsigned char val;
+                               dbus_message_iter_get_basic(&iter, &val);
+                               last_cell_radio_dbm = val;
+                       }
+                       dbus_message_iter_next (&iter);
+               }
+
+               dbus_message_unref (reply);
+       }
+       set_dbus_retval(buffer, intMax_length, item);
+       dbus_message_unref (message);
+       dbus_connection_unref (connection);
+       dbus_queue = 0;//reset to zero now that complete
+}
+
+void set_dbus_retval(char *buffer, unsigned int intMax_length, int item)
+{
+       switch (item) {
+               case DBUS_CELL_DBM:
+                       snprintf(buffer, intMax_length, "%d", last_cell_radio_dbm);
+                       break;
+               case DBUS_CELL_PERCENT:
+                       snprintf(buffer, intMax_length, "%d", last_cell_radio_percent);
+                       break;
+               default:
+                       fprintf (stderr, "invalid item type in set_dbus_retval");
+                       break;
+       }
+}
+
 void set_return_value(char *buffer, unsigned int n, int item, int idx);
 
 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
@@ -1708,7 +1844,7 @@ void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
 
                fclose(sysfs_bat_fp[idx]);
                sysfs_bat_fp[idx] = NULL;
-               
+
                last_battery_volts[idx] = voltage;
                last_battery_temp[idx] = temp;
 
@@ -1745,7 +1881,7 @@ void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
 //             }
                /* discharging */
                else if (present_rate > 0) {
-                         
+
                                /* e.g. discharging 35% */
                                snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%", remaining_capacity);
 
@@ -1753,7 +1889,7 @@ void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
                                        sizeof(last_battery_time_str[idx]) - 1, "unknown");
                                        /* e.g. 1h 12m */
 //                             format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
-//                                           (long) (((float) remaining_capacity / present_rate) * 3600));             
+//                                           (long) (((float) remaining_capacity / present_rate) * 3600));
 //                      else {
 //                             snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
 //                                     "discharging %d%%",
@@ -1764,10 +1900,10 @@ void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
                }
                /* charged */
                /* thanks to Lukas Zapletal <lzap@seznam.cz> */
-               
+
                 if (remaining_capacity == 100)
                                strcpy(last_battery_str[idx], "charged");
-                                       
+
 //             else if (strncmp(charging_state, "Charged", 64) == 0 || strncmp(charging_state, "Full", 64) == 0) {
 //                             /* Below happens with the second battery on my X40,
 //                              * when the second one is empty and the first one
@@ -2037,7 +2173,7 @@ int get_battery_perct(const char *bat)
                                break;
                        if (strncmp(buf, "POWER_SUPPLY_CAPACITY=", 22) == 0) {
                                sscanf(buf, "POWER_SUPPLY_CAPACITY=%d", &remaining_capacity);
-                               
+
 //                     if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
 //                             sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
 //                     } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
@@ -2052,7 +2188,7 @@ int get_battery_perct(const char *bat)
                fclose(sysfs_bat_fp[idx]);
                sysfs_bat_fp[idx] = NULL;
 
-       } 
+       }
 //     else if (acpi_bat_fp[idx] != NULL) {
 //             /* ACPI */
 //             /* read last full capacity if it's zero */
@@ -2097,7 +2233,7 @@ int get_battery_perct(const char *bat)
                return 0;
        }
        /* compute the battery percentage */
-       last_battery_perct[idx] = 
+       last_battery_perct[idx] =
                remaining_capacity;
                //(int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
        //if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
index 4581c91..53a71fb 100644 (file)
@@ -49,6 +49,8 @@ enum text_object_type {
        OBJ_battery_percent,
        OBJ_battery_bar,
        OBJ_battery_short,
+       OBJ_cell_radio_dbm,
+       OBJ_cell_radio_percent,
 #endif /* !__OpenBSD__ */
        OBJ_buffers,
        OBJ_cached,