[xiph-commits] r13566 - in experimental/moritz: . iceadmin iceadmin/compat iceadmin/compat/sys iceadmin/doc iceadmin/include iceadmin/lib iceadmin/src

moritz at svn.xiph.org moritz at svn.xiph.org
Sat Aug 18 05:05:21 PDT 2007


Author: moritz
Date: 2007-08-18 05:05:20 -0700 (Sat, 18 Aug 2007)
New Revision: 13566

Added:
   experimental/moritz/iceadmin/
   experimental/moritz/iceadmin/COPYING
   experimental/moritz/iceadmin/Makefile.am
   experimental/moritz/iceadmin/NEWS
   experimental/moritz/iceadmin/README
   experimental/moritz/iceadmin/autogen.sh
   experimental/moritz/iceadmin/build-aux/
   experimental/moritz/iceadmin/compat/
   experimental/moritz/iceadmin/compat/Makefile.am
   experimental/moritz/iceadmin/compat/sys/
   experimental/moritz/iceadmin/compat/sys/Makefile.am
   experimental/moritz/iceadmin/compat/sys/tree.3
   experimental/moritz/iceadmin/compat/sys/tree.h
   experimental/moritz/iceadmin/configure.in
   experimental/moritz/iceadmin/doc/
   experimental/moritz/iceadmin/doc/Makefile.am
   experimental/moritz/iceadmin/iceadmin.pc.in
   experimental/moritz/iceadmin/include/
   experimental/moritz/iceadmin/include/Makefile.am
   experimental/moritz/iceadmin/include/config.h.in
   experimental/moritz/iceadmin/include/iceadmin.h
   experimental/moritz/iceadmin/lib/
   experimental/moritz/iceadmin/lib/Makefile.am
   experimental/moritz/iceadmin/lib/b64_ntop.txt
   experimental/moritz/iceadmin/lib/error.c
   experimental/moritz/iceadmin/lib/error.h
   experimental/moritz/iceadmin/lib/iceadmin_internal.c
   experimental/moritz/iceadmin/lib/iceadmin_internal.h
   experimental/moritz/iceadmin/lib/libiceadmin.c
   experimental/moritz/iceadmin/lib/strlcat.c
   experimental/moritz/iceadmin/lib/strnstr.txt
   experimental/moritz/iceadmin/lib/xalloc.c
   experimental/moritz/iceadmin/lib/xalloc.h
   experimental/moritz/iceadmin/src/
   experimental/moritz/iceadmin/src/Makefile.am
   experimental/moritz/iceadmin/src/iceadmin.c
Log:
Import the (still unfinished) [lib]iceadmin remote administration library
and utility for Icecast, to avoid losing work. The library already supports all
documented admin features of Icecast, but there appears to be a bit more work
to be done, still.


Added: experimental/moritz/iceadmin/COPYING
===================================================================
--- experimental/moritz/iceadmin/COPYING	                        (rev 0)
+++ experimental/moritz/iceadmin/COPYING	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,3 @@
+IceAdmin is Copyright (C) 2007 Moritz Grimm <mdgrimm at gmx.net>
+
+IceAdmin does not exist, yet.

Added: experimental/moritz/iceadmin/Makefile.am
===================================================================
--- experimental/moritz/iceadmin/Makefile.am	                        (rev 0)
+++ experimental/moritz/iceadmin/Makefile.am	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,15 @@
+AUTOMAKE_OPTIONS = 1.9 foreign
+
+SUBDIRS =	compat doc include lib src
+
+dist_doc_DATA = COPYING NEWS README
+
+pkgconfigdir =	$(libdir)/pkgconfig
+pkgconfig_DATA = iceadmin.pc
+
+CLEANFILES =	core *.core *~ .*~
+
+.PHONY: 	snapshot
+
+snapshot:
+	${MAKE} distcheck distdir=${PACKAGE}-snapshot-`date +'%Y%m%d'`

Added: experimental/moritz/iceadmin/NEWS
===================================================================
--- experimental/moritz/iceadmin/NEWS	                        (rev 0)
+++ experimental/moritz/iceadmin/NEWS	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,3 @@
+IceAdmin 0.1:
+
+  * Initial version.

Added: experimental/moritz/iceadmin/README
===================================================================
--- experimental/moritz/iceadmin/README	                        (rev 0)
+++ experimental/moritz/iceadmin/README	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1 @@
+Nothing here, yet.

Added: experimental/moritz/iceadmin/autogen.sh
===================================================================
--- experimental/moritz/iceadmin/autogen.sh	                        (rev 0)
+++ experimental/moritz/iceadmin/autogen.sh	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"
+
+if [ ! -f "./`basename $0`" ]; then
+	echo "Please chdir into `basename $0`'s directory first."
+	exit 1
+fi
+
+AUTOCONF_VERSION=2.61 AUTOMAKE_VERSION=1.9 aclocal
+AUTOCONF_VERSION=2.61 AUTOMAKE_VERSION=1.9 autoconf
+AUTOCONF_VERSION=2.61 AUTOMAKE_VERSION=1.9 autoheader
+AUTOCONF_VERSION=2.61 AUTOMAKE_VERSION=1.9 libtoolize --automake -c -f
+AUTOCONF_VERSION=2.61 AUTOMAKE_VERSION=1.9 automake -a -c
+rm -r autom4te.cache


Property changes on: experimental/moritz/iceadmin/autogen.sh
___________________________________________________________________
Name: svn:executable
   + 

Added: experimental/moritz/iceadmin/compat/Makefile.am
===================================================================
--- experimental/moritz/iceadmin/compat/Makefile.am	                        (rev 0)
+++ experimental/moritz/iceadmin/compat/Makefile.am	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,5 @@
+AUTOMAKE_OPTIONS = 1.9 foreign
+
+SUBDIRS =	sys
+
+CLEANFILES =	*~ *.core core

Added: experimental/moritz/iceadmin/compat/sys/Makefile.am
===================================================================
--- experimental/moritz/iceadmin/compat/sys/Makefile.am	                        (rev 0)
+++ experimental/moritz/iceadmin/compat/sys/Makefile.am	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,5 @@
+AUTOMAKE_OPTIONS = 1.9 foreign
+
+EXTRA_DIST =	tree.h tree.3
+
+CLEANFILES =	*~ *.core core

Added: experimental/moritz/iceadmin/compat/sys/tree.3
===================================================================
--- experimental/moritz/iceadmin/compat/sys/tree.3	                        (rev 0)
+++ experimental/moritz/iceadmin/compat/sys/tree.3	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,534 @@
+.\"	$OpenBSD: tree.3,v 1.13 2007/05/31 19:19:48 jmc Exp $
+.\"/*
+.\" * Copyright 2002 Niels Provos <provos at citi.umich.edu>
+.\" * All rights reserved.
+.\" *
+.\" * Redistribution and use in source and binary forms, with or without
+.\" * modification, are permitted provided that the following conditions
+.\" * are met:
+.\" * 1. Redistributions of source code must retain the above copyright
+.\" *    notice, this list of conditions and the following disclaimer.
+.\" * 2. Redistributions in binary form must reproduce the above copyright
+.\" *    notice, this list of conditions and the following disclaimer in the
+.\" *    documentation and/or other materials provided with the distribution.
+.\" * 3. All advertising materials mentioning features or use of this software
+.\" *    must display the following acknowledgement:
+.\" *      This product includes software developed by Niels Provos.
+.\" * 4. The name of the author may not be used to endorse or promote products
+.\" *    derived from this software without specific prior written permission.
+.\" *
+.\" * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" */
+.Dd $Mdocdate$
+.Dt TREE 3
+.Os
+.Sh NAME
+.Nm SPLAY_PROTOTYPE ,
+.Nm SPLAY_GENERATE ,
+.Nm SPLAY_ENTRY ,
+.Nm SPLAY_HEAD ,
+.Nm SPLAY_INITIALIZER ,
+.Nm SPLAY_ROOT ,
+.Nm SPLAY_EMPTY ,
+.Nm SPLAY_NEXT ,
+.Nm SPLAY_MIN ,
+.Nm SPLAY_MAX ,
+.Nm SPLAY_FIND ,
+.Nm SPLAY_LEFT ,
+.Nm SPLAY_RIGHT ,
+.Nm SPLAY_FOREACH ,
+.Nm SPLAY_INIT ,
+.Nm SPLAY_INSERT ,
+.Nm SPLAY_REMOVE ,
+.Nm RB_PROTOTYPE ,
+.Nm RB_GENERATE ,
+.Nm RB_ENTRY ,
+.Nm RB_HEAD ,
+.Nm RB_INITIALIZER ,
+.Nm RB_ROOT ,
+.Nm RB_EMPTY ,
+.Nm RB_NEXT ,
+.Nm RB_MIN ,
+.Nm RB_MAX ,
+.Nm RB_FIND ,
+.Nm RB_LEFT ,
+.Nm RB_RIGHT ,
+.Nm RB_PARENT ,
+.Nm RB_FOREACH ,
+.Nm RB_INIT ,
+.Nm RB_INSERT ,
+.Nm RB_REMOVE
+.Nd "implementations of splay and red-black trees"
+.Sh SYNOPSIS
+.Fd #include <sys/tree.h>
+.Pp
+.Fn SPLAY_PROTOTYPE "NAME" "TYPE" "FIELD" "CMP"
+.Fn SPLAY_GENERATE "NAME" "TYPE" "FIELD" "CMP"
+.Fn SPLAY_ENTRY "TYPE"
+.Fn SPLAY_HEAD "HEADNAME" "TYPE"
+.Ft "struct TYPE *"
+.Fn SPLAY_INITIALIZER "SPLAY_HEAD *head"
+.Fn SPLAY_ROOT "SPLAY_HEAD *head"
+.Ft "bool"
+.Fn SPLAY_EMPTY "SPLAY_HEAD *head"
+.Ft "struct TYPE *"
+.Fn SPLAY_NEXT "NAME" "SPLAY_HEAD *head" "struct TYPE *elm"
+.Ft "struct TYPE *"
+.Fn SPLAY_MIN "NAME" "SPLAY_HEAD *head"
+.Ft "struct TYPE *"
+.Fn SPLAY_MAX "NAME" "SPLAY_HEAD *head"
+.Ft "struct TYPE *"
+.Fn SPLAY_FIND "NAME" "SPLAY_HEAD *head" "struct TYPE *elm"
+.Ft "struct TYPE *"
+.Fn SPLAY_LEFT "struct TYPE *elm" "SPLAY_ENTRY NAME"
+.Ft "struct TYPE *"
+.Fn SPLAY_RIGHT "struct TYPE *elm" "SPLAY_ENTRY NAME"
+.Fn SPLAY_FOREACH "VARNAME" "NAME" "SPLAY_HEAD *head"
+.Ft void
+.Fn SPLAY_INIT "SPLAY_HEAD *head"
+.Ft "struct TYPE *"
+.Fn SPLAY_INSERT "NAME" "SPLAY_HEAD *head" "struct TYPE *elm"
+.Ft "struct TYPE *"
+.Fn SPLAY_REMOVE "NAME" "SPLAY_HEAD *head" "struct TYPE *elm"
+.Pp
+.Fn RB_PROTOTYPE "NAME" "TYPE" "FIELD" "CMP"
+.Fn RB_GENERATE "NAME" "TYPE" "FIELD" "CMP"
+.Fn RB_ENTRY "TYPE"
+.Fn RB_HEAD "HEADNAME" "TYPE"
+.Fn RB_INITIALIZER "RB_HEAD *head"
+.Ft "struct TYPE *"
+.Fn RB_ROOT "RB_HEAD *head"
+.Ft "bool"
+.Fn RB_EMPTY "RB_HEAD *head"
+.Ft "struct TYPE *"
+.Fn RB_NEXT "NAME" "RB_HEAD *head" "struct TYPE *elm"
+.Ft "struct TYPE *"
+.Fn RB_MIN "NAME" "RB_HEAD *head"
+.Ft "struct TYPE *"
+.Fn RB_MAX "NAME" "RB_HEAD *head"
+.Ft "struct TYPE *"
+.Fn RB_FIND "NAME" "RB_HEAD *head" "struct TYPE *elm"
+.Ft "struct TYPE *"
+.Fn RB_LEFT "struct TYPE *elm" "RB_ENTRY NAME"
+.Ft "struct TYPE *"
+.Fn RB_RIGHT "struct TYPE *elm" "RB_ENTRY NAME"
+.Ft "struct TYPE *"
+.Fn RB_PARENT "struct TYPE *elm" "RB_ENTRY NAME"
+.Fn RB_FOREACH "VARNAME" "NAME" "RB_HEAD *head"
+.Ft void
+.Fn RB_INIT "RB_HEAD *head"
+.Ft "struct TYPE *"
+.Fn RB_INSERT "NAME" "RB_HEAD *head" "struct TYPE *elm"
+.Ft "struct TYPE *"
+.Fn RB_REMOVE "NAME" "RB_HEAD *head" "struct TYPE *elm"
+.Sh DESCRIPTION
+These macros define data structures for different types of trees:
+splay trees and red-black trees.
+.Pp
+In the macro definitions,
+.Fa TYPE
+is the name tag of a user defined structure that must contain a field of type
+.Li SPLAY_ENTRY ,
+or
+.Li RB_ENTRY ,
+named
+.Fa ENTRYNAME .
+The argument
+.Fa HEADNAME
+is the name tag of a user defined structure that must be declared
+using the macros
+.Fn SPLAY_HEAD
+or
+.Fn RB_HEAD .
+The argument
+.Fa NAME
+has to be a unique name prefix for every tree that is defined.
+.Pp
+The function prototypes are declared with either
+.Li SPLAY_PROTOTYPE
+or
+.Li RB_PROTOTYPE .
+The function bodies are generated with either
+.Li SPLAY_GENERATE
+or
+.Li RB_GENERATE .
+See the examples below for further explanation of how these macros are used.
+.Sh SPLAY TREES
+A splay tree is a self-organizing data structure.
+Every operation on the tree causes a splay to happen.
+The splay moves the requested node to the root of the tree and partly
+rebalances it.
+.Pp
+This has the benefit that request locality causes faster lookups as
+the requested nodes move to the top of the tree.
+On the other hand, every lookup causes memory writes.
+.Pp
+The Balance Theorem bounds the total access time for m operations
+and n inserts on an initially empty tree as O((m + n)lg n).
+The amortized cost for a sequence of m accesses to a splay tree is O(lg n).
+.Pp
+A splay tree is headed by a structure defined by the
+.Fn SPLAY_HEAD
+macro.
+A
+.Fa SPLAY_HEAD
+structure is declared as follows:
+.Bd -literal -offset indent
+SPLAY_HEAD(HEADNAME, TYPE) head;
+.Ed
+.Pp
+where
+.Fa HEADNAME
+is the name of the structure to be defined, and struct
+.Fa TYPE
+is the type of the elements to be inserted into the tree.
+.Pp
+The
+.Fn SPLAY_ENTRY
+macro declares a structure that allows elements to be connected in the tree.
+.Pp
+In order to use the functions that manipulate the tree structure,
+their prototypes need to be declared with the
+.Fn SPLAY_PROTOTYPE
+macro,
+where
+.Fa NAME
+is a unique identifier for this particular tree.
+The
+.Fa TYPE
+argument is the type of the structure that is being managed
+by the tree.
+The
+.Fa FIELD
+argument is the name of the element defined by
+.Fn SPLAY_ENTRY .
+.Pp
+The function bodies are generated with the
+.Fn SPLAY_GENERATE
+macro.
+It takes the same arguments as the
+.Fn SPLAY_PROTOTYPE
+macro, but should be used only once.
+.Pp
+Finally,
+the
+.Fa CMP
+argument is the name of a function used to compare trees noded
+with each other.
+The function takes two arguments of type
+.Fa "struct TYPE *" .
+If the first argument is smaller than the second, the function returns a
+value smaller than zero.
+If they are equal, the function returns zero.
+Otherwise, it should return a value greater than zero.
+The compare function defines the order of the tree elements.
+.Pp
+The
+.Fn SPLAY_INIT
+macro initializes the tree referenced by
+.Fa head .
+.Pp
+The splay tree can also be initialized statically by using the
+.Fn SPLAY_INITIALIZER
+macro like this:
+.Bd -literal -offset indent
+SPLAY_HEAD(HEADNAME, TYPE) head = SPLAY_INITIALIZER(&head);
+.Ed
+.Pp
+The
+.Fn SPLAY_INSERT
+macro inserts the new element
+.Fa elm
+into the tree.
+.Pp
+The
+.Fn SPLAY_REMOVE
+macro removes the element
+.Fa elm
+from the tree pointed by
+.Fa head .
+.Pp
+The
+.Fn SPLAY_FIND
+macro can be used to find a particular element in the tree.
+.Bd -literal -offset indent
+struct TYPE find, *res;
+find.key = 30;
+res = SPLAY_FIND(NAME, &head, &find);
+.Ed
+.Pp
+The
+.Fn SPLAY_ROOT ,
+.Fn SPLAY_MIN ,
+.Fn SPLAY_MAX ,
+and
+.Fn SPLAY_NEXT
+macros can be used to traverse the tree:
+.Bd -literal -offset indent
+for (np = SPLAY_MIN(NAME, &head); np != NULL; np = SPLAY_NEXT(NAME, &head, np))
+.Ed
+.Pp
+Or, for simplicity, one can use the
+.Fn SPLAY_FOREACH
+macro:
+.Bd -literal -offset indent
+SPLAY_FOREACH(np, NAME, &head)
+.Ed
+.Pp
+The
+.Fn SPLAY_EMPTY
+macro should be used to check whether a splay tree is empty.
+.Sh RED-BLACK TREES
+A red-black tree is a binary search tree with the node color as an
+extra attribute.
+It fulfills a set of conditions:
+.Pp
+.Bl -enum -compact -offset indent
+.It
+every search path from the root to a leaf consists of the same number of
+black nodes,
+.It
+each red node (except for the root) has a black parent,
+.It
+each leaf node is black.
+.El
+.Pp
+Every operation on a red-black tree is bounded as O(lg n).
+The maximum height of a red-black tree is 2lg (n+1).
+.Pp
+A red-black tree is headed by a structure defined by the
+.Fn RB_HEAD
+macro.
+A
+.Fa RB_HEAD
+structure is declared as follows:
+.Bd -literal -offset indent
+RB_HEAD(HEADNAME, TYPE) head;
+.Ed
+.Pp
+where
+.Fa HEADNAME
+is the name of the structure to be defined, and struct
+.Fa TYPE
+is the type of the elements to be inserted into the tree.
+.Pp
+The
+.Fn RB_ENTRY
+macro declares a structure that allows elements to be connected in the tree.
+.Pp
+In order to use the functions that manipulate the tree structure,
+their prototypes need to be declared with the
+.Fn RB_PROTOTYPE
+macro,
+where
+.Fa NAME
+is a unique identifier for this particular tree.
+The
+.Fa TYPE
+argument is the type of the structure that is being managed
+by the tree.
+The
+.Fa FIELD
+argument is the name of the element defined by
+.Fn RB_ENTRY .
+.Pp
+The function bodies are generated with the
+.Fn RB_GENERATE
+macro.
+It takes the same arguments as the
+.Fn RB_PROTOTYPE
+macro, but should be used only once.
+.Pp
+Finally,
+the
+.Fa CMP
+argument is the name of a function used to compare trees noded
+with each other.
+The function takes two arguments of type
+.Fa "struct TYPE *" .
+If the first argument is smaller than the second, the function returns a
+value smaller than zero.
+If they are equal, the function returns zero.
+Otherwise, it should return a value greater than zero.
+The compare function defines the order of the tree elements.
+.Pp
+The
+.Fn RB_INIT
+macro initializes the tree referenced by
+.Fa head .
+.Pp
+The red-black tree can also be initialized statically by using the
+.Fn RB_INITIALIZER
+macro like this:
+.Bd -literal -offset indent
+RB_HEAD(HEADNAME, TYPE) head = RB_INITIALIZER(&head);
+.Ed
+.Pp
+The
+.Fn RB_INSERT
+macro inserts the new element
+.Fa elm
+into the tree.
+.Pp
+The
+.Fn RB_REMOVE
+macro removes the element
+.Fa elm
+from the tree pointed by
+.Fa head .
+.Pp
+The
+.Fn RB_FIND
+macro can be used to find a particular element in the tree.
+.Bd -literal -offset indent
+struct TYPE find, *res;
+find.key = 30;
+res = RB_FIND(NAME, &head, &find);
+.Ed
+.Pp
+The
+.Fn RB_ROOT ,
+.Fn RB_MIN ,
+.Fn RB_MAX ,
+and
+.Fn RB_NEXT
+macros can be used to traverse the tree:
+.Bd -literal -offset indent
+for (np = RB_MIN(NAME, &head); np != NULL; np = RB_NEXT(NAME, &head, np))
+.Ed
+.Pp
+Or, for simplicity, one can use the
+.Fn RB_FOREACH
+macro:
+.Bd -literal -offset indent
+RB_FOREACH(np, NAME, &head)
+.Ed
+.Pp
+The
+.Fn RB_EMPTY
+macro should be used to check whether a red-black tree is empty.
+.Sh EXAMPLES
+The following example demonstrates how to declare a red-black tree
+holding integers.
+Values are inserted into it and the contents of the tree are printed
+in order.
+Lastly, the internal structure of the tree is printed.
+.Bd -literal -offset 3n
+#include <sys/tree.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+struct node {
+	RB_ENTRY(node) entry;
+	int i;
+};
+
+int
+intcmp(struct node *e1, struct node *e2)
+{
+	return (e1->i - e2->i);
+}
+
+RB_HEAD(inttree, node) head = RB_INITIALIZER(&head);
+RB_GENERATE(inttree, node, entry, intcmp)
+
+int testdata[] = {
+	20, 16, 17, 13, 3, 6, 1, 8, 2, 4, 10, 19, 5, 9, 12, 15, 18,
+	7, 11, 14
+};
+
+void
+print_tree(struct node *n)
+{
+	struct node *left, *right;
+
+	if (n == NULL) {
+		printf("nil");
+		return;
+	}
+	left = RB_LEFT(n, entry);
+	right = RB_RIGHT(n, entry);
+	if (left == NULL && right == NULL)
+		printf("%d", n->i);
+	else {
+		printf("%d(", n->i);
+		print_tree(left);
+		printf(",");
+		print_tree(right);
+		printf(")");
+	}
+}
+
+int
+main()
+{
+	int i;
+	struct node *n;
+
+	for (i = 0; i < sizeof(testdata) / sizeof(testdata[0]); i++) {
+		if ((n = malloc(sizeof(struct node))) == NULL)
+			err(1, NULL);
+		n->i = testdata[i];
+		RB_INSERT(inttree, &head, n);
+	}
+
+	RB_FOREACH(n, inttree, &head) {
+		printf("%d\en", n->i);
+	}
+	print_tree(RB_ROOT(&head));
+	printf("\en");
+	return (0);
+}
+.Ed
+.Sh NOTES
+Trying to free a tree in the following way is a common error:
+.Bd -literal -offset indent
+SPLAY_FOREACH(var, NAME, &head) {
+	SPLAY_REMOVE(NAME, &head, var);
+	free(var);
+}
+free(head);
+.Ed
+.Pp
+Since
+.Va var
+is free'd, the
+.Fn FOREACH
+macro refers to a pointer that may have been reallocated already.
+Proper code needs a second variable.
+.Bd -literal -offset indent
+for (var = SPLAY_MIN(NAME, &head); var != NULL; var = nxt) {
+	nxt = SPLAY_NEXT(NAME, &head, var);
+	SPLAY_REMOVE(NAME, &head, var);
+	free(var);
+}
+.Ed
+.Pp
+Both
+.Fn RB_INSERT
+and
+.Fn SPLAY_INSERT
+return
+.Va NULL
+if the element was inserted in the tree successfully, otherwise they
+return a pointer to the element with the colliding key.
+.Pp
+Accordingly,
+.Fn RB_REMOVE
+and
+.Fn SPLAY_REMOVE
+return the pointer to the removed element, otherwise they return
+.Va NULL
+to indicate an error.
+.Sh AUTHORS
+The author of the tree macros is Niels Provos.

Added: experimental/moritz/iceadmin/compat/sys/tree.h
===================================================================
--- experimental/moritz/iceadmin/compat/sys/tree.h	                        (rev 0)
+++ experimental/moritz/iceadmin/compat/sys/tree.h	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,677 @@
+/*	$OpenBSD: tree.h,v 1.9 2004/11/24 18:10:42 tdeval Exp $	*/
+/*
+ * Copyright 2002 Niels Provos <provos at citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef	_SYS_TREE_H_
+#define	_SYS_TREE_H_
+
+/*
+ * This file defines data structures for different types of trees:
+ * splay trees and red-black trees.
+ *
+ * A splay tree is a self-organizing data structure.  Every operation
+ * on the tree causes a splay to happen.  The splay moves the requested
+ * node to the root of the tree and partly rebalances it.
+ *
+ * This has the benefit that request locality causes faster lookups as
+ * the requested nodes move to the top of the tree.  On the other hand,
+ * every lookup causes memory writes.
+ *
+ * The Balance Theorem bounds the total access time for m operations
+ * and n inserts on an initially empty tree as O((m + n)lg n).  The
+ * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
+ *
+ * A red-black tree is a binary search tree with the node color as an
+ * extra attribute.  It fulfills a set of conditions:
+ *	- every search path from the root to a leaf consists of the
+ *	  same number of black nodes,
+ *	- each red node (except for the root) has a black parent,
+ *	- each leaf node is black.
+ *
+ * Every operation on a red-black tree is bounded as O(lg n).
+ * The maximum height of a red-black tree is 2lg (n+1).
+ */
+
+#define SPLAY_HEAD(name, type)						\
+struct name {								\
+	struct type *sph_root; /* root of the tree */			\
+}
+
+#define SPLAY_INITIALIZER(root)						\
+	{ NULL }
+
+#define SPLAY_INIT(root) do {						\
+	(root)->sph_root = NULL;					\
+} while (0)
+
+#define SPLAY_ENTRY(type)						\
+struct {								\
+	struct type *spe_left; /* left element */			\
+	struct type *spe_right; /* right element */			\
+}
+
+#define SPLAY_LEFT(elm, field)		(elm)->field.spe_left
+#define SPLAY_RIGHT(elm, field)		(elm)->field.spe_right
+#define SPLAY_ROOT(head)		(head)->sph_root
+#define SPLAY_EMPTY(head)		(SPLAY_ROOT(head) == NULL)
+
+/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
+#define SPLAY_ROTATE_RIGHT(head, tmp, field) do {			\
+	SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field);	\
+	SPLAY_RIGHT(tmp, field) = (head)->sph_root;			\
+	(head)->sph_root = tmp;						\
+} while (0)
+	
+#define SPLAY_ROTATE_LEFT(head, tmp, field) do {			\
+	SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field);	\
+	SPLAY_LEFT(tmp, field) = (head)->sph_root;			\
+	(head)->sph_root = tmp;						\
+} while (0)
+
+#define SPLAY_LINKLEFT(head, tmp, field) do {				\
+	SPLAY_LEFT(tmp, field) = (head)->sph_root;			\
+	tmp = (head)->sph_root;						\
+	(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);		\
+} while (0)
+
+#define SPLAY_LINKRIGHT(head, tmp, field) do {				\
+	SPLAY_RIGHT(tmp, field) = (head)->sph_root;			\
+	tmp = (head)->sph_root;						\
+	(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);	\
+} while (0)
+
+#define SPLAY_ASSEMBLE(head, node, left, right, field) do {		\
+	SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field);	\
+	SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
+	SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field);	\
+	SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field);	\
+} while (0)
+
+/* Generates prototypes and inline functions */
+
+#define SPLAY_PROTOTYPE(name, type, field, cmp)				\
+void name##_SPLAY(struct name *, struct type *);			\
+void name##_SPLAY_MINMAX(struct name *, int);				\
+struct type *name##_SPLAY_INSERT(struct name *, struct type *);		\
+struct type *name##_SPLAY_REMOVE(struct name *, struct type *);		\
+									\
+/* Finds the node with the same key as elm */				\
+static __inline struct type *						\
+name##_SPLAY_FIND(struct name *head, struct type *elm)			\
+{									\
+	if (SPLAY_EMPTY(head))						\
+		return(NULL);						\
+	name##_SPLAY(head, elm);					\
+	if ((cmp)(elm, (head)->sph_root) == 0)				\
+		return (head->sph_root);				\
+	return (NULL);							\
+}									\
+									\
+static __inline struct type *						\
+name##_SPLAY_NEXT(struct name *head, struct type *elm)			\
+{									\
+	name##_SPLAY(head, elm);					\
+	if (SPLAY_RIGHT(elm, field) != NULL) {				\
+		elm = SPLAY_RIGHT(elm, field);				\
+		while (SPLAY_LEFT(elm, field) != NULL) {		\
+			elm = SPLAY_LEFT(elm, field);			\
+		}							\
+	} else								\
+		elm = NULL;						\
+	return (elm);							\
+}									\
+									\
+static __inline struct type *						\
+name##_SPLAY_MIN_MAX(struct name *head, int val)			\
+{									\
+	name##_SPLAY_MINMAX(head, val);					\
+        return (SPLAY_ROOT(head));					\
+}
+
+/* Main splay operation.
+ * Moves node close to the key of elm to top
+ */
+#define SPLAY_GENERATE(name, type, field, cmp)				\
+struct type *								\
+name##_SPLAY_INSERT(struct name *head, struct type *elm)		\
+{									\
+    if (SPLAY_EMPTY(head)) {						\
+	    SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL;	\
+    } else {								\
+	    int __comp;							\
+	    name##_SPLAY(head, elm);					\
+	    __comp = (cmp)(elm, (head)->sph_root);			\
+	    if(__comp < 0) {						\
+		    SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
+		    SPLAY_RIGHT(elm, field) = (head)->sph_root;		\
+		    SPLAY_LEFT((head)->sph_root, field) = NULL;		\
+	    } else if (__comp > 0) {					\
+		    SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
+		    SPLAY_LEFT(elm, field) = (head)->sph_root;		\
+		    SPLAY_RIGHT((head)->sph_root, field) = NULL;	\
+	    } else							\
+		    return ((head)->sph_root);				\
+    }									\
+    (head)->sph_root = (elm);						\
+    return (NULL);							\
+}									\
+									\
+struct type *								\
+name##_SPLAY_REMOVE(struct name *head, struct type *elm)		\
+{									\
+	struct type *__tmp;						\
+	if (SPLAY_EMPTY(head))						\
+		return (NULL);						\
+	name##_SPLAY(head, elm);					\
+	if ((cmp)(elm, (head)->sph_root) == 0) {			\
+		if (SPLAY_LEFT((head)->sph_root, field) == NULL) {	\
+			(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
+		} else {						\
+			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
+			(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
+			name##_SPLAY(head, elm);			\
+			SPLAY_RIGHT((head)->sph_root, field) = __tmp;	\
+		}							\
+		return (elm);						\
+	}								\
+	return (NULL);							\
+}									\
+									\
+void									\
+name##_SPLAY(struct name *head, struct type *elm)			\
+{									\
+	struct type __node, *__left, *__right, *__tmp;			\
+	int __comp;							\
+\
+	SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+	__left = __right = &__node;					\
+\
+	while ((__comp = (cmp)(elm, (head)->sph_root))) {		\
+		if (__comp < 0) {					\
+			__tmp = SPLAY_LEFT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if ((cmp)(elm, __tmp) < 0){			\
+				SPLAY_ROTATE_RIGHT(head, __tmp, field);	\
+				if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKLEFT(head, __right, field);		\
+		} else if (__comp > 0) {				\
+			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if ((cmp)(elm, __tmp) > 0){			\
+				SPLAY_ROTATE_LEFT(head, __tmp, field);	\
+				if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKRIGHT(head, __left, field);		\
+		}							\
+	}								\
+	SPLAY_ASSEMBLE(head, &__node, __left, __right, field);		\
+}									\
+									\
+/* Splay with either the minimum or the maximum element			\
+ * Used to find minimum or maximum element in tree.			\
+ */									\
+void name##_SPLAY_MINMAX(struct name *head, int __comp) \
+{									\
+	struct type __node, *__left, *__right, *__tmp;			\
+\
+	SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+	__left = __right = &__node;					\
+\
+	while (1) {							\
+		if (__comp < 0) {					\
+			__tmp = SPLAY_LEFT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if (__comp < 0){				\
+				SPLAY_ROTATE_RIGHT(head, __tmp, field);	\
+				if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKLEFT(head, __right, field);		\
+		} else if (__comp > 0) {				\
+			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if (__comp > 0) {				\
+				SPLAY_ROTATE_LEFT(head, __tmp, field);	\
+				if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKRIGHT(head, __left, field);		\
+		}							\
+	}								\
+	SPLAY_ASSEMBLE(head, &__node, __left, __right, field);		\
+}
+
+#define SPLAY_NEGINF	-1
+#define SPLAY_INF	1
+
+#define SPLAY_INSERT(name, x, y)	name##_SPLAY_INSERT(x, y)
+#define SPLAY_REMOVE(name, x, y)	name##_SPLAY_REMOVE(x, y)
+#define SPLAY_FIND(name, x, y)		name##_SPLAY_FIND(x, y)
+#define SPLAY_NEXT(name, x, y)		name##_SPLAY_NEXT(x, y)
+#define SPLAY_MIN(name, x)		(SPLAY_EMPTY(x) ? NULL	\
+					: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
+#define SPLAY_MAX(name, x)		(SPLAY_EMPTY(x) ? NULL	\
+					: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
+
+#define SPLAY_FOREACH(x, name, head)					\
+	for ((x) = SPLAY_MIN(name, head);				\
+	     (x) != NULL;						\
+	     (x) = SPLAY_NEXT(name, head, x))
+
+/* Macros that define a red-black tree */
+#define RB_HEAD(name, type)						\
+struct name {								\
+	struct type *rbh_root; /* root of the tree */			\
+}
+
+#define RB_INITIALIZER(root)						\
+	{ NULL }
+
+#define RB_INIT(root) do {						\
+	(root)->rbh_root = NULL;					\
+} while (0)
+
+#define RB_BLACK	0
+#define RB_RED		1
+#define RB_ENTRY(type)							\
+struct {								\
+	struct type *rbe_left;		/* left element */		\
+	struct type *rbe_right;		/* right element */		\
+	struct type *rbe_parent;	/* parent element */		\
+	int rbe_color;			/* node color */		\
+}
+
+#define RB_LEFT(elm, field)		(elm)->field.rbe_left
+#define RB_RIGHT(elm, field)		(elm)->field.rbe_right
+#define RB_PARENT(elm, field)		(elm)->field.rbe_parent
+#define RB_COLOR(elm, field)		(elm)->field.rbe_color
+#define RB_ROOT(head)			(head)->rbh_root
+#define RB_EMPTY(head)			(RB_ROOT(head) == NULL)
+
+#define RB_SET(elm, parent, field) do {					\
+	RB_PARENT(elm, field) = parent;					\
+	RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL;		\
+	RB_COLOR(elm, field) = RB_RED;					\
+} while (0)
+
+#define RB_SET_BLACKRED(black, red, field) do {				\
+	RB_COLOR(black, field) = RB_BLACK;				\
+	RB_COLOR(red, field) = RB_RED;					\
+} while (0)
+
+#ifndef RB_AUGMENT
+#define RB_AUGMENT(x)
+#endif
+
+#define RB_ROTATE_LEFT(head, elm, tmp, field) do {			\
+	(tmp) = RB_RIGHT(elm, field);					\
+	if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) {		\
+		RB_PARENT(RB_LEFT(tmp, field), field) = (elm);		\
+	}								\
+	RB_AUGMENT(elm);						\
+	if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) {		\
+		if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))	\
+			RB_LEFT(RB_PARENT(elm, field), field) = (tmp);	\
+		else							\
+			RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);	\
+	} else								\
+		(head)->rbh_root = (tmp);				\
+	RB_LEFT(tmp, field) = (elm);					\
+	RB_PARENT(elm, field) = (tmp);					\
+	RB_AUGMENT(tmp);						\
+	if ((RB_PARENT(tmp, field)))					\
+		RB_AUGMENT(RB_PARENT(tmp, field));			\
+} while (0)
+
+#define RB_ROTATE_RIGHT(head, elm, tmp, field) do {			\
+	(tmp) = RB_LEFT(elm, field);					\
+	if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) {		\
+		RB_PARENT(RB_RIGHT(tmp, field), field) = (elm);		\
+	}								\
+	RB_AUGMENT(elm);						\
+	if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) {		\
+		if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))	\
+			RB_LEFT(RB_PARENT(elm, field), field) = (tmp);	\
+		else							\
+			RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);	\
+	} else								\
+		(head)->rbh_root = (tmp);				\
+	RB_RIGHT(tmp, field) = (elm);					\
+	RB_PARENT(elm, field) = (tmp);					\
+	RB_AUGMENT(tmp);						\
+	if ((RB_PARENT(tmp, field)))					\
+		RB_AUGMENT(RB_PARENT(tmp, field));			\
+} while (0)
+
+/* Generates prototypes and inline functions */
+#define RB_PROTOTYPE(name, type, field, cmp)				\
+void name##_RB_INSERT_COLOR(struct name *, struct type *);	\
+void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
+struct type *name##_RB_REMOVE(struct name *, struct type *);		\
+struct type *name##_RB_INSERT(struct name *, struct type *);		\
+struct type *name##_RB_FIND(struct name *, struct type *);		\
+struct type *name##_RB_NEXT(struct type *);				\
+struct type *name##_RB_MINMAX(struct name *, int);			\
+									\
+
+/* Main rb operation.
+ * Moves node close to the key of elm to top
+ */
+#define RB_GENERATE(name, type, field, cmp)				\
+void									\
+name##_RB_INSERT_COLOR(struct name *head, struct type *elm)		\
+{									\
+	struct type *parent, *gparent, *tmp;				\
+	while ((parent = RB_PARENT(elm, field)) &&			\
+	    RB_COLOR(parent, field) == RB_RED) {			\
+		gparent = RB_PARENT(parent, field);			\
+		if (parent == RB_LEFT(gparent, field)) {		\
+			tmp = RB_RIGHT(gparent, field);			\
+			if (tmp && RB_COLOR(tmp, field) == RB_RED) {	\
+				RB_COLOR(tmp, field) = RB_BLACK;	\
+				RB_SET_BLACKRED(parent, gparent, field);\
+				elm = gparent;				\
+				continue;				\
+			}						\
+			if (RB_RIGHT(parent, field) == elm) {		\
+				RB_ROTATE_LEFT(head, parent, tmp, field);\
+				tmp = parent;				\
+				parent = elm;				\
+				elm = tmp;				\
+			}						\
+			RB_SET_BLACKRED(parent, gparent, field);	\
+			RB_ROTATE_RIGHT(head, gparent, tmp, field);	\
+		} else {						\
+			tmp = RB_LEFT(gparent, field);			\
+			if (tmp && RB_COLOR(tmp, field) == RB_RED) {	\
+				RB_COLOR(tmp, field) = RB_BLACK;	\
+				RB_SET_BLACKRED(parent, gparent, field);\
+				elm = gparent;				\
+				continue;				\
+			}						\
+			if (RB_LEFT(parent, field) == elm) {		\
+				RB_ROTATE_RIGHT(head, parent, tmp, field);\
+				tmp = parent;				\
+				parent = elm;				\
+				elm = tmp;				\
+			}						\
+			RB_SET_BLACKRED(parent, gparent, field);	\
+			RB_ROTATE_LEFT(head, gparent, tmp, field);	\
+		}							\
+	}								\
+	RB_COLOR(head->rbh_root, field) = RB_BLACK;			\
+}									\
+									\
+void									\
+name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
+{									\
+	struct type *tmp;						\
+	while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) &&	\
+	    elm != RB_ROOT(head)) {					\
+		if (RB_LEFT(parent, field) == elm) {			\
+			tmp = RB_RIGHT(parent, field);			\
+			if (RB_COLOR(tmp, field) == RB_RED) {		\
+				RB_SET_BLACKRED(tmp, parent, field);	\
+				RB_ROTATE_LEFT(head, parent, tmp, field);\
+				tmp = RB_RIGHT(parent, field);		\
+			}						\
+			if ((RB_LEFT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+			    (RB_RIGHT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+				RB_COLOR(tmp, field) = RB_RED;		\
+				elm = parent;				\
+				parent = RB_PARENT(elm, field);		\
+			} else {					\
+				if (RB_RIGHT(tmp, field) == NULL ||	\
+				    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
+					struct type *oleft;		\
+					if ((oleft = RB_LEFT(tmp, field)))\
+						RB_COLOR(oleft, field) = RB_BLACK;\
+					RB_COLOR(tmp, field) = RB_RED;	\
+					RB_ROTATE_RIGHT(head, tmp, oleft, field);\
+					tmp = RB_RIGHT(parent, field);	\
+				}					\
+				RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+				RB_COLOR(parent, field) = RB_BLACK;	\
+				if (RB_RIGHT(tmp, field))		\
+					RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
+				RB_ROTATE_LEFT(head, parent, tmp, field);\
+				elm = RB_ROOT(head);			\
+				break;					\
+			}						\
+		} else {						\
+			tmp = RB_LEFT(parent, field);			\
+			if (RB_COLOR(tmp, field) == RB_RED) {		\
+				RB_SET_BLACKRED(tmp, parent, field);	\
+				RB_ROTATE_RIGHT(head, parent, tmp, field);\
+				tmp = RB_LEFT(parent, field);		\
+			}						\
+			if ((RB_LEFT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+			    (RB_RIGHT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+				RB_COLOR(tmp, field) = RB_RED;		\
+				elm = parent;				\
+				parent = RB_PARENT(elm, field);		\
+			} else {					\
+				if (RB_LEFT(tmp, field) == NULL ||	\
+				    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
+					struct type *oright;		\
+					if ((oright = RB_RIGHT(tmp, field)))\
+						RB_COLOR(oright, field) = RB_BLACK;\
+					RB_COLOR(tmp, field) = RB_RED;	\
+					RB_ROTATE_LEFT(head, tmp, oright, field);\
+					tmp = RB_LEFT(parent, field);	\
+				}					\
+				RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+				RB_COLOR(parent, field) = RB_BLACK;	\
+				if (RB_LEFT(tmp, field))		\
+					RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
+				RB_ROTATE_RIGHT(head, parent, tmp, field);\
+				elm = RB_ROOT(head);			\
+				break;					\
+			}						\
+		}							\
+	}								\
+	if (elm)							\
+		RB_COLOR(elm, field) = RB_BLACK;			\
+}									\
+									\
+struct type *								\
+name##_RB_REMOVE(struct name *head, struct type *elm)			\
+{									\
+	struct type *child, *parent, *old = elm;			\
+	int color;							\
+	if (RB_LEFT(elm, field) == NULL)				\
+		child = RB_RIGHT(elm, field);				\
+	else if (RB_RIGHT(elm, field) == NULL)				\
+		child = RB_LEFT(elm, field);				\
+	else {								\
+		struct type *left;					\
+		elm = RB_RIGHT(elm, field);				\
+		while ((left = RB_LEFT(elm, field)))			\
+			elm = left;					\
+		child = RB_RIGHT(elm, field);				\
+		parent = RB_PARENT(elm, field);				\
+		color = RB_COLOR(elm, field);				\
+		if (child)						\
+			RB_PARENT(child, field) = parent;		\
+		if (parent) {						\
+			if (RB_LEFT(parent, field) == elm)		\
+				RB_LEFT(parent, field) = child;		\
+			else						\
+				RB_RIGHT(parent, field) = child;	\
+			RB_AUGMENT(parent);				\
+		} else							\
+			RB_ROOT(head) = child;				\
+		if (RB_PARENT(elm, field) == old)			\
+			parent = elm;					\
+		(elm)->field = (old)->field;				\
+		if (RB_PARENT(old, field)) {				\
+			if (RB_LEFT(RB_PARENT(old, field), field) == old)\
+				RB_LEFT(RB_PARENT(old, field), field) = elm;\
+			else						\
+				RB_RIGHT(RB_PARENT(old, field), field) = elm;\
+			RB_AUGMENT(RB_PARENT(old, field));		\
+		} else							\
+			RB_ROOT(head) = elm;				\
+		RB_PARENT(RB_LEFT(old, field), field) = elm;		\
+		if (RB_RIGHT(old, field))				\
+			RB_PARENT(RB_RIGHT(old, field), field) = elm;	\
+		if (parent) {						\
+			left = parent;					\
+			do {						\
+				RB_AUGMENT(left);			\
+			} while ((left = RB_PARENT(left, field)));	\
+		}							\
+		goto color;						\
+	}								\
+	parent = RB_PARENT(elm, field);					\
+	color = RB_COLOR(elm, field);					\
+	if (child)							\
+		RB_PARENT(child, field) = parent;			\
+	if (parent) {							\
+		if (RB_LEFT(parent, field) == elm)			\
+			RB_LEFT(parent, field) = child;			\
+		else							\
+			RB_RIGHT(parent, field) = child;		\
+		RB_AUGMENT(parent);					\
+	} else								\
+		RB_ROOT(head) = child;					\
+color:									\
+	if (color == RB_BLACK)						\
+		name##_RB_REMOVE_COLOR(head, parent, child);		\
+	return (old);							\
+}									\
+									\
+/* Inserts a node into the RB tree */					\
+struct type *								\
+name##_RB_INSERT(struct name *head, struct type *elm)			\
+{									\
+	struct type *tmp;						\
+	struct type *parent = NULL;					\
+	int comp = 0;							\
+	tmp = RB_ROOT(head);						\
+	while (tmp) {							\
+		parent = tmp;						\
+		comp = (cmp)(elm, parent);				\
+		if (comp < 0)						\
+			tmp = RB_LEFT(tmp, field);			\
+		else if (comp > 0)					\
+			tmp = RB_RIGHT(tmp, field);			\
+		else							\
+			return (tmp);					\
+	}								\
+	RB_SET(elm, parent, field);					\
+	if (parent != NULL) {						\
+		if (comp < 0)						\
+			RB_LEFT(parent, field) = elm;			\
+		else							\
+			RB_RIGHT(parent, field) = elm;			\
+		RB_AUGMENT(parent);					\
+	} else								\
+		RB_ROOT(head) = elm;					\
+	name##_RB_INSERT_COLOR(head, elm);				\
+	return (NULL);							\
+}									\
+									\
+/* Finds the node with the same key as elm */				\
+struct type *								\
+name##_RB_FIND(struct name *head, struct type *elm)			\
+{									\
+	struct type *tmp = RB_ROOT(head);				\
+	int comp;							\
+	while (tmp) {							\
+		comp = cmp(elm, tmp);					\
+		if (comp < 0)						\
+			tmp = RB_LEFT(tmp, field);			\
+		else if (comp > 0)					\
+			tmp = RB_RIGHT(tmp, field);			\
+		else							\
+			return (tmp);					\
+	}								\
+	return (NULL);							\
+}									\
+									\
+struct type *								\
+name##_RB_NEXT(struct type *elm)					\
+{									\
+	if (RB_RIGHT(elm, field)) {					\
+		elm = RB_RIGHT(elm, field);				\
+		while (RB_LEFT(elm, field))				\
+			elm = RB_LEFT(elm, field);			\
+	} else {							\
+		if (RB_PARENT(elm, field) &&				\
+		    (elm == RB_LEFT(RB_PARENT(elm, field), field)))	\
+			elm = RB_PARENT(elm, field);			\
+		else {							\
+			while (RB_PARENT(elm, field) &&			\
+			    (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
+				elm = RB_PARENT(elm, field);		\
+			elm = RB_PARENT(elm, field);			\
+		}							\
+	}								\
+	return (elm);							\
+}									\
+									\
+struct type *								\
+name##_RB_MINMAX(struct name *head, int val)				\
+{									\
+	struct type *tmp = RB_ROOT(head);				\
+	struct type *parent = NULL;					\
+	while (tmp) {							\
+		parent = tmp;						\
+		if (val < 0)						\
+			tmp = RB_LEFT(tmp, field);			\
+		else							\
+			tmp = RB_RIGHT(tmp, field);			\
+	}								\
+	return (parent);						\
+}
+
+#define RB_NEGINF	-1
+#define RB_INF	1
+
+#define RB_INSERT(name, x, y)	name##_RB_INSERT(x, y)
+#define RB_REMOVE(name, x, y)	name##_RB_REMOVE(x, y)
+#define RB_FIND(name, x, y)	name##_RB_FIND(x, y)
+#define RB_NEXT(name, x, y)	name##_RB_NEXT(y)
+#define RB_MIN(name, x)		name##_RB_MINMAX(x, RB_NEGINF)
+#define RB_MAX(name, x)		name##_RB_MINMAX(x, RB_INF)
+
+#define RB_FOREACH(x, name, head)					\
+	for ((x) = RB_MIN(name, head);					\
+	     (x) != NULL;						\
+	     (x) = name##_RB_NEXT(x))
+
+#endif	/* _SYS_TREE_H_ */

Added: experimental/moritz/iceadmin/configure.in
===================================================================
--- experimental/moritz/iceadmin/configure.in	                        (rev 0)
+++ experimental/moritz/iceadmin/configure.in	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,192 @@
+dnl INITIALIZATION
+
+m4_define(iceadmin_major, 0)
+m4_define(iceadmin_minor, 1)
+m4_define(iceadmin_micro, alpha01)
+m4_define(iceadmin_version,
+  m4_if(iceadmin_micro, 0, iceadmin_major.iceadmin_minor,
+   iceadmin_major.iceadmin_minor.iceadmin_micro))
+
+AC_INIT([IceAdmin], iceadmin_version, [mdgrimm at gmx.net])
+
+LIBICEADMIN_SO_MAJOR="0"
+LIBICEADMIN_SO_MINOR="0"
+
+AC_PREREQ(2.61)
+AC_CONFIG_SRCDIR(lib/libiceadmin.c)
+AC_CONFIG_AUX_DIR(build-aux)
+AM_INIT_AUTOMAKE
+AC_CONFIG_HEADERS(include/config.h)
+AC_CONFIG_LIBOBJ_DIR(lib)
+AM_MAINTAINER_MODE
+AC_USE_SYSTEM_EXTENSIONS
+AC_PROG_CC_STDC
+AC_PROG_LIBTOOL
+AC_DEFINE([LIBICEADMIN_MAJOR], iceadmin_major, [Iceadmin library major version])
+AC_DEFINE([LIBICEADMIN_MINOR], iceadmin_minor, [Iceadmin library minor version])
+AC_DEFINE([LIBICEADMIN_MICRO], iceadmin_micro, [Iceadmin library patch version])
+
+
+dnl SETUP
+
+AC_CANONICAL_HOST
+
+if test -z "$GCC"; then
+	case $host in 
+	*-irix*)
+		XIPH_CPPFLAGS="-fullwarn"
+		;;
+	*-solaris*)
+		XIPH_CPPFLAGS="-v"
+		;;
+	*)
+		;;
+	esac
+else
+	XIPH_CPPFLAGS="-fstrict-aliasing -Wall -W -ansi -pedantic -Wno-unused-parameter -Wwrite-strings -Wpointer-arith -Wsign-compare -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations"
+fi
+
+XALLOC_CPPFLAGS=""
+ia_enable_debug=no
+AC_ARG_ENABLE(debug,
+	AS_HELP_STRING([--enable-debug],
+		[enable memory debugging (default: no)]),
+[case "$enableval" in
+	no) ;;
+	*) ia_enable_debug=yes ;;
+esac], [])
+AC_MSG_CHECKING([whether to enable debugging])
+if test x"$ia_enable_debug" = "xyes"; then
+	XALLOC_CPPFLAGS="-DUSE_XALLOC=1 -DXALLOC_DEBUG=1"
+fi
+AC_MSG_RESULT([$ia_enable_debug])
+
+PKG_PROG_PKG_CONFIG([0.15])
+
+
+dnl MISC SYSTEM CHARACTERISTICS
+
+AC_C_CONST
+AC_TYPE_SIZE_T
+AC_CHECK_MEMBER(struct sockaddr.sa_len,
+	AC_DEFINE(HAVE_SOCKADDR_SA_LEN, 1,
+		[Define whether we have struct sockaddr.sa_len]), ,
+		[ #include <sys/types.h>
+		  #include <sys/socket.h> ]
+)
+
+
+dnl USEFUL HEADERS
+
+AC_CHECK_HEADERS(sys/socket.h netdb.h)
+
+COMPAT_INCLUDES=""
+if test x"$ia_enable_debug" = "xyes"; then
+	AC_CHECK_HEADERS(sys/tree.h)
+	if test x"$ac_cv_header_sys_tree_h" = "xyes"; then
+		AC_MSG_CHECKING([for RB_FOREACH and RB_INSERT in sys/tree.h])
+		AC_EGREP_CPP([yes], [
+#include <sys/tree.h>
+#if defined(RB_FOREACH) && defined(RB_INSERT)
+ yes
+#endif
+		], [
+			AC_MSG_RESULT([yes])
+			AC_DEFINE(HAVE_WORKING_SYS_TREE_H, 1,
+				[Define whether RB_FOREACH is defined in <sys/tree.h>])
+		], [
+			AC_MSG_RESULT([no])
+			COMPAT_INCLUDES="-I\$(top_srcdir)/compat"
+		])
+	else
+		COMPAT_INCLUDES="-I\$(top_srcdir)/compat"
+	fi
+fi
+
+
+dnl LIBRARY FUNCTIONS
+
+AC_REPLACE_FUNCS(strlcat)
+dnl AC_CHECK_FUNCS(getaddrinfo)
+dnl LIBCURL_CHECK_CONFIG([yes], [], [], [
+dnl 	AC_MSG_ERROR([Sorry, libcurl is required.])
+dnl ])
+
+
+dnl CONFIGURE BUILD OPTIONS, 3RD PART LIBRARIES
+
+dnl Check for libxml2
+use_xml2_config="no"
+AC_ARG_WITH(xml2-config,
+	[AS_HELP_STRING([--with-xml2-config=path],
+		[use specific xml2-config in path (default: use pkg-config)])],
+[case "$withval" in
+	no) ;;
+	yes) use_xml2_config="" ;;
+	*) use_xml2_config="$withval" ;;
+esac], [])
+XML2CONFIG=""
+if test x"$use_xml2_config" != "xno"; then
+	if test x"$use_xml2_config" = "x"; then
+		AC_PATH_PROGS(XML2CONFIG, [xml2-config], "")
+	else
+		XML2CONFIG="$use_xml2_config"
+		AC_SUBST(XML2CONFIG)
+	fi
+fi
+XML2_LIBS=""
+XML2_CFLAGS=""
+found_libxml2="no"
+if test x"$XML2CONFIG" = "x"; then
+	PKG_CHECK_MODULES(XML2, libxml-2.0, [found_libxml2="yes"], [
+		AC_PATH_PROGS(XML2CONFIG, [xml2-config], "")
+	])
+fi
+if test x"$XML2CONFIG" != "x" -a x"$found_libxml2" = "xno"; then
+	test -x "$XML2CONFIG" || AC_MSG_ERROR([$XML2CONFIG is not an executable], 1)
+	XML2_LIBS="`$XML2CONFIG --libs`"
+	XML2_CFLAGS="`$XML2CONFIG --cflags`"
+	found_libxml2="yes"
+fi
+if test x"$found_libxml2" = x"no"; then
+	AC_MSG_ERROR([libxml2 is required but could not be found], 1)
+fi
+ac_xml2_save_LIBS="$LIBS"
+ac_xml2_save_CFLAGS="$CFLAGS"
+LIBS="$XML2_LIBS $LIBS"
+CFLAGS="$XML2_CFLAGS $CFLAGS"
+AC_CHECK_FUNC(xmlNewDoc,, [AC_MSG_ERROR([Unable to link with libxml2])])
+LIBS="$ac_xml2_save_LIBS"
+CFLAGS="$ac_xml2_save_CFLAGS"
+
+
+dnl OUTPUT
+
+ICEADMIN_CFLAGS=""
+ICEADMIN_CPPFLAGS=""
+ICEADMIN_VERSION="$PACKAGE_VERSION"
+ICEADMIN_REQUIRES="libxml-2.0"
+AC_SUBST(ICEADMIN_CFLAGS)
+AC_SUBST(ICEADMIN_CPPFLAGS)
+AC_SUBST(ICEADMIN_VERSION)
+AC_SUBST(ICEADMIN_REQUIRES)
+
+AC_SUBST(LIBTOOL_DEPS)
+AC_SUBST(XALLOC_CPPFLAGS)
+AC_SUBST(COMPAT_INCLUDES)
+AC_SUBST(LIBICEADMIN_SO_MAJOR)
+AC_SUBST(LIBICEADMIN_SO_MINOR)
+AC_SUBST(XML2_LIBS)
+AC_SUBST(XML2_CFLAGS)
+AC_SUBST(XIPH_CPPFLAGS)
+
+AC_CONFIG_FILES(Makefile \
+		compat/Makefile \
+		compat/sys/Makefile \
+		doc/Makefile \
+		include/Makefile \
+		lib/Makefile \
+		src/Makefile \
+		iceadmin.pc)
+
+AC_OUTPUT

Added: experimental/moritz/iceadmin/doc/Makefile.am
===================================================================
--- experimental/moritz/iceadmin/doc/Makefile.am	                        (rev 0)
+++ experimental/moritz/iceadmin/doc/Makefile.am	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,3 @@
+AUTOMAKE_OPTIONS = 1.9 foreign
+
+CLEANFILES =	core *.core *~ .*~

Added: experimental/moritz/iceadmin/iceadmin.pc.in
===================================================================
--- experimental/moritz/iceadmin/iceadmin.pc.in	                        (rev 0)
+++ experimental/moritz/iceadmin/iceadmin.pc.in	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,15 @@
+# libiceadmin pkg-config template
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+cppflags=@ICEADMIN_CPPFLAGS@
+cflags_only=@ICEADMIN_CFLAGS@
+
+Name: IceAdmin
+Description: Icecast remote administration library
+Version: @ICEADMIN_VERSION@
+Requires: @ICEADMIN_REQUIRES@
+Libs: -L${libdir} -liceadmin
+Cflags: -I${includedir} @ICEADMIN_CFLAGS@

Added: experimental/moritz/iceadmin/include/Makefile.am
===================================================================
--- experimental/moritz/iceadmin/include/Makefile.am	                        (rev 0)
+++ experimental/moritz/iceadmin/include/Makefile.am	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,5 @@
+AUTOMAKE_OPTIONS = 1.9 foreign
+
+include_HEADERS = iceadmin.h
+
+CLEANFILES =	core *.core *~ .*~

Added: experimental/moritz/iceadmin/include/config.h.in
===================================================================
--- experimental/moritz/iceadmin/include/config.h.in	                        (rev 0)
+++ experimental/moritz/iceadmin/include/config.h.in	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,121 @@
+/* include/config.h.in.  Generated from configure.in by autoheader.  */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#undef HAVE_NETDB_H
+
+/* Define whether we have struct sockaddr.sa_len */
+#undef HAVE_SOCKADDR_SA_LEN
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strlcat' function. */
+#undef HAVE_STRLCAT
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/tree.h> header file. */
+#undef HAVE_SYS_TREE_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define whether RB_FOREACH is defined in <sys/tree.h> */
+#undef HAVE_WORKING_SYS_TREE_H
+
+/* Iceadmin library major version */
+#undef LIBICEADMIN_MAJOR
+
+/* Iceadmin library patch version */
+#undef LIBICEADMIN_MICRO
+
+/* Iceadmin library minor version */
+#undef LIBICEADMIN_MINOR
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION
+
+/* Define to 1 if on AIX 3.
+   System headers sometimes define this.
+   We just want to avoid a redefinition error message.  */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+
+/* Enable GNU extensions on systems that have them.  */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+
+/* Define to 1 if on MINIX. */
+#undef _MINIX
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+   this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+#undef _POSIX_SOURCE
+
+/* Enable extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t

Added: experimental/moritz/iceadmin/include/iceadmin.h
===================================================================
--- experimental/moritz/iceadmin/include/iceadmin.h	                        (rev 0)
+++ experimental/moritz/iceadmin/include/iceadmin.h	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,98 @@
+#ifndef __ICEADMIN_H__
+#define __ICEADMIN_H__
+
+/* The function completed successfully: */
+#define ICEADMIN_SUCCESS	0
+/* A request succeeded, but the command could not be fulfilled: */
+#define ICEADMIN_FAILURE	1
+/* An error occured: */
+#define ICEADMIN_ERROR		2
+/* Libiceadmin is being abused with bad function arguments, e.g. NULL where
+ * not permitted: */
+#define ICEADMIN_ABUSE		3
+/* Libiceadmin is insane and something unexpected happened. This is a bug: */
+#define ICEADMIN_INSANE 	4
+/* Stale handle that wasn't initialized properly: */
+#define ICEADMIN_BADHANDLE	5
+
+typedef struct iceadmin 	*iceadmin_t;
+
+#if defined(__cplusplus) && (!defined(__BEGIN_DECLS) || !defined(__END_DECLS))
+# undef __BEGIN_DECLS
+# undef __END_DECLS
+# define __BEGIN_DECLS		extern "C" {
+# define __END_DECLS		}
+#endif /* !__BEGIN_DECLS || !_END_DECLS */
+
+__BEGIN_DECLS
+void	iceadmin_initialize(void);
+void	iceadmin_shutdown(void);
+
+void	iceadmin_get_error(iceadmin_t /* handle */, char * /* buffer */,
+			   size_t /* buffer size */);
+
+/* Set the user-agent string to "name/version" or NULL for the default. */
+void	iceadmin_set_useragent(const char * /* string */);
+
+int	iceadmin_new(iceadmin_t * /* ptr to new handle */,
+		     const char * /* mountpoint, may be NULL for certain *
+				   * _get_/_list_ functions */,
+		     const char * /* hostname */,
+		     const char * /* service/port */,
+		     const char * /* username */,
+		     const char * /* password */);
+#define iceadmin_delete(i)						\
+	iceadmin_delete_c(&(i))
+int	iceadmin_delete_c(iceadmin_t * /* handle ptr */);
+#define iceadmin_free(p)						\
+	iceadmin_free_c((void *)&(p))
+int	iceadmin_free_c(void ** /* buffer allocated by iceadmin */);
+
+/*
+ * Commands that work without a mountpoint.
+ * A buffer is allocated and its size stored. The buffer needs to be freed by
+ * the caller.
+ */
+int	iceadmin_cmd_get_stats(iceadmin_t /* handle */,
+			       char ** /* buffer */,
+			       size_t * /* buffer size */);
+int	iceadmin_cmd_get_xmlstats(iceadmin_t /* handle */,
+				  char ** /* buffer */,
+				  size_t * /* buffer size */);
+int	iceadmin_cmd_list_mounts(iceadmin_t /* handle */,
+				 char ** /* buffer */,
+				 size_t * /* buffer size */);
+int	iceadmin_cmd_list_xmlmounts(iceadmin_t /* handle */,
+				    char ** /* buffer */,
+				    size_t * /* buffer size */);
+
+/*
+ * Commands that require a mountpoint.
+ * A buffer is allocated and its size stored. The buffer needs to be freed by
+ * the caller.
+ */
+int	iceadmin_cmd_list_clients(iceadmin_t /* handle */,
+				  char ** /* buffer */,
+				  size_t * /* buffer size */);
+int	iceadmin_cmd_list_xmlclients(iceadmin_t /* handle */,
+				     char ** /* buffer */,
+				     size_t * /* buffer size */);
+
+/*
+ * Miscellaneous commands that work on a mountpoint.
+ */
+int	iceadmin_cmd_kill_source(iceadmin_t /* handle */);
+int	iceadmin_cmd_kill_client(iceadmin_t /* handle */,
+				 unsigned int /* client ID */);
+int	iceadmin_cmd_move_clients(iceadmin_t /* handle */,
+				  const char * /* destination */);
+int	iceadmin_cmd_update_fallback(iceadmin_t /* handle */,
+				     const char * /* mountpoint */);
+int	iceadmin_cmd_update_metadata(iceadmin_t /* handle */,
+				     const char * /* metadata string */);
+int	iceadmin_cmd_update_artist_title(iceadmin_t /* handle */,
+					 const char * /* artist */,
+					 const char * /* title */);
+__END_DECLS
+
+#endif /* __ICEADMIN_H__ */

Added: experimental/moritz/iceadmin/lib/Makefile.am
===================================================================
--- experimental/moritz/iceadmin/lib/Makefile.am	                        (rev 0)
+++ experimental/moritz/iceadmin/lib/Makefile.am	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,16 @@
+AUTOMAKE_OPTIONS = 1.9 foreign
+
+lib_LTLIBRARIES = libiceadmin.la
+
+libiceadmin_la_SOURCES = error.c libiceadmin.c iceadmin_internal.c xalloc.c
+libiceadmin_la_LDFLAGS = @LTLIBOBJS@ -version-info @LIBICEADMIN_SO_MAJOR@:@LIBICEADMIN_SO_MINOR@
+
+INCLUDES =	@COMPAT_INCLUDES@ -I$(top_srcdir)/include
+
+AM_CFLAGS =	@XML2_CFLAGS@
+AM_CPPFLAGS =	@XIPH_CPPFLAGS@ @XALLOC_CPPFLAGS@
+
+EXTRA_DIST =	error.h iceadmin_internal.h xalloc.h \
+		b64_ntop.txt strnstr.txt
+
+CLEANFILES =	core *.core *~ .*~

Added: experimental/moritz/iceadmin/lib/b64_ntop.txt
===================================================================
--- experimental/moritz/iceadmin/lib/b64_ntop.txt	                        (rev 0)
+++ experimental/moritz/iceadmin/lib/b64_ntop.txt	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,43 @@
+License of the code used in _iceadmin_base64_encode()
+
+/*
+ * Copyright (c) 1996 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Portions Copyright (c) 1995 by International Business Machines, Inc.
+ *
+ * International Business Machines, Inc. (hereinafter called IBM) grants
+ * permission under its copyrights to use, copy, modify, and distribute this
+ * Software with or without fee, provided that the above copyright notice and
+ * all paragraphs of this notice appear in all copies, and that the name of IBM
+ * not be used in connection with the marketing of any product incorporating
+ * the Software or modifications thereof, without specific, written prior
+ * permission.
+ *
+ * To the extent it has a right to do so, IBM grants an immunity from suit
+ * under its patents, if any, for the use, sale or manufacture of products to
+ * the extent that such products are used for performing Domain Name System
+ * dynamic updates in TCP/IP networks by means of the Software.  No immunity is
+ * granted for any product per se or for any other function of any product.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE.  IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
+ * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/

Added: experimental/moritz/iceadmin/lib/error.c
===================================================================
--- experimental/moritz/iceadmin/lib/error.c	                        (rev 0)
+++ experimental/moritz/iceadmin/lib/error.c	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,70 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "iceadmin_internal.h"
+#include "error.h"
+#include "xalloc.h"
+
+void
+_set_error(struct iceadmin *iceadmin, int from_netdb, int errnum, const char *fmt, ...)
+{
+	va_list ap;
+	char	errmsg[ERRMSG_MAX];
+
+	if (iceadmin->errmsg != NULL)
+		xfree(iceadmin->errmsg);
+
+	va_start(ap, fmt);
+	vsnprintf(errmsg, sizeof(errmsg), fmt, ap);
+	va_end(ap);
+
+	iceadmin->errmsg = xstrdup(errmsg);
+	iceadmin->errnum = errnum;
+	iceadmin->gai_err = from_netdb;
+}
+
+void
+_clear_error(struct iceadmin *iceadmin)
+{
+	if (iceadmin->errmsg != NULL) {
+		xfree(iceadmin->errmsg);
+		iceadmin->errmsg = NULL;
+	}
+	iceadmin->errnum = 0;
+}
+
+void
+_get_error_string(struct iceadmin *iceadmin, char *buf, size_t bufsize)
+{
+	if (iceadmin->errmsg == NULL && iceadmin->errnum == 0) {
+		snprintf(buf, bufsize, "No error\n");
+		return;
+	}
+
+	if (iceadmin->errmsg != NULL) {
+		if (iceadmin->errnum != 0) {
+			snprintf(buf, bufsize, "%s: %s\n", iceadmin->errmsg,
+				 iceadmin->gai_err ?
+				 gai_strerror(iceadmin->errnum) :
+				 strerror(iceadmin->errnum));
+		} else {
+			snprintf(buf, bufsize, "%s\n", iceadmin->errmsg);
+		}
+	} else {
+		snprintf(buf, bufsize, "%s\n",
+			 iceadmin->gai_err ?
+			 gai_strerror(iceadmin->errnum) :
+			 strerror(iceadmin->errnum));
+	}
+}

Added: experimental/moritz/iceadmin/lib/error.h
===================================================================
--- experimental/moritz/iceadmin/lib/error.h	                        (rev 0)
+++ experimental/moritz/iceadmin/lib/error.h	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,10 @@
+#ifndef __ERROR_H__
+#define __ERROR_H__
+
+#define ERRMSG_MAX	256
+
+void	_set_error(struct iceadmin *, int, int, const char *, ...);
+void	_clear_error(struct iceadmin *);
+void	_get_error_string(struct iceadmin *, char *, size_t);
+
+#endif /* __ERROR_H__ */

Added: experimental/moritz/iceadmin/lib/iceadmin_internal.c
===================================================================
--- experimental/moritz/iceadmin/lib/iceadmin_internal.c	                        (rev 0)
+++ experimental/moritz/iceadmin/lib/iceadmin_internal.c	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,589 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include "iceadmin.h"
+#include "iceadmin_internal.h"
+#include "error.h"
+#include "xalloc.h"
+
+/* -1 ~= illegal, 1 ~= take as-is (safe), 0 ~= needs encoding */
+const int _url_safe_chars[] = {
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00-0F */
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10-1F */
+     0,  1,  0,  0,  0,  0,  0,  1,  1,  1,  0,  0,  0,  1,  1,  0, /* 20-2F */
+     1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,  0, /* 30-3F */
+     0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, /* 40-4F */
+     1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  1, /* 50-5F */
+     0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, /* 60-6F */
+     1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0, -1, /* 70-7F */
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80-8F */
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90-9F */
+     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, /* A0-AF */
+     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, /* B0-BF */
+     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, /* C0-CF */
+     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, /* D0-DF */
+     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, /* E0-EF */
+     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0  /* F0-FF */
+};
+
+const unsigned char _hex_chars[] = {
+	'0', '1', '2', '3', '4', '5', '6', '7',
+	'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+};
+
+const unsigned char _base64_chars[] = {
+	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+	'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+	'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+	'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+	'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+	'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+	'w', 'x', 'y', 'z', '0', '1', '2', '3',
+	'4', '5', '6', '7', '8', '9', '+', '/'
+};
+const unsigned char _base64_pad = '=';
+
+char *
+_iceadmin_url_encode(const char *str)
+{
+	char		*enc_str, *cp;
+	const char	*ccp;
+	size_t		 size;
+	unsigned int	 safe_chars, encode_chars;
+
+	if (str == NULL)
+		return (NULL);
+
+	safe_chars = encode_chars = 0;
+
+	for (ccp = str; *ccp != '\0'; ccp++) {
+		switch (_url_safe_chars[(unsigned int)*ccp]) {
+		case -1:
+			return (NULL);
+		case 0:
+			encode_chars++;
+			break;
+		case 1:
+			safe_chars++;
+			break;
+		default:
+			return (NULL);
+		}
+	}
+
+	size = 3 * encode_chars + safe_chars + 1;
+	enc_str = xcalloc(size, sizeof(char));
+
+	for (ccp = str, cp = enc_str; *ccp != '\0'; ccp++, cp++) {
+		if (_url_safe_chars[(unsigned int)*ccp]) {
+			*cp = *ccp;
+			continue;
+		}
+
+		*cp++ = '%';
+		*cp++ = _hex_chars[(*ccp >> 4) & 0xF];
+		*cp = _hex_chars[*ccp & 0xF];
+	}
+
+	return (enc_str);
+}
+
+#define ISXDIGIT(x)	isxdigit((int)((unsigned char)x))
+char *
+_iceadmin_url_decode(const char *str)
+{
+	char		*dec_str, *cp;
+	const char	*ccp;
+	size_t		 str_size;
+
+	str_size = strlen(str) + 1;
+	dec_str = xcalloc(str_size, sizeof(char));
+	for (ccp = str, cp = dec_str; *ccp != '\0'; ccp++) {
+		if (*ccp == '%' && ISXDIGIT(ccp[1]) && ISXDIGIT(ccp[2])) {
+			char	hexstr[3];
+			long	hex;
+
+			hexstr[0] = ccp[1];
+			hexstr[1] = ccp[2];
+			hexstr[2] = '\0';
+			hex = strtol(hexstr, NULL, 16);
+			if (_url_safe_chars[(unsigned int)hex] != -1)
+				*cp++ = (unsigned char)hex;
+			ccp += 2;
+		} else
+			*cp++ = *ccp;
+	}
+
+	return (dec_str);
+}
+
+char *
+_iceadmin_auth_basic_string(const char *user, const char *pass)
+{
+	size_t	 siz1, siz2;
+	char	*buf1, *buf2;
+
+	if (user == NULL || pass == NULL)
+		return (xstrdup(""));
+
+	siz1 = strlen(user) + 1 + strlen(pass) + 1;
+	buf1 = xcalloc(siz1, sizeof(char));
+	snprintf(buf1, siz1, "%s:%s", user, pass);
+	if (_iceadmin_base64_encode(buf1, siz1 - 1, &buf2, &siz2) != ICEADMIN_SUCCESS) {
+		xfree(buf1);
+		return (xstrdup(""));
+	}
+	xfree(buf1);
+
+	siz1 = 24 + siz2 + 1;
+	buf1 = xcalloc(siz1, sizeof(char));
+	snprintf(buf1, siz1, "Authorization: Basic %s\r\n", buf2);
+	xfree(buf2);
+
+	return (buf1);
+}
+
+/*
+ * Based on b64_ntop() by IBM and ISC, from
+ * $OpenBSD: src/lib/libc/net/base64.c,v 1.5 2006/10/21 09:55:03 otto Exp $
+ * License is in b64_ntop.txt
+ */
+int
+_iceadmin_base64_encode(const char *in, size_t inputlen, char **out_p, size_t *outputlen)
+{
+	size_t		 datalen;
+	unsigned char	 input[3];
+	unsigned char	 output[4];
+	char		*out;
+	size_t		 bufsize;
+
+	bufsize = (inputlen * 4 / 3) + 4;
+	out = xcalloc(bufsize, sizeof(char));
+
+	datalen = 0;
+	while (inputlen > 2) {
+		input[0] = *in++;
+		input[1] = *in++;
+		input[2] = *in++;
+		inputlen -= 3;
+
+		output[0] = input[0] >> 2;
+		output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+		output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+		output[3] = input[2] & 0x3f;
+
+		if (datalen + 4 > bufsize) {
+			xfree(out);
+			return (ICEADMIN_INSANE);
+		}
+
+		out[datalen++] = _base64_chars[output[0]];
+		out[datalen++] = _base64_chars[output[1]];
+		out[datalen++] = _base64_chars[output[2]];
+		out[datalen++] = _base64_chars[output[3]];
+	}
+
+	if (inputlen != 0) {
+		unsigned int	i;
+
+		input[0] = input[1] = input[2] = '\0';
+		for (i = 0; i < inputlen; i++)
+			input[i] = *in++;
+
+		output[0] = input[0] >> 2;
+		output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+		output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+
+		if (datalen + 4 > bufsize) {
+			xfree(out);
+			return (ICEADMIN_INSANE);
+		}
+
+		out[datalen++] = _base64_chars[output[0]];
+		out[datalen++] = _base64_chars[output[1]];
+		if (inputlen == 1)
+			out[datalen++] = _base64_pad;
+		else
+			out[datalen++] = _base64_chars[output[2]];
+		out[datalen++] = _base64_pad;
+	}
+
+	if (datalen >= bufsize) {
+		xfree(out);
+		return (ICEADMIN_INSANE);
+	}
+
+	out[datalen] = '\0';
+	*out_p = xrealloc(out, datalen + 1, sizeof(char));
+	if (outputlen != NULL)
+		*outputlen = datalen;
+
+	return (ICEADMIN_SUCCESS);
+}
+
+int
+_iceadmin_read_http_reply(struct iceadmin *iceadmin, int sock, unsigned int *httpret)
+{
+	char		static_buf[BUFSIZ], *bp, ch;
+	long		rret;
+	unsigned int	i;
+
+	static_buf[0] = '\0';
+	bp = static_buf;
+	i = 0;
+	while ((rret = (long)read(sock, &ch, sizeof(char))) != -1 && rret != 0) {
+		if (++i >= sizeof(static_buf) - 1) {
+			_set_error(iceadmin, 0, 0, "[%s}:%s: (Non-)HTTP reply too large",
+				   iceadmin->hostname, iceadmin->service);
+			return (ICEADMIN_ERROR);
+		}
+		if ((*bp++ = ch) == '\n') {
+			*bp = '\0';
+			break;
+		}
+	}
+	if (rret == -1) {
+		_set_error(iceadmin, 0, errno, "read: [%s]:%s",
+			   iceadmin->hostname, iceadmin->service);
+		return (ICEADMIN_ERROR);
+	}
+
+	if (strncmp(static_buf, "HTTP/1.", 7) != 0) {
+		_set_error(iceadmin, 0, 0, "[%s]:%s: No HTTP reply from server",
+			   iceadmin->hostname, iceadmin->service);
+		return (ICEADMIN_ERROR);
+	}
+
+	if (sscanf(static_buf, "HTTP/%*s %u %*s", httpret) != 1) {
+		_set_error(iceadmin, 0, 0, "[%s]:%s: HTTP parse error",
+			   iceadmin->hostname, iceadmin->service);
+		return (ICEADMIN_ERROR);
+	}
+
+	if (*httpret != 200) {
+		switch (*httpret) {
+		case 400:
+			/*
+			 * Context sensitive, but set a sane default. Caller
+			 * should make this more specific.
+			 */
+			_set_error(iceadmin, 0, 0, "[%s]:%s: 400 - Bad request; not an Icecast server?",
+				   iceadmin->hostname, iceadmin->service);
+			break;
+		case 401:
+			_set_error(iceadmin, 0, 0, "[%s]:%s: 401 - Authentication for user '%s' failed",
+				   iceadmin->hostname, iceadmin->service,
+				   iceadmin->username);
+			break;
+		case 403:
+			_set_error(iceadmin, 0, 0, "[%s]:%s: 403 - Access denied; not an Icecast server?",
+				   iceadmin->hostname, iceadmin->service);
+			break;
+		case 404:
+			_set_error(iceadmin, 0, 0, "[%s]:%s: 404 - Not found; not an Icecast server?",
+				   iceadmin->hostname, iceadmin->service);
+			break;
+		default:
+			_set_error(iceadmin, 0, 0, "[%s]:%s: %3u - Unsupported HTTP reply",
+				   iceadmin->hostname, iceadmin->service, *httpret);
+			break;
+		}
+		return (ICEADMIN_ERROR);
+	}
+
+	return (ICEADMIN_SUCCESS);
+}
+
+/*
+ * Based on FreeBSD's strnstr():
+ * $FreeBSD: src/lib/libc/string/strnstr.c,v 1.4 2007/01/09 00:28:12 imp Exp $
+ * License is in strnstr.txt
+ */
+char *
+_iceadmin_strnstr(const char *s, const char *find, size_t slen, size_t *pos_p)
+{
+	char c, sc;
+	size_t len, pos;
+
+	pos = 0;
+	if ((c = *find++) != '\0') {
+		len = strlen(find);
+		do {
+			do {
+				if (slen-- < 1 || (sc = *s++) == '\0')
+					return (NULL);
+				else
+					pos++;
+			} while (sc != c);
+			if (len > slen)
+				return (NULL);
+		} while (strncmp(s, find, len) != 0);
+		s--;
+		pos--;
+	}
+
+	if (pos_p != NULL)
+		*pos_p = pos;
+
+	return ((char *)s);
+}
+
+int
+_iceadmin_read_xml_data(struct iceadmin *iceadmin, int sock, char **buf_p, size_t *bufsize_p)
+{
+	char	*buf, *bp, static_buf[BUFSIZ];
+	size_t	 bufsize, pos;
+	long	 rret;
+
+	/*
+	 * Fill the buffer with a chunk of data. We're looking for the start
+	 * of XML data. So, if it's not here, we can be sure that we received
+	 * junk.
+	 */
+	if ((rret = (long)read(sock, static_buf, sizeof(static_buf))) == -1) {
+		_set_error(iceadmin, 0, errno, "read: [%s]:%s",
+			   iceadmin->hostname, iceadmin->service);
+		return (ICEADMIN_ERROR);
+	}
+	if ((bp = _iceadmin_strnstr(static_buf, "<?xml version=", sizeof(static_buf), &pos)) == NULL) {
+		_set_error(iceadmin, 0, 0, "[%s]:%s: No XML data in reply from server",
+			   iceadmin->hostname, iceadmin->service);
+		return (ICEADMIN_ERROR);
+	}
+
+	bufsize = rret - pos + 1;
+	buf = xmalloc(bufsize);
+	memcpy(buf, bp, rret - pos);
+	pos = rret - pos;
+
+	while ((rret = (long)read(sock, static_buf, sizeof(static_buf))) != -1 && rret != 0) {
+		bufsize += rret;
+		buf = xrealloc(buf, bufsize, sizeof(char));
+		memcpy(&buf[pos], static_buf, rret);
+		pos += rret;
+	}
+	if (rret == -1) {
+		_set_error(iceadmin, 0, errno, "read: [%s]:%s",
+			   iceadmin->hostname, iceadmin->service);
+		if (buf != NULL)
+			xfree(buf);
+		return (ICEADMIN_ERROR);
+	}
+	buf[pos] = '\0';
+	if (pos > 0 && buf[pos - 1] == '\n')
+		buf[pos - 1] = '\0';
+
+	if (bufsize_p != NULL)
+		*bufsize_p = bufsize;
+
+	*buf_p = buf;
+
+	return (ICEADMIN_SUCCESS);
+}
+
+int
+_iceadmin_connect(struct iceadmin *iceadmin, int *sock)
+{
+	if ((*sock = socket(iceadmin->domain, iceadmin->type,
+			   iceadmin->protocol)) == -1) {
+		_set_error(iceadmin, 0, errno, "socket");
+		return (ICEADMIN_ERROR);
+	}
+
+	if (connect(*sock, iceadmin->address, iceadmin->addrsize) == -1) {
+		_set_error(iceadmin, 0, errno, "connect: [%s]:%s",
+			   iceadmin->hostname, iceadmin->service);
+		close(*sock);
+		return (ICEADMIN_ERROR);
+	}
+
+	return (ICEADMIN_SUCCESS);
+}
+
+int
+_iceadmin_format_xmltree(struct iceadmin *iceadmin, char **buf_p,
+			 size_t *bufsize_p, xmlDocPtr doc)
+{
+	xmlNodePtr	  node;
+	char		 *buf;
+	size_t		  bufsize;
+	char		**strbuf;
+	size_t		  strbuf_size;
+
+	if ((node = xmlDocGetRootElement(doc)) == NULL) {
+		_set_error(iceadmin, 0, 0, "[%s]:%s: XML parse error (empty document)",
+			   iceadmin->hostname, iceadmin->service);
+		return (ICEADMIN_ERROR);
+	}
+
+	strbuf_size = 32;	/* This is also the maximum recursion depth. */
+	strbuf = xcalloc(strbuf_size, sizeof(char *));
+	bufsize = 1;
+	buf = xcalloc(bufsize, sizeof(char));
+	_iceadmin_walk_xmltree(&buf, &bufsize, doc, node, strbuf, strbuf_size, 0);
+	xfree(strbuf);
+
+	*buf_p = buf;
+	if (bufsize_p != NULL)
+		*bufsize_p = bufsize;
+
+	return (ICEADMIN_SUCCESS);
+}
+
+void
+_iceadmin_walk_xmltree(char **outbuf_p, size_t *outbuf_size, xmlDocPtr doc,
+		       xmlNodePtr node, char **strbuf,
+		       size_t strbuf_size, unsigned int depth)
+{
+	xmlChar 	 *content;
+	xmlAttrPtr	  attr;
+	char		 *attr_buf;
+	size_t		  attr_bufsize;
+
+	if (depth >= strbuf_size - 1) {
+		/* Maximum recursion depth reached */
+		return;
+	}
+
+	attr_buf = NULL;
+	attr_bufsize = 0;
+	if (node->properties != NULL) {
+		attr_bufsize = 2;
+		attr_buf = xmalloc(attr_bufsize);
+		attr_buf[0] = '[';
+		attr_buf[1] = '\0';
+		for (attr = node->properties; attr != NULL; attr = attr->next) {
+			attr_bufsize += strlen((const char *)attr->name);
+			attr_buf = xrealloc(attr_buf, attr_bufsize, sizeof(char));
+			strlcat(attr_buf, (const char *)attr->name, attr_bufsize);
+
+			if (attr->children != NULL && attr->children->content != NULL) {
+				attr_bufsize += strlen((const char *)attr->children->content) + 3;
+				attr_buf = xrealloc(attr_buf, attr_bufsize,
+						    sizeof(char));
+				strlcat(attr_buf, "=\"", attr_bufsize);
+				strlcat(attr_buf, (const char *)attr->children->content,
+					attr_bufsize);
+				strlcat(attr_buf, "\"", attr_bufsize);
+			}
+
+			if (attr->next != NULL) {
+				attr_bufsize++;
+				attr_buf = xrealloc(attr_buf, attr_bufsize,
+						    sizeof(char));
+				strlcat(attr_buf, ";", attr_bufsize);
+			}
+		}
+		attr_bufsize++;
+		attr_buf = xrealloc(attr_buf, attr_bufsize, sizeof(char));
+		strlcat(attr_buf, "]", attr_bufsize);
+	}
+
+	if (attr_buf != NULL) {
+		size_t	siz;
+
+		siz = strlen((const char *)node->name) + attr_bufsize;
+		strbuf[depth] = xcalloc(siz, sizeof(char));
+		snprintf(strbuf[depth], siz, "%s%s", (const char *)node->name, attr_buf);
+		xfree(attr_buf);
+	} else
+		strbuf[depth] = xstrdup((const char *)node->name);
+	strbuf[depth + 1] = NULL;
+
+	for (node = node->xmlChildrenNode; node != NULL; node = node->next) {
+		if ((content = xmlNodeListGetString(doc, node, 1)) != NULL) {
+			unsigned int	i;
+			char		*str;
+
+			for (i = 0; strbuf[i] != NULL; i++) {
+				*outbuf_size += strlen(strbuf[i]);
+				*outbuf_p = xrealloc(*outbuf_p, *outbuf_size, sizeof(char));
+				strlcat(*outbuf_p, strbuf[i], *outbuf_size);
+				if (strbuf[i + 1] != NULL) {
+					*outbuf_size += 1;
+					*outbuf_p = xrealloc(*outbuf_p, *outbuf_size, sizeof(char));
+					strlcat(*outbuf_p, ".", *outbuf_size);
+				}
+			}
+
+			str = _iceadmin_url_decode((const char *)content);
+			xmlFree(content);
+			*outbuf_size += strlen(str) + 6;
+			*outbuf_p = xrealloc(*outbuf_p, *outbuf_size, sizeof(char));
+			strlcat(*outbuf_p, " = \"", *outbuf_size);
+			strlcat(*outbuf_p, str, *outbuf_size);
+			xfree(str);
+			strlcat(*outbuf_p, "\"\n", *outbuf_size);
+		} else {
+			_iceadmin_walk_xmltree(outbuf_p, outbuf_size, doc,
+					       node, strbuf, strbuf_size,
+					       depth + 1);
+		}
+	}
+
+	xfree(strbuf[depth]);
+}
+
+int
+_iceadmin_get_xml_reply(char *buf, size_t bufsize, char *retbuf, size_t retbuf_size)
+{
+	xmlDocPtr	doc;
+	xmlNodePtr	node;
+	int		ret;
+
+	if ((doc = xmlParseMemory(buf, bufsize)) == NULL)
+		return (-1);
+
+	if ((node = xmlDocGetRootElement(doc)) == NULL) {
+		xmlFreeDoc(doc);
+		return (-1);
+	}
+
+	ret = -1;
+	for (node = node->xmlChildrenNode; node != NULL; node = node->next) {
+		if (xmlStrcmp(node->name, BAD_CAST "message") == 0) {
+			xmlChar *content;
+			char	*str;
+
+			content = xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+			if (content == NULL)
+				continue;
+			str = _iceadmin_url_decode((const char *)content);
+			xmlFree(content);
+			snprintf(retbuf, retbuf_size, "%s", str);
+			xfree(str);
+		} else if (xmlStrcmp(node->name, BAD_CAST "return") == 0) {
+			xmlChar *content;
+
+			content = xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+			if (content == NULL)
+				continue;
+			if (content[0] == '0')
+				ret = 0;
+			else if (content[0] == '1')
+				ret = 1;
+			xmlFree(content);
+		}
+	}
+
+	xmlFreeDoc(doc);
+
+	return (ret);
+}

Added: experimental/moritz/iceadmin/lib/iceadmin_internal.h
===================================================================
--- experimental/moritz/iceadmin/lib/iceadmin_internal.h	                        (rev 0)
+++ experimental/moritz/iceadmin/lib/iceadmin_internal.h	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,63 @@
+#ifndef __ICEADMIN_INTERNAL_H__
+#define __ICEADMIN_INTERNAL_H__
+
+#include <libxml/parser.h>
+
+/* Args: None */
+#define PFX_GET_STATS		"/admin/stats"
+/* Args: None */
+#define PFX_LIST_MOUNTS 	"/admin/listmounts"
+/* Args: mount= */
+#define PFX_LIST_CLIENTS	"/admin/listclients?"
+/* Args: mount=, (song= || artist=, title=) */
+#define PFX_UPD_METADATA	"/admin/metadata?mode=updinfo&"
+/* Args: mount=, fallback= */
+#define PFX_UPD_FALLBACK	"/admin/fallbacks?"
+/* Args: mount=, destination= */
+#define PFX_MOVE_CLIENTS	"/admin/moveclients?"
+/* Args: mount=, id= */
+#define PFX_KILL_CLIENT 	"/admin/killclient?"
+/* Args: mount= */
+#define PFX_KILL_SOURCE 	"/admin/killsource?"
+
+#ifndef BUFSIZ
+# define BUFSIZ 		1024
+#endif /* !BUFSIZ */
+
+struct iceadmin {
+	char		*hostname;
+	char		*service;
+	struct sockaddr *address;
+	int		 domain;
+	int		 type;
+	int		 protocol;
+	socklen_t	 addrsize;
+	char		*mountpoint;
+	char		*username;
+	char		*password;
+	char		*errmsg;
+	int		 errnum;
+	int		 gai_err;
+	int		 initialized;
+};
+
+char *	_iceadmin_url_encode(const char *);
+char *	_iceadmin_url_decode(const char *);
+char *	_iceadmin_auth_basic_string(const char *, const char *);
+int	_iceadmin_base64_encode(const char *, size_t, char **, size_t *);
+int	_iceadmin_read_http_reply(struct iceadmin *, int, unsigned int *);
+char *	_iceadmin_strnstr(const char *, const char *, size_t, size_t *);
+int	_iceadmin_read_xml_data(struct iceadmin *, int, char **, size_t *);
+int	_iceadmin_connect(struct iceadmin *, int *);
+int	_iceadmin_format_xmltree(struct iceadmin *, char **, size_t *,
+				 xmlDocPtr);
+void	_iceadmin_walk_xmltree(char **, size_t *, xmlDocPtr, xmlNodePtr,
+			       char **, size_t, unsigned int);
+int	_iceadmin_get_xml_reply(char *, size_t, char *, size_t);
+
+#ifndef HAVE_STRLCAT
+# define strlcat	_iceadmin_strlcat
+#endif /* !HAVE_STRLCAT */
+size_t	_iceadmin_strlcat(char *, const char *, size_t);
+
+#endif /* __ICEADMIN_INTERNAL_H__   */

Added: experimental/moritz/iceadmin/lib/libiceadmin.c
===================================================================
--- experimental/moritz/iceadmin/lib/libiceadmin.c	                        (rev 0)
+++ experimental/moritz/iceadmin/lib/libiceadmin.c	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,952 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+#include <errno.h>
+#ifdef HAVE_NETDB_H
+# include <netdb.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <libxml/parser.h>
+
+#include "iceadmin.h"
+#include "iceadmin_internal.h"
+#include "error.h"
+#include "xalloc.h"
+
+#ifndef SA_LEN
+# ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+#  define SA_LEN(x)	((x)->sa_len)
+# else
+#  define SA_LEN(x)	((x)->sa_family == AF_INET6 ? \
+			sizeof(struct sockaddr_in6) : \
+			sizeof(struct sockaddr_in))
+# endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
+#endif /* !SA_LEN */
+
+
+char	*_iceadmin_useragent = NULL;
+
+void
+iceadmin_initialize(void)
+{
+	xalloc_initialize_debug(2, stderr);
+	iceadmin_set_useragent(NULL);
+	xmlInitParser();
+}
+
+void
+iceadmin_shutdown(void)
+{
+	xmlCleanupParser();
+	if (_iceadmin_useragent != NULL)
+		xfree(_iceadmin_useragent);
+	xalloc_shutdown();
+}
+
+void
+iceadmin_get_error(struct iceadmin *iceadmin, char *buf, size_t bufsize)
+{
+	if (iceadmin == NULL || buf == NULL || bufsize == 0)
+		return;
+
+	_get_error_string(iceadmin, buf, bufsize);
+}
+
+void
+iceadmin_set_useragent(const char *agent)
+{
+	size_t	size;
+
+	if (_iceadmin_useragent != NULL)
+		xfree(_iceadmin_useragent);
+
+	if (agent == NULL) {
+		size = strlen(PACKAGE_NAME) + 1 + strlen(PACKAGE_VERSION) + 1;
+		_iceadmin_useragent = xmalloc(size);
+		snprintf(_iceadmin_useragent, size, "%s/%s",
+			 PACKAGE_NAME, PACKAGE_VERSION);
+	} else {
+		size = strlen(agent) + 1;
+		_iceadmin_useragent = xmalloc(size);
+		snprintf(_iceadmin_useragent, size, "%s", agent);
+	}
+}
+
+int
+iceadmin_new(struct iceadmin **iceadmin_p, const char *mountpoint,
+	     const char *hostname, const char *service, const char *username,
+	     const char *password)
+{
+	struct iceadmin *iceadmin = NULL;
+	int		 error, success;
+	struct addrinfo *res0, *res, hints;
+	const char	*last_error;
+	char		 connect_error[BUFSIZ];
+
+	if (iceadmin_p == NULL || hostname == NULL || service == NULL ||
+	    username == NULL || password == NULL)
+		return (ICEADMIN_ABUSE);
+
+	*iceadmin_p = xcalloc(1, sizeof(struct iceadmin));
+	iceadmin = *iceadmin_p;
+
+	iceadmin->hostname = xstrdup(hostname);
+	iceadmin->service = xstrdup(service);
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = PF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+
+	if ((error = getaddrinfo(hostname, service, &hints, &res0)) != 0) {
+		_set_error(iceadmin, 1, error, "getaddrinfo: [%s]:%s",
+			   hostname, service);
+		return (ICEADMIN_ERROR);
+	}
+
+	snprintf(connect_error, sizeof(connect_error), "connect: [%s]:%s",
+		 hostname, service);
+
+	success = 0;
+	last_error = NULL;
+	for (res = res0; res != NULL; res = res->ai_next) {
+		int	sock;
+
+		if ((sock = socket(res->ai_family, res->ai_socktype,
+				   res->ai_protocol)) == -1) {
+			last_error = "socket";
+			error = errno;
+			continue;
+		}
+
+		if ((connect(sock, res->ai_addr, res->ai_addrlen)) == -1) {
+			last_error = connect_error;
+			error = errno;
+			close(sock);
+			continue;
+		}
+
+		close(sock);
+		iceadmin->addrsize = res->ai_addrlen;
+		iceadmin->address = xcalloc(1, iceadmin->addrsize);
+		memcpy(iceadmin->address, res->ai_addr, iceadmin->addrsize);
+		iceadmin->domain = res->ai_family;
+		iceadmin->type = res->ai_socktype;
+		iceadmin->protocol = res->ai_protocol;
+		success = 1;
+		error = 0;
+		last_error = NULL;
+
+		break;
+	}
+	freeaddrinfo(res0);
+
+	if (!success) {
+		_set_error(iceadmin, 0, error, last_error);
+		return (ICEADMIN_ERROR);
+	}
+
+	if (mountpoint == NULL)
+		iceadmin->mountpoint = xstrdup("");
+	else
+		iceadmin->mountpoint = xstrdup(mountpoint);
+	iceadmin->username = xstrdup(username);
+	iceadmin->password = xstrdup(password);
+
+	iceadmin->initialized = 1;
+
+	return (ICEADMIN_SUCCESS);
+}
+
+int
+iceadmin_delete_c(struct iceadmin **iceadmin_p)
+{
+	struct iceadmin *iceadmin;
+
+	if (iceadmin_p == NULL || (iceadmin = *iceadmin_p) == NULL ||
+	    iceadmin == NULL)
+		return (ICEADMIN_ABUSE);
+
+	_clear_error(iceadmin);
+
+	if (iceadmin->hostname != NULL)
+		xfree(iceadmin->hostname);
+	if (iceadmin->service != NULL)
+		xfree(iceadmin->service);
+	if (iceadmin->address != NULL)
+		xfree(iceadmin->address);
+	if (iceadmin->mountpoint != NULL)
+		xfree(iceadmin->mountpoint);
+	if (iceadmin->username != NULL)
+		xfree(iceadmin->username);
+	if (iceadmin->password != NULL)
+		xfree(iceadmin->password);
+
+	memset(iceadmin, 0, sizeof(struct iceadmin));
+	xfree(*iceadmin_p);
+	*iceadmin_p = NULL;
+
+	return (ICEADMIN_SUCCESS);
+}
+
+int
+iceadmin_free_c(void **buf_p)
+{
+	char	*buf;
+
+	if (buf_p == NULL)
+		return (ICEADMIN_ERROR);
+
+	if ((buf = (char *)*buf_p) != NULL) {
+		xfree(buf);
+		*buf_p = NULL;
+	}
+
+	return (ICEADMIN_SUCCESS);
+}
+
+int
+iceadmin_cmd_get_stats(struct iceadmin *iceadmin,
+		       char **buf_p, size_t *bufsize_p)
+{
+	char		*buf;
+	size_t		 bufsize;
+	xmlDocPtr	 doc;
+
+	if (iceadmin == NULL || buf_p == NULL)
+		return (ICEADMIN_ABUSE);
+	if (iceadmin->initialized != 1)
+		return (ICEADMIN_BADHANDLE);
+
+	if (iceadmin_cmd_get_xmlstats(iceadmin, &buf, &bufsize) != ICEADMIN_SUCCESS)
+		return (ICEADMIN_ERROR);
+
+	if ((doc = xmlParseMemory(buf, bufsize)) == NULL) {
+		_set_error(iceadmin, 0, 0, "[%s]:%s: XML parse error (not well-formed)",
+			   iceadmin->hostname, iceadmin->service);
+		xfree(buf);
+		return (ICEADMIN_ERROR);
+	}
+	xfree(buf);
+
+	if (_iceadmin_format_xmltree(iceadmin, &buf, &bufsize, doc) != ICEADMIN_SUCCESS) {
+		xmlFreeDoc(doc);
+		return (ICEADMIN_ERROR);
+	}
+	xmlFreeDoc(doc);
+
+	*buf_p = buf;
+	if (bufsize_p != NULL)
+		*bufsize_p = bufsize;
+
+	return (ICEADMIN_SUCCESS);
+}
+
+int
+iceadmin_cmd_get_xmlstats(struct iceadmin *iceadmin,
+			  char **buf_p, size_t *bufsize_p)
+{
+	char		*buf, *auth;
+	size_t		 bufsize;
+	int		 sock;
+	char		 static_buf[BUFSIZ];
+	unsigned int	 httpret;
+	int		 ret;
+
+	if (iceadmin == NULL || buf_p == NULL)
+		return (ICEADMIN_ABUSE);
+	if (iceadmin->initialized != 1)
+		return (ICEADMIN_BADHANDLE);
+
+	if (_iceadmin_connect(iceadmin, &sock) != ICEADMIN_SUCCESS)
+		return (ICEADMIN_ERROR);
+
+	auth = _iceadmin_auth_basic_string(iceadmin->username,
+					   iceadmin->password);
+	ret = snprintf(static_buf, sizeof(static_buf), "GET %s HTTP/1.0\r\nUser-Agent: %s\r\n%s\r\n",
+		       PFX_GET_STATS, _iceadmin_useragent, auth);
+	if (ret < 0 || ret >= (int)sizeof(static_buf)) {
+		_set_error(iceadmin, 0, 0, "Bad request -- too long");
+		xfree(auth);
+		return (ICEADMIN_ERROR);
+	}
+	xfree(auth);
+
+	if (write(sock, static_buf, strlen(static_buf)) == -1) {
+		_set_error(iceadmin, 0, errno, "write: [%s]:%s",
+			   iceadmin->hostname, iceadmin->service);
+		close(sock);
+		return (ICEADMIN_ERROR);
+	}
+
+	if (_iceadmin_read_http_reply(iceadmin, sock, &httpret)
+	    != ICEADMIN_SUCCESS) {
+		close(sock);
+		return (ICEADMIN_ERROR);
+	}
+
+	if (_iceadmin_read_xml_data(iceadmin, sock, &buf, &bufsize)
+	    != ICEADMIN_SUCCESS) {
+		close(sock);
+		return (ICEADMIN_ERROR);
+	}
+	close(sock);
+
+	*buf_p = buf;
+	if (bufsize_p != NULL)
+		*bufsize_p = bufsize;
+
+	return (ICEADMIN_SUCCESS);
+}
+
+int
+iceadmin_cmd_list_mounts(struct iceadmin *iceadmin,
+			 char **buf_p, size_t *bufsize_p)
+{
+	char		*buf;
+	size_t		 bufsize;
+	xmlDocPtr	 doc;
+
+	if (iceadmin == NULL || buf_p == NULL)
+		return (ICEADMIN_ABUSE);
+	if (iceadmin->initialized != 1)
+		return (ICEADMIN_BADHANDLE);
+
+	if (iceadmin_cmd_list_xmlmounts(iceadmin, &buf, &bufsize) != ICEADMIN_SUCCESS)
+		return (ICEADMIN_ERROR);
+
+	if ((doc = xmlParseMemory(buf, bufsize)) == NULL) {
+		_set_error(iceadmin, 0, 0, "[%s]:%s: XML parse error (not well-formed)",
+			   iceadmin->hostname, iceadmin->service);
+		xfree(buf);
+		return (ICEADMIN_ERROR);
+	}
+	xfree(buf);
+
+	if (_iceadmin_format_xmltree(iceadmin, &buf, &bufsize, doc) != ICEADMIN_SUCCESS) {
+		xmlFreeDoc(doc);
+		return (ICEADMIN_ERROR);
+	}
+	xmlFreeDoc(doc);
+
+	*buf_p = buf;
+	if (bufsize_p != NULL)
+		*bufsize_p = bufsize;
+
+	return (ICEADMIN_SUCCESS);
+}
+
+int
+iceadmin_cmd_list_xmlmounts(struct iceadmin *iceadmin,
+			    char **buf_p, size_t *bufsize_p)
+{
+	char		*buf, *auth;
+	size_t		 bufsize;
+	int		 sock;
+	char		 static_buf[BUFSIZ];
+	unsigned int	 httpret;
+	int		 ret;
+
+	if (iceadmin == NULL || buf_p == NULL)
+		return (ICEADMIN_ABUSE);
+	if (iceadmin->initialized != 1)
+		return (ICEADMIN_BADHANDLE);
+
+	if (_iceadmin_connect(iceadmin, &sock) != ICEADMIN_SUCCESS)
+		return (ICEADMIN_ERROR);
+
+	auth = _iceadmin_auth_basic_string(iceadmin->username,
+					   iceadmin->password);
+	ret = snprintf(static_buf, sizeof(static_buf), "GET %s HTTP/1.0\r\nUser-Agent: %s\r\n%s\r\n",
+		       PFX_LIST_MOUNTS, _iceadmin_useragent, auth);
+	if (ret < 0 || ret >= (int)sizeof(static_buf)) {
+		_set_error(iceadmin, 0, 0, "Bad request -- too long");
+		xfree(auth);
+		return (ICEADMIN_ERROR);
+	}
+	xfree(auth);
+
+	if (write(sock, static_buf, strlen(static_buf)) == -1) {
+		_set_error(iceadmin, 0, errno, "write: [%s]:%s",
+			   iceadmin->hostname, iceadmin->service);
+		close(sock);
+		return (ICEADMIN_ERROR);
+	}
+
+	if (_iceadmin_read_http_reply(iceadmin, sock, &httpret)
+	    != ICEADMIN_SUCCESS) {
+		close(sock);
+		return (ICEADMIN_ERROR);
+	}
+
+	if (_iceadmin_read_xml_data(iceadmin, sock, &buf, &bufsize)
+	    != ICEADMIN_SUCCESS) {
+		close(sock);
+		return (ICEADMIN_ERROR);
+	}
+	close(sock);
+
+	*buf_p = buf;
+	if (bufsize_p != NULL)
+		*bufsize_p = bufsize;
+
+	return (ICEADMIN_SUCCESS);
+}
+
+int
+iceadmin_cmd_list_clients(struct iceadmin *iceadmin,
+			  char **buf_p, size_t *bufsize_p)
+{
+	char		*buf;
+	size_t		 bufsize;
+	xmlDocPtr	 doc;
+
+	if (iceadmin == NULL || buf_p == NULL)
+		return (ICEADMIN_ABUSE);
+	if (iceadmin->initialized != 1)
+		return (ICEADMIN_BADHANDLE);
+	if (iceadmin->mountpoint[0] == '\0')
+		return (ICEADMIN_ERROR);
+
+	if (iceadmin_cmd_list_xmlclients(iceadmin, &buf, &bufsize) != ICEADMIN_SUCCESS)
+		return (ICEADMIN_ERROR);
+
+	if ((doc = xmlParseMemory(buf, bufsize)) == NULL) {
+		_set_error(iceadmin, 0, 0, "[%s]:%s: XML parse error (not well-formed)",
+			   iceadmin->hostname, iceadmin->service);
+		xfree(buf);
+		return (ICEADMIN_ERROR);
+	}
+	xfree(buf);
+
+	if (_iceadmin_format_xmltree(iceadmin, &buf, &bufsize, doc) != ICEADMIN_SUCCESS) {
+		xmlFreeDoc(doc);
+		return (ICEADMIN_ERROR);
+	}
+	xmlFreeDoc(doc);
+
+	*buf_p = buf;
+	if (bufsize_p != NULL)
+		*bufsize_p = bufsize;
+
+	return (ICEADMIN_SUCCESS);
+}
+
+int
+iceadmin_cmd_list_xmlclients(struct iceadmin *iceadmin,
+			     char **buf_p, size_t *bufsize_p)
+{
+	char		*buf, *auth;
+	size_t		 bufsize;
+	int		 sock;
+	char		 static_buf[BUFSIZ];
+	unsigned int	 httpret;
+	int		 ret;
+	char		*enc_mount;
+
+	if (iceadmin == NULL || buf_p == NULL)
+		return (ICEADMIN_ABUSE);
+	if (iceadmin->initialized != 1)
+		return (ICEADMIN_BADHANDLE);
+	if (iceadmin->mountpoint[0] == '\0')
+		return (ICEADMIN_ERROR);
+
+	if (_iceadmin_connect(iceadmin, &sock) != ICEADMIN_SUCCESS)
+		return (ICEADMIN_ERROR);
+
+	enc_mount = _iceadmin_url_encode(iceadmin->mountpoint);
+	auth = _iceadmin_auth_basic_string(iceadmin->username,
+					   iceadmin->password);
+	ret = snprintf(static_buf, sizeof(static_buf), "GET %smount=%s HTTP/1.0\r\nUser-Agent: %s\r\n%s\r\n",
+		       PFX_LIST_CLIENTS, enc_mount, _iceadmin_useragent, auth);
+	if (ret < 0 || ret >= (int)sizeof(static_buf)) {
+		_set_error(iceadmin, 0, 0, "Bad request -- too long");
+		xfree(auth);
+		xfree(enc_mount);
+		return (ICEADMIN_ERROR);
+	}
+	xfree(auth);
+	xfree(enc_mount);
+
+	if (write(sock, static_buf, strlen(static_buf)) == -1) {
+		_set_error(iceadmin, 0, errno, "write: [%s]:%s",
+			   iceadmin->hostname, iceadmin->service);
+		close(sock);
+		return (ICEADMIN_ERROR);
+	}
+
+	httpret = 0;
+	if (_iceadmin_read_http_reply(iceadmin, sock, &httpret)
+	    != ICEADMIN_SUCCESS) {
+		close(sock);
+		if (httpret == 400)
+			_set_error(iceadmin, 0, 0, "[%s]:%s: Mountpoint '%s' not found",
+				   iceadmin->hostname, iceadmin->service,
+				   iceadmin->mountpoint);
+		return (ICEADMIN_ERROR);
+	}
+
+	if (_iceadmin_read_xml_data(iceadmin, sock, &buf, &bufsize)
+	    != ICEADMIN_SUCCESS) {
+		close(sock);
+		return (ICEADMIN_ERROR);
+	}
+	close(sock);
+
+	*buf_p = buf;
+	if (bufsize_p != NULL)
+		*bufsize_p = bufsize;
+
+	return (ICEADMIN_SUCCESS);
+}
+
+int
+iceadmin_cmd_kill_source(struct iceadmin *iceadmin)
+{
+	char		*auth;
+	int		 sock;
+	char		 static_buf[BUFSIZ];
+	unsigned int	 httpret;
+	int		 ret;
+	char		*enc_mount;
+
+	if (iceadmin == NULL)
+		return (ICEADMIN_ABUSE);
+	if (iceadmin->initialized != 1)
+		return (ICEADMIN_BADHANDLE);
+	if (iceadmin->mountpoint[0] == '\0')
+		return (ICEADMIN_ERROR);
+
+	if (_iceadmin_connect(iceadmin, &sock) != ICEADMIN_SUCCESS)
+		return (ICEADMIN_ERROR);
+
+	enc_mount = _iceadmin_url_encode(iceadmin->mountpoint);
+	auth = _iceadmin_auth_basic_string(iceadmin->username,
+					   iceadmin->password);
+	ret = snprintf(static_buf, sizeof(static_buf), "GET %smount=%s HTTP/1.0\r\nUser-Agent: %s\r\n%s\r\n",
+		       PFX_KILL_SOURCE, enc_mount, _iceadmin_useragent, auth);
+	if (ret < 0 || ret >= (int)sizeof(static_buf)) {
+		_set_error(iceadmin, 0, 0, "Bad request -- too long");
+		xfree(auth);
+		xfree(enc_mount);
+		return (ICEADMIN_ERROR);
+	}
+	xfree(auth);
+	xfree(enc_mount);
+
+	if (write(sock, static_buf, strlen(static_buf)) == -1) {
+		_set_error(iceadmin, 0, errno, "write: [%s]:%s",
+			   iceadmin->hostname, iceadmin->service);
+		close(sock);
+		return (ICEADMIN_ERROR);
+	}
+
+	httpret = 0;
+	if (_iceadmin_read_http_reply(iceadmin, sock, &httpret)
+	    != ICEADMIN_SUCCESS) {
+		close(sock);
+		if (httpret == 400)
+			_set_error(iceadmin, 0, 0, "[%s]:%s: Mountpoint '%s' not found",
+				   iceadmin->hostname, iceadmin->service,
+				   iceadmin->mountpoint);
+		return (ICEADMIN_ERROR);
+	}
+	close(sock);
+
+	return (ICEADMIN_SUCCESS);
+}
+
+int
+iceadmin_cmd_kill_client(struct iceadmin *iceadmin, unsigned int client_id)
+{
+	char		*buf, *auth;
+	size_t		 bufsize;
+	int		 sock;
+	char		 static_buf[BUFSIZ];
+	unsigned int	 httpret;
+	int		 ret;
+	char		*enc_mount;
+
+	if (iceadmin == NULL)
+		return (ICEADMIN_ABUSE);
+	if (iceadmin->initialized != 1)
+		return (ICEADMIN_BADHANDLE);
+	if (iceadmin->mountpoint[0] == '\0')
+		return (ICEADMIN_ERROR);
+
+	if (_iceadmin_connect(iceadmin, &sock) != ICEADMIN_SUCCESS)
+		return (ICEADMIN_ERROR);
+
+	enc_mount = _iceadmin_url_encode(iceadmin->mountpoint);
+	auth = _iceadmin_auth_basic_string(iceadmin->username,
+					   iceadmin->password);
+	ret = snprintf(static_buf, sizeof(static_buf), "GET %smount=%s&id=%u HTTP/1.0\r\nUser-Agent: %s\r\n%s\r\n",
+		       PFX_KILL_CLIENT, enc_mount, client_id,
+		       _iceadmin_useragent, auth);
+	if (ret < 0 || ret >= (int)sizeof(static_buf)) {
+		_set_error(iceadmin, 0, 0, "Bad request -- too long");
+		xfree(auth);
+		xfree(enc_mount);
+		return (ICEADMIN_ERROR);
+	}
+	xfree(auth);
+	xfree(enc_mount);
+
+	if (write(sock, static_buf, strlen(static_buf)) == -1) {
+		_set_error(iceadmin, 0, errno, "write: [%s]:%s",
+			   iceadmin->hostname, iceadmin->service);
+		close(sock);
+		return (ICEADMIN_ERROR);
+	}
+
+	httpret = 0;
+	if (_iceadmin_read_http_reply(iceadmin, sock, &httpret)
+	    != ICEADMIN_SUCCESS) {
+		close(sock);
+		if (httpret == 400)
+			_set_error(iceadmin, 0, 0, "[%s]:%s: Mountpoint '%s' not found",
+				   iceadmin->hostname, iceadmin->service,
+				   iceadmin->mountpoint);
+		return (ICEADMIN_ERROR);
+	}
+
+	if (_iceadmin_read_xml_data(iceadmin, sock, &buf, &bufsize)
+	    != ICEADMIN_SUCCESS) {
+		close(sock);
+		return (ICEADMIN_ERROR);
+	}
+	close(sock);
+
+	ret = _iceadmin_get_xml_reply(buf, bufsize, static_buf, sizeof(static_buf));
+	xfree(buf);
+
+	switch (ret) {
+	case 1:
+		/* Okay */
+		break;
+	case 0:
+		_set_error(iceadmin, 0, 0, "[%s]:%s: %s",
+			   iceadmin->hostname, iceadmin->service,
+			   static_buf);
+		return (ICEADMIN_FAILURE);
+	default:
+		_set_error(iceadmin, 0, 0, "[%s]:%s: Bad XML reply",
+			   iceadmin->hostname, iceadmin->service);
+		return (ICEADMIN_ERROR);
+	}
+
+	return (ICEADMIN_SUCCESS);
+}
+
+int
+iceadmin_cmd_move_clients(struct iceadmin *iceadmin, const char *destination)
+{
+	char		*auth;
+	int		 sock;
+	char		 static_buf[BUFSIZ];
+	unsigned int	 httpret;
+	int		 ret;
+	char            *enc_mount, *enc_dest;
+
+	if (iceadmin == NULL || destination == NULL)
+		return (ICEADMIN_ABUSE);
+	if (iceadmin->initialized != 1)
+		return (ICEADMIN_BADHANDLE);
+	if (iceadmin->mountpoint[0] == '\0')
+		return (ICEADMIN_ERROR);
+
+	if (strcmp(iceadmin->mountpoint, destination) == 0) {
+		_set_error(iceadmin, 0, 0, "[%s]:%s: Mountpoint '%s' and destination '%s' are identical",
+			   iceadmin->hostname, iceadmin->service,
+			   iceadmin->mountpoint, destination);
+		return (ICEADMIN_ERROR);
+	}
+
+	if (_iceadmin_connect(iceadmin, &sock) != ICEADMIN_SUCCESS)
+		return (ICEADMIN_ERROR);
+
+	enc_mount = _iceadmin_url_encode(iceadmin->mountpoint);
+        enc_dest = _iceadmin_url_encode(destination);
+	auth = _iceadmin_auth_basic_string(iceadmin->username,
+					   iceadmin->password);
+	ret = snprintf(static_buf, sizeof(static_buf), "GET %smount=%s&destination=%s HTTP/1.0\r\nUser-Agent: %s\r\n%s\r\n",
+		       PFX_MOVE_CLIENTS, enc_mount, enc_dest,
+		       _iceadmin_useragent, auth);
+	if (ret < 0 || ret >= (int)sizeof(static_buf)) {
+		_set_error(iceadmin, 0, 0, "Bad request -- too long");
+		xfree(auth);
+		xfree(enc_dest);
+		xfree(enc_mount);
+		return (ICEADMIN_ERROR);
+	}
+	xfree(auth);
+	xfree(enc_dest);
+	xfree(enc_mount);
+
+	if (write(sock, static_buf, strlen(static_buf)) == -1) {
+		_set_error(iceadmin, 0, errno, "write: [%s]:%s",
+			   iceadmin->hostname, iceadmin->service);
+		close(sock);
+		return (ICEADMIN_ERROR);
+	}
+
+	httpret = 0;
+	if (_iceadmin_read_http_reply(iceadmin, sock, &httpret)
+	    != ICEADMIN_SUCCESS) {
+		close(sock);
+		if (httpret == 400)
+			_set_error(iceadmin, 0, 0, "[%s]:%s: Mountpoint '%s' or destination '%s' not found",
+				   iceadmin->hostname, iceadmin->service,
+				   iceadmin->mountpoint, destination);
+		return (ICEADMIN_ERROR);
+	}
+	close(sock);
+
+	return (ICEADMIN_SUCCESS);
+}
+
+int
+iceadmin_cmd_update_fallback(struct iceadmin *iceadmin, const char *fallback)
+{
+	char		*auth;
+	int		 sock;
+	char		 static_buf[BUFSIZ];
+	unsigned int	 httpret;
+	int		 ret;
+	char		*enc_mount, *enc_fallback;
+
+	if (iceadmin == NULL || fallback == NULL)
+		return (ICEADMIN_ABUSE);
+	if (iceadmin->initialized != 1)
+		return (ICEADMIN_BADHANDLE);
+	if (iceadmin->mountpoint[0] == '\0')
+		return (ICEADMIN_ERROR);
+
+	if (_iceadmin_connect(iceadmin, &sock) != ICEADMIN_SUCCESS)
+		return (ICEADMIN_ERROR);
+
+	enc_mount = _iceadmin_url_encode(iceadmin->mountpoint);
+	enc_fallback = _iceadmin_url_encode(fallback);
+	auth = _iceadmin_auth_basic_string(iceadmin->username,
+					   iceadmin->password);
+	ret = snprintf(static_buf, sizeof(static_buf), "GET %smount=%s&fallback=%s HTTP/1.0\r\nUser-Agent: %s\r\n%s\r\n",
+		       PFX_UPD_FALLBACK, enc_mount, enc_fallback,
+		       _iceadmin_useragent, auth);
+	if (ret < 0 || ret >= (int)sizeof(static_buf)) {
+		_set_error(iceadmin, 0, 0, "Bad request -- too long");
+		xfree(auth);
+		xfree(enc_fallback);
+		xfree(enc_mount);
+		return (ICEADMIN_ERROR);
+	}
+	xfree(auth);
+	xfree(enc_fallback);
+	xfree(enc_mount);
+
+	if (write(sock, static_buf, strlen(static_buf)) == -1) {
+		_set_error(iceadmin, 0, errno, "write: [%s]:%s",
+			   iceadmin->hostname, iceadmin->service);
+		close(sock);
+		return (ICEADMIN_ERROR);
+	}
+
+	httpret = 0;
+	if (_iceadmin_read_http_reply(iceadmin, sock, &httpret)
+	    != ICEADMIN_SUCCESS) {
+		close(sock);
+		if (httpret == 400)
+			_set_error(iceadmin, 0, 0, "[%s]:%s: Mountpoint '%s' not found",
+				   iceadmin->hostname, iceadmin->service,
+				   iceadmin->mountpoint);
+		return (ICEADMIN_ERROR);
+	}
+	close(sock);
+
+	return (ICEADMIN_SUCCESS);
+}
+
+int
+iceadmin_cmd_update_metadata(struct iceadmin *iceadmin, const char *metadata)
+{
+	char		*buf, *auth;
+	size_t		 bufsize;
+	int		 sock;
+	char		 static_buf[BUFSIZ];
+	unsigned int	 httpret;
+	int		 ret;
+	char		*enc_mount, *enc_meta;
+
+	if (iceadmin == NULL || metadata == NULL)
+		return (ICEADMIN_ABUSE);
+	if (iceadmin->initialized != 1)
+		return (ICEADMIN_BADHANDLE);
+	if (iceadmin->mountpoint[0] == '\0')
+		return (ICEADMIN_ERROR);
+
+	if (_iceadmin_connect(iceadmin, &sock) != ICEADMIN_SUCCESS)
+		return (ICEADMIN_ERROR);
+
+	enc_mount = _iceadmin_url_encode(iceadmin->mountpoint);
+	enc_meta = _iceadmin_url_encode(metadata);
+	auth = _iceadmin_auth_basic_string(iceadmin->username,
+					   iceadmin->password);
+	ret = snprintf(static_buf, sizeof(static_buf), "GET %smount=%s&song=%s HTTP/1.0\r\nUser-Agent: %s\r\n%s\r\n",
+		       PFX_UPD_METADATA, enc_mount, enc_meta,
+		       _iceadmin_useragent, auth);
+	if (ret < 0 || ret >= (int)sizeof(static_buf)) {
+		_set_error(iceadmin, 0, 0, "Bad request -- too long");
+		xfree(auth);
+		xfree(enc_meta);
+		xfree(enc_mount);
+		return (ICEADMIN_ERROR);
+	}
+	xfree(auth);
+	xfree(enc_meta);
+	xfree(enc_mount);
+
+	if (write(sock, static_buf, strlen(static_buf)) == -1) {
+		_set_error(iceadmin, 0, errno, "write: [%s]:%s",
+			   iceadmin->hostname, iceadmin->service);
+		close(sock);
+		return (ICEADMIN_ERROR);
+	}
+
+	httpret = 0;
+	if (_iceadmin_read_http_reply(iceadmin, sock, &httpret)
+	    != ICEADMIN_SUCCESS) {
+		close(sock);
+		if (httpret == 400)
+			_set_error(iceadmin, 0, 0, "[%s]:%s: Mountpoint '%s' not found",
+				   iceadmin->hostname, iceadmin->service,
+				   iceadmin->mountpoint);
+		return (ICEADMIN_ERROR);
+	}
+
+	if (_iceadmin_read_xml_data(iceadmin, sock, &buf, &bufsize)
+	    != ICEADMIN_SUCCESS) {
+		close(sock);
+		return (ICEADMIN_ERROR);
+	}
+	close(sock);
+
+	ret = _iceadmin_get_xml_reply(buf, bufsize, static_buf, sizeof(static_buf));
+	xfree(buf);
+
+	switch (ret) {
+	case 1:
+		/* Okay */
+		break;
+	case 0:
+		_set_error(iceadmin, 0, 0, "[%s]:%s: %s",
+			   iceadmin->hostname, iceadmin->service,
+			   static_buf);
+		return (ICEADMIN_FAILURE);
+	default:
+		_set_error(iceadmin, 0, 0, "[%s]:%s: Bad XML reply",
+			   iceadmin->hostname, iceadmin->service);
+		return (ICEADMIN_ERROR);
+	}
+
+	return (ICEADMIN_SUCCESS);
+}
+
+int
+iceadmin_cmd_update_artist_title(struct iceadmin *iceadmin,
+				 const char *artist, const char *title)
+{
+	char		*buf, *auth;
+	size_t		 bufsize;
+	int		 sock;
+	char		 static_buf[BUFSIZ];
+	unsigned int	 httpret;
+	int		 ret;
+	char		*enc_mount, *enc_artist, *enc_title;
+
+	if (iceadmin == NULL || artist == NULL || title == NULL)
+		return (ICEADMIN_ABUSE);
+	if (iceadmin->initialized != 1)
+		return (ICEADMIN_BADHANDLE);
+	if (iceadmin->mountpoint[0] == '\0')
+		return (ICEADMIN_ERROR);
+
+	if (_iceadmin_connect(iceadmin, &sock) != ICEADMIN_SUCCESS)
+		return (ICEADMIN_ERROR);
+
+	enc_mount = _iceadmin_url_encode(iceadmin->mountpoint);
+	enc_artist = _iceadmin_url_encode(artist);
+	enc_title = _iceadmin_url_encode(title);
+	auth = _iceadmin_auth_basic_string(iceadmin->username,
+					   iceadmin->password);
+	ret = snprintf(static_buf, sizeof(static_buf), "GET %smount=%s&artist=%s&title=%s HTTP/1.0\r\nUser-Agent: %s\r\n%s\r\n",
+		       PFX_UPD_METADATA, enc_mount, enc_artist, enc_title,
+		       _iceadmin_useragent, auth);
+	if (ret < 0 || ret >= (int)sizeof(static_buf)) {
+		_set_error(iceadmin, 0, 0, "Bad request -- too long");
+		xfree(auth);
+		xfree(enc_title);
+		xfree(enc_artist);
+		xfree(enc_mount);
+		return (ICEADMIN_ERROR);
+	}
+	xfree(auth);
+	xfree(enc_title);
+	xfree(enc_artist);
+	xfree(enc_mount);
+
+	if (write(sock, static_buf, strlen(static_buf)) == -1) {
+		_set_error(iceadmin, 0, errno, "write: [%s]:%s",
+			   iceadmin->hostname, iceadmin->service);
+		close(sock);
+		return (ICEADMIN_ERROR);
+	}
+
+	httpret = 0;
+	if (_iceadmin_read_http_reply(iceadmin, sock, &httpret)
+	    != ICEADMIN_SUCCESS) {
+		close(sock);
+		if (httpret == 400)
+			_set_error(iceadmin, 0, 0, "[%s]:%s: Mountpoint '%s' not found",
+				   iceadmin->hostname, iceadmin->service,
+				   iceadmin->mountpoint);
+		return (ICEADMIN_ERROR);
+	}
+
+	if (_iceadmin_read_xml_data(iceadmin, sock, &buf, &bufsize)
+	    != ICEADMIN_SUCCESS) {
+		close(sock);
+		return (ICEADMIN_ERROR);
+	}
+	close(sock);
+
+	ret = _iceadmin_get_xml_reply(buf, bufsize, static_buf, sizeof(static_buf));
+	xfree(buf);
+
+	switch (ret) {
+	case 1:
+		/* Okay */
+		break;
+	case 0:
+		_set_error(iceadmin, 0, 0, "[%s]:%s: %s",
+			   iceadmin->hostname, iceadmin->service,
+			   static_buf);
+		return (ICEADMIN_FAILURE);
+	default:
+		_set_error(iceadmin, 0, 0, "[%s]:%s: Bad XML reply",
+			   iceadmin->hostname, iceadmin->service);
+		return (ICEADMIN_ERROR);
+	}
+
+	return (ICEADMIN_SUCCESS);
+}

Added: experimental/moritz/iceadmin/lib/strlcat.c
===================================================================
--- experimental/moritz/iceadmin/lib/strlcat.c	                        (rev 0)
+++ experimental/moritz/iceadmin/lib/strlcat.c	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,60 @@
+/*	$OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $	*/
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller at courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+# incldue "config.h"
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "iceadmin_internal.h"
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left).  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+size_t
+_iceadmin_strlcat(char *dst, const char *src, size_t siz)
+{
+	char *d = dst;
+	const char *s = src;
+	size_t n = siz;
+	size_t dlen;
+
+	/* Find the end of dst and adjust bytes left but don't go past end */
+	while (n-- != 0 && *d != '\0')
+		d++;
+	dlen = d - dst;
+	n = siz - dlen;
+
+	if (n == 0)
+		return(dlen + strlen(s));
+	while (*s != '\0') {
+		if (n != 1) {
+			*d++ = *s;
+			n--;
+		}
+		s++;
+	}
+	*d = '\0';
+
+	return(dlen + (s - src));	/* count does not include NUL */
+}

Added: experimental/moritz/iceadmin/lib/strnstr.txt
===================================================================
--- experimental/moritz/iceadmin/lib/strnstr.txt	                        (rev 0)
+++ experimental/moritz/iceadmin/lib/strnstr.txt	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,34 @@
+License of the code used in _iceadmin_strnstr()
+
+/*-
+ * Copyright (c) 2001 Mike Barcroft <mike at FreeBSD.org>
+ * Copyright (c) 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */

Added: experimental/moritz/iceadmin/lib/xalloc.c
===================================================================
--- experimental/moritz/iceadmin/lib/xalloc.c	                        (rev 0)
+++ experimental/moritz/iceadmin/lib/xalloc.c	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,772 @@
+/*
+ * Copyright (C) 2007  Moritz Grimm <mdgrimm at gmx.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "xalloc.h"
+
+#ifdef USE_XALLOC
+
+#ifndef SIZE_T_MAX
+# define SIZE_T_MAX		((size_t)-1)
+#endif
+#ifndef va_copy
+# define va_copy(d, s)		(d) = (s)
+#endif
+#define XALLOC_DBGLVL_MAX	2
+
+#if defined(XALLOC_DEBUG) && defined(XALLOC_SILENT)
+# undef XALLOC_SILENT
+#endif /* XALLOC_DEBUG && XALLOC_SILENT */
+
+#ifdef THREAD_SAFE
+# include <pthread.h>
+static pthread_mutex_t	 xalloc_mutex;
+static pthread_mutex_t	 strerror_mutex;
+# define XALLOC_LOCK(mtx) do {						\
+	int error;							\
+	if ((error = pthread_mutex_lock(&mtx)) != 0)			\
+		_xalloc_error(error, "XALLOC: Internal error in %s:%u: pthread_mutex_lock()", \
+			      __FILE__, __LINE__);			\
+} while (0)
+# define XALLOC_UNLOCK(mtx) do {					\
+	int error;							\
+	if ((error = pthread_mutex_unlock(&mtx)) != 0)			\
+		_xalloc_error(error, "XALLOC: Internal error in %s:%u: pthread_mutex_unlock()", \
+			      __FILE__, __LINE__);			\
+} while (0)
+#else
+# define XALLOC_LOCK(mtx)	((void)0)
+# define XALLOC_UNLOCK(mtx)	((void)0)
+#endif /* THREAD_SAFE */
+
+#ifdef XALLOC_DEBUG
+# include <sys/tree.h>
+
+int	_memory_cmp(void *, void *);
+
+struct memory {
+	RB_ENTRY(memory) entry;
+	void		*ptr;
+	unsigned int	 id;
+	size_t		 size;
+	const char	*allocated_by;
+	unsigned int	 allocated_in_line;
+	const char	*reallocated_by;
+	unsigned int	 reallocated_in_line;
+	const char	*freed_by;
+	unsigned int	 freed_in_line;
+};
+RB_HEAD(memory_tree, memory) memory_tree_head = RB_INITIALIZER(&memory_tree_head);
+RB_PROTOTYPE(memory_tree, memory, entry, _memory_cmp)
+
+void	_memory_free(struct memory **);
+#endif /* XALLOC_DEBUG */
+
+void	_xalloc_warn(const char *, ...);
+void	_xalloc_error(int, const char *, ...);
+void	_xalloc_fatal(const char *, ...);
+void	_xalloc_debug_printf(unsigned int, const char *, ...);
+#ifdef XALLOC_WITH_XASPRINTF
+int	_xalloc_vasprintf(char **, const char *, va_list, size_t *);
+#endif /* XALLOC_WITH_XASPRINTF */
+
+static unsigned int	  debug_level;
+static FILE		 *debug_output;
+#ifdef XALLOC_DEBUG
+static unsigned int	  xalloc_next_id;
+#endif
+static int		  xalloc_initialized;
+static size_t		  xalloc_allocated;
+static size_t		  xalloc_total;
+static size_t		  xalloc_peak;
+static size_t		  xalloc_freed;
+static void *		(*real_malloc)(size_t) = NULL;
+static void *		(*real_calloc)(size_t, size_t) = NULL;
+static void *		(*real_realloc)(void *, size_t) = NULL;
+static void		(*real_free)(void *) = NULL;
+static const char	 *unknown_file = "<unknown>";
+
+#ifdef XALLOC_DEBUG
+RB_GENERATE(memory_tree, memory, entry, _memory_cmp)
+
+int
+_memory_cmp(void *arg_a, void *arg_b)
+{
+	struct memory	*a = (struct memory *)arg_a;
+	struct memory	*b = (struct memory *)arg_b;
+
+	if (a->ptr < b->ptr)
+		return (-1);
+	else if (a->ptr > b->ptr)
+		return (1);
+	return (0);
+}
+
+void
+_memory_free(struct memory **mem_p)
+{
+	struct memory	*mem = *mem_p;
+
+	if (mem->allocated_by != NULL)
+		mem->allocated_by = NULL;
+	if (mem->reallocated_by != NULL)
+		mem->reallocated_by = NULL;
+	if (mem->freed_by != NULL)
+		mem->freed_by = NULL;
+	real_free(mem);
+	*mem_p = NULL;
+}
+#endif /* XALLOC_DEBUG */
+
+void
+_xalloc_warn(const char *fmt, ...)
+{
+	va_list ap;
+
+	if (debug_output == NULL)
+		debug_output = XALLOC_DEFAULT_OUTPUT;
+
+	va_start(ap, fmt);
+#ifndef XALLOC_SILENT
+	vfprintf(debug_output, fmt, ap);
+	fflush(debug_output);
+#endif /* !XALLOC_SILENT */
+	va_end(ap);
+}
+
+void
+_xalloc_error(int errnum, const char *fmt, ...)
+{
+	va_list ap;
+
+	if (debug_output == NULL)
+		debug_output = XALLOC_DEFAULT_OUTPUT;
+
+	va_start(ap, fmt);
+#ifndef XALLOC_SILENT
+	vfprintf(debug_output, fmt, ap);
+	if (errnum > 0) {
+		if (xalloc_initialized)
+			XALLOC_LOCK(strerror_mutex);
+		vfprintf(debug_output, ": %s\n", strerror(errnum));
+		if (xalloc_initialized)
+			XALLOC_UNLOCK(strerror_mutex);
+	}
+	fflush(debug_output);
+#endif /* !XALLOC_SILENT */
+	va_end(ap);
+
+	exit(1);
+}
+
+void
+_xalloc_fatal(const char *fmt, ...)
+{
+	va_list ap;
+
+	if (debug_output == NULL)
+		debug_output = XALLOC_DEFAULT_OUTPUT;
+
+	va_start(ap, fmt);
+#ifndef XALLOC_SILENT
+	vfprintf(debug_output, fmt, ap);
+	fflush(debug_output);
+#endif /* !XALLOC_SILENT */
+	va_end(ap);
+
+	abort();
+}
+
+void
+_xalloc_debug_printf(unsigned int level, const char *fmt, ...)
+{
+	va_list ap;
+
+	if (level > debug_level)
+		return;
+
+	va_start(ap, fmt);
+#ifdef XALLOC_DEBUG
+	vfprintf(debug_output, fmt, ap);
+	fflush(debug_output);
+#endif /* XALLOC_DEBUG */
+	va_end(ap);
+}
+
+#ifdef XALLOC_WITH_XASPRINTF
+int
+_xalloc_vasprintf(char **str_p, const char *fmt, va_list ap, size_t *strsiz)
+{
+	int	ret = -1;
+	va_list ap_local;
+
+	*str_p = NULL;
+#ifndef WIN32
+# ifndef HAVE_BROKEN_VSNPRINTF
+
+	/* MODERN UNIX */
+
+	va_copy(ap_local, ap);
+	*strsiz = vsnprintf(NULL, (size_t)0, fmt, ap_local) + 1;
+	va_end(ap_local);
+#  ifdef HAVE_ASPRINTF
+	if ((ret = vasprintf(str_p, fmt, ap)) == -1)
+		*str_p = NULL;
+#  else
+	if ((*str_p = real_calloc(*strsiz, sizeof(char))) == NULL)
+		return (-1);
+	ret = vsnprintf(*str_p, *strsiz, fmt, ap);
+#  endif /* HAVE_ASPRINTF */
+# else
+
+	/* ANCIENT UNIX */
+
+	{
+		char	*buf = NULL;
+
+		*strsiz = 4;
+		for (;;) {
+			char	*tbuf;
+			int	 pret;
+
+			if ((tbuf = real_realloc(buf, *strsiz)) == NULL) {
+				real_free(buf);
+				return (-1);
+			}
+			buf = tbuf;
+			va_copy(ap_local, ap);
+			pret = vsnprintf(buf, *strsiz, fmt, ap_local);
+			va_end(ap_local);
+			if (pret > 0 && pret < (int)*strsiz)
+				break;
+			if ((int)(*strsiz *= 2) < 0) {
+				real_free(buf);
+				return (-1);
+			}
+		}
+		ret = vsnprintf(buf, *strsiz, fmt, ap);
+		*str_p = buf;
+	}
+# endif /* !HAVE_BROKEN_VSNPRINTF */
+#else
+
+	/* WINDOWS */
+
+	va_copy(ap_local, ap);
+	*strsiz = _vscprintf(fmt, ap_local) + 1;
+	va_end(ap_local);
+	if ((*str_p = real_calloc(*strsiz, sizeof(char))) == NULL)
+		return (-1);
+	ret = _vsnprintf(*str_p, *strsiz, fmt, ap);
+#endif /* !WIN32 */
+
+	return (ret);
+}
+#endif /* XALLOC_WITH_XASPRINTF */
+
+void
+xalloc_initialize_debug(unsigned int level, FILE *output)
+{
+#ifdef THREAD_SAFE
+	int	err;
+#endif /* THREAD_SAFE */
+
+	if (xalloc_initialized)
+		_xalloc_fatal("XALLOC: xalloc_initialize(): Xalloc library already initialized\n");
+
+	if ((debug_level = level) > XALLOC_DBGLVL_MAX)
+		debug_level = XALLOC_DBGLVL_MAX;
+	if (output == NULL)
+		debug_output = XALLOC_DEFAULT_OUTPUT;
+	else
+		debug_output = output;
+
+	real_malloc = malloc;
+	real_calloc = calloc;
+	real_realloc = realloc;
+	real_free = free;
+	xalloc_next_id = 0;
+	xalloc_allocated = 0;
+	xalloc_total = 0;
+	xalloc_peak = 0;
+	xalloc_freed = 0;
+
+#ifdef THREAD_SAFE
+	if ((err = pthread_mutex_init(&strerror_mutex, NULL)) != 0)
+		_xalloc_error(err, "XALLOC: xalloc_initialize(): Initializing xalloc_mutex");
+	if ((err = pthread_mutex_init(&xalloc_mutex, NULL)) != 0)
+		_xalloc_error(err, "XALLOC: xalloc_initialize(): Initializing strerror_mutex");
+#endif /* THREAD_SAFE */
+
+	xalloc_initialized = 1;
+}
+
+void
+xalloc_set_functions(void *(*malloc_func)(size_t),
+		     void *(*calloc_func)(size_t, size_t),
+		     void *(*realloc_func)(void *, size_t),
+		     void (*free_func)(void *))
+{
+	if (!xalloc_initialized)
+		_xalloc_fatal("XALLOC: xalloc_set_functions(): Xalloc library not initialized\n");
+
+	if (malloc_func == NULL ||
+	    calloc_func == NULL ||
+	    realloc_func == NULL)
+		_xalloc_fatal("XALLOC: xalloc_set_functions(): Bad argument(s)\n");
+
+	XALLOC_LOCK(xalloc_mutex);
+	real_malloc = malloc_func;
+	real_calloc = calloc_func;
+	real_realloc = realloc_func;
+	real_free = free_func;
+	XALLOC_UNLOCK(xalloc_mutex);
+}
+
+void
+xalloc_shutdown(void)
+{
+	if (!xalloc_initialized)
+		_xalloc_fatal("XALLOC: xalloc_shutdown(): Xalloc library not initialized\n");
+
+#ifdef XALLOC_DEBUG
+	if (debug_level > 0) {
+		struct memory	*mem, *mem_next;
+		size_t		 leaked_bytes = 0;
+
+		XALLOC_LOCK(xalloc_mutex);
+
+		for (mem = RB_MIN(memory_tree, &memory_tree_head);
+		     mem != NULL;
+		     mem = mem_next) {
+			mem_next = RB_NEXT(memory_tree, &memory_tree_head, mem);
+			RB_REMOVE(memory_tree, &memory_tree_head, mem);
+
+			if (mem->freed_by == NULL) {
+				_xalloc_debug_printf(1, "XALLOC: MEMORY LEAK (%p:%u): allocated in %s:%u, ",
+						     mem->ptr,
+						     mem->id,
+						     mem->allocated_by,
+						     mem->allocated_in_line);
+				if (mem->reallocated_by != NULL)
+					_xalloc_debug_printf(1, "last reallocated in %s:%u, ",
+							     mem->reallocated_by,
+							     mem->reallocated_in_line);
+				_xalloc_debug_printf(1, "leaks %lu bytes\n",
+						     (unsigned long)mem->size);
+				leaked_bytes += mem->size;
+				real_free(mem->ptr);
+			}
+
+			_memory_free(&mem);
+		}
+
+		if (leaked_bytes != xalloc_allocated)
+			_xalloc_fatal("XALLOC: Internal error: xalloc_shutdown(): leaked_bytes(%lu) != xalloc_allocated(%lu)\n",
+				      (unsigned long)leaked_bytes,
+				      (unsigned long)xalloc_allocated);
+
+		_xalloc_debug_printf(1, "XALLOC: STATS: leaked: %lu bytes, peak allocation: %lu bytes (freed/total: %lu/%lu bytes)\n",
+				     (unsigned long)xalloc_allocated,
+				     (unsigned long)xalloc_peak,
+				     (unsigned long)xalloc_freed,
+				     (unsigned long)xalloc_total);
+
+		XALLOC_UNLOCK(xalloc_mutex);
+	}
+#endif /* XALLOC_DEBUG */
+
+#ifdef THREAD_SAFE
+	if (pthread_mutex_destroy(&xalloc_mutex) != 0)
+		_xalloc_fatal("XALLOC: Internal error: xalloc_shutdown(): xalloc_mutex %p cannot be destroyed\n",
+			      xalloc_mutex);
+	if (pthread_mutex_destroy(&strerror_mutex) != 0)
+		_xalloc_fatal("XALLOC: Internal error: xalloc_shutdown(): strerror_mutex %p cannot be destroyed\n",
+			      strerror_mutex);
+#endif /* THREAD_SAFE */
+
+	xalloc_initialized = 0;
+}
+
+void *
+xmalloc_c(size_t size, const char *file, unsigned int line)
+{
+	void		*ret;
+
+	if (!xalloc_initialized)
+		_xalloc_fatal("XALLOC: xmalloc(): Xalloc library not initialized\n");
+
+	if (size == 0)
+		_xalloc_fatal("XALLOC: xmalloc(): %s:%u: Zero size\n",
+			      file ? file : unknown_file, line);
+
+	if ((ret = real_malloc(size)) == NULL)
+		_xalloc_error(errno, "XALLOC: xmalloc(): %s:%u: Allocating %lu bytes",
+			      file ? file : unknown_file, line,
+			      (unsigned long)(size));
+
+#ifdef XALLOC_DEBUG
+	if (debug_level > 0) {
+		struct memory	*mem, *mem_exists;
+
+		if ((mem = real_calloc(1, sizeof(struct memory))) == NULL)
+			_xalloc_error(errno, "XALLOC: Internal error");
+		mem->ptr = ret;
+		mem->size = size;
+		if (file)
+			mem->allocated_by = file;
+		else
+			mem->allocated_by = unknown_file;
+		mem->allocated_in_line = line;
+		XALLOC_LOCK(xalloc_mutex);
+		mem->id = ++xalloc_next_id;
+		if ((mem_exists = RB_INSERT(memory_tree, &memory_tree_head, mem)) != NULL) {
+			/* Freed pointer is being reused: */
+			if (mem_exists->id != 0)
+				_xalloc_fatal("XALLOC: Internal error: Assertion (mem_exists->id == 0) in %s:%u failed!\n",
+					      __FILE__, __LINE__);
+			RB_REMOVE(memory_tree, &memory_tree_head, mem_exists);
+			_memory_free(&mem_exists);
+			RB_INSERT(memory_tree, &memory_tree_head, mem);
+		}
+		xalloc_allocated += size;
+		xalloc_total += size;
+		if (xalloc_allocated > xalloc_peak)
+			xalloc_peak = xalloc_allocated;
+		XALLOC_UNLOCK(xalloc_mutex);
+	}
+#endif /* XALLOC_DEBUG */
+
+	return (ret);
+}
+
+void *
+xcalloc_c(size_t nmemb, size_t size, int may_fail,
+	  const char *file, unsigned int line)
+{
+	void		*ret;
+
+	if (!xalloc_initialized)
+		_xalloc_fatal("XALLOC: xcalloc(): Xalloc library not initialized\n");
+
+	if (nmemb == 0 || size == 0)
+		_xalloc_fatal("XALLOC: xcalloc(): %s:%u: Zero size\n",
+			      file ? file : unknown_file, line);
+
+	if (SIZE_T_MAX / nmemb < size)
+		_xalloc_fatal("XALLOC: xcalloc(): %s:%u: Integer overflow (nmemb * size > SIZE_T_MAX)\n",
+			      file ? file : unknown_file, line);
+ 
+	if ((ret = real_calloc(nmemb, size)) == NULL && may_fail == 0)
+		_xalloc_error(errno, "XALLOC: xcalloc(): %s:%u: Allocating %lu bytes",
+			      file ? file : unknown_file, line,
+			      (unsigned long)(nmemb * size));
+
+#ifdef XALLOC_DEBUG
+	if (ret != NULL && debug_level > 0) {
+		struct memory	*mem, *mem_exists;
+
+		if ((mem = real_calloc(1, sizeof(struct memory))) == NULL)
+			_xalloc_error(errno, "XALLOC: Internal error");
+		mem->ptr = ret;
+		mem->size = nmemb * size;
+		if (file)
+			mem->allocated_by = file;
+		else
+			mem->allocated_by = unknown_file;
+		mem->allocated_in_line = line;
+		XALLOC_LOCK(xalloc_mutex);
+		mem->id = ++xalloc_next_id;
+		if ((mem_exists = RB_INSERT(memory_tree, &memory_tree_head, mem)) != NULL) {
+			/* Freed pointer is being reused: */
+			if (mem_exists->id != 0)
+				_xalloc_fatal("XALLOC: Internal error: Assertion (mem_exists->id == 0) in %s:%u failed!\n",
+					      __FILE__, __LINE__);
+			RB_REMOVE(memory_tree, &memory_tree_head, mem_exists);
+			_memory_free(&mem_exists);
+			RB_INSERT(memory_tree, &memory_tree_head, mem);
+		}
+		xalloc_allocated += nmemb * size;
+		xalloc_total += nmemb * size;
+		if (xalloc_allocated > xalloc_peak)
+			xalloc_peak = xalloc_allocated;
+		XALLOC_UNLOCK(xalloc_mutex);
+	}
+#endif /* XALLOC_DEBUG */
+
+	return (ret);
+}
+
+void *
+xrealloc_c(void *ptr, size_t nmemb, size_t size,
+	   const char *file, unsigned int line)
+{
+	void		*ret;
+	size_t		 nsiz = nmemb * size;
+#ifdef XALLOC_DEBUG
+	struct memory	*mem = NULL;
+#endif /* XALLOC_DEBUG */
+
+	if (!xalloc_initialized)
+		_xalloc_fatal("XALLOC: xrealloc(): Xalloc library not initialized\n");
+
+	if (nmemb == 0 || size == 0)
+		_xalloc_fatal("XALLOC: xrealloc(): %s:%u: Zero size\n",
+			      file ? file : unknown_file, line);
+
+	if (SIZE_T_MAX / nmemb < size)
+		_xalloc_fatal("XALLOC: xrealloc(): %s:%u: Integer overflow (nmemb * size > SIZE_T_MAX)\n",
+			      file ? file : unknown_file, line);
+
+	if (ptr == NULL) {
+		ret = real_malloc(nsiz);
+#ifdef XALLOC_DEBUG
+		if (debug_level > 0) {
+			if ((mem = real_calloc(1, sizeof(struct memory))) == NULL)
+				_xalloc_error(errno, "XALLOC: Internal error");
+			mem->ptr = ret;
+			XALLOC_LOCK(xalloc_mutex);
+			mem->id = ++xalloc_next_id;
+			XALLOC_UNLOCK(xalloc_mutex);
+			if (file)
+				mem->allocated_by = file;
+			else
+				mem->allocated_by = unknown_file;
+			mem->allocated_in_line = line;
+		}
+#endif /* XALLOC_DEBUG */
+	} else {
+#ifdef XALLOC_DEBUG
+		struct memory	find_mem;
+
+		XALLOC_LOCK(xalloc_mutex);
+		if (debug_level > 0) {
+			find_mem.ptr = ptr;
+			if ((mem = RB_FIND(memory_tree, &memory_tree_head, &find_mem)) == NULL)
+				_xalloc_fatal("XALLOC: xrealloc(): %s:%u: Junk pointer %p not accounted for\n",
+					      file ? file : unknown_file,
+					      line, ptr);
+			RB_REMOVE(memory_tree, &memory_tree_head, mem);
+		}
+		XALLOC_UNLOCK(xalloc_mutex);
+#endif /* XALLOC_DEBUG */
+		ret = real_realloc(ptr, nsiz);
+#ifdef XALLOC_DEBUG
+		if (debug_level > 0) {
+			mem->ptr = ret;
+			if (file)
+				mem->reallocated_by = file;
+			else
+				mem->reallocated_by = unknown_file;
+			mem->reallocated_in_line = line;
+		}
+#endif /* XALLOC_DEBUG */
+	}
+
+	if (ret == NULL)
+		_xalloc_error(errno, "XALLOC: xrealloc(): %s:%u: (Re)allocating %lu bytes",
+			      file ? file : unknown_file, line,
+			      (unsigned long)(nmemb * size));
+
+#ifdef XALLOC_DEBUG
+	if (debug_level > 0) {
+		struct memory	*mem_exists;
+		ssize_t 	 diff = (ssize_t)(nsiz - mem->size);
+
+		XALLOC_LOCK(xalloc_mutex);
+		xalloc_allocated += diff;
+		if (diff < 0)
+			xalloc_freed += -diff;
+		else
+			xalloc_total += diff;
+		if (xalloc_allocated > xalloc_peak)
+			xalloc_peak = xalloc_allocated;
+		mem->size = nsiz;
+		if ((mem_exists = RB_INSERT(memory_tree, &memory_tree_head, mem)) != NULL) {
+			/* Freed pointer is being reused: */
+			if (mem_exists->id != 0)
+				_xalloc_fatal("XALLOC: Internal error: Assertion (mem_exists->id == 0) in %s:%u failed!\n",
+					      __FILE__, __LINE__);
+			RB_REMOVE(memory_tree, &memory_tree_head, mem_exists);
+			_memory_free(&mem_exists);
+			RB_INSERT(memory_tree, &memory_tree_head, mem);
+		}
+		XALLOC_UNLOCK(xalloc_mutex);
+	}
+#endif /* XALLOC_DEBUG */
+
+	return (ret);
+}
+
+char *
+xstrdup_c(const char *str, const char *file, unsigned int line)
+{
+	size_t	 len;
+	char	*nstr;
+
+	if (!xalloc_initialized)
+		_xalloc_fatal("XALLOC: xstrdup(): Xalloc library not initialized\n");
+
+	len = strlen(str) + 1;
+	if ((nstr = xcalloc_c(len, sizeof(char), 0, file, line)) == NULL)
+		_xalloc_error(errno, "XALLOC: xstrdup(): %s:%u: Allocating %lu bytes: %s\n",
+			      file ? file : unknown_file, line,
+			      (unsigned long)(len));
+	memcpy(nstr, str, len);
+	return (nstr);
+}
+
+void
+xfree_c(void **ptr_p, const char *file, unsigned int line)
+{
+	if (!xalloc_initialized)
+		_xalloc_fatal("XALLOC: xfree(): Xalloc library not initialized\n");
+
+	if (ptr_p == NULL)
+		_xalloc_fatal("XALLOC: xfree(): Bad argument(s)\n");
+
+	if (*ptr_p == NULL) {
+		_xalloc_warn("XALLOC: xfree(): Warning: %s:%u: Freeing NULL pointer\n",
+			     file ? file : unknown_file, line);
+		return;
+	}
+
+#ifdef XALLOC_DEBUG
+	if (debug_level > 0) {
+		struct memory	*mem = NULL, find_mem;
+
+		XALLOC_LOCK(xalloc_mutex);
+		find_mem.ptr = *ptr_p;
+		if ((mem = RB_FIND(memory_tree, &memory_tree_head, &find_mem)) == NULL)
+			_xalloc_fatal("XALLOC: xfree(): %s:%u: Junk pointer %p not accounted for\n",
+				      file ? file : unknown_file, line,
+				      *ptr_p);
+
+		if (mem->freed_by != NULL && mem->id == 0) {
+			_xalloc_debug_printf(2, "XALLOC: DOUBLE FREE of pointer %p in %s:%u: allocated in %s:%u, ",
+					     mem->ptr,
+					     file ? file : unknown_file, line,
+					     mem->allocated_by,
+					     mem->allocated_in_line);
+			if (mem->reallocated_by != NULL)
+				_xalloc_debug_printf(2, "last reallocated in %s:%u, ",
+						     mem->reallocated_by,
+						     mem->reallocated_in_line);
+			_xalloc_debug_printf(2, "already freed in %s:%u\n",
+					     mem->freed_by,
+					     mem->freed_in_line);
+			abort();
+		}
+
+		xalloc_freed += mem->size;
+		xalloc_allocated -= mem->size;
+		mem->id = 0;
+		mem->size = 0;
+		if (debug_level > 1) {
+			if (file)
+				mem->freed_by = file;
+			else
+				mem->freed_by = unknown_file;
+			mem->freed_in_line = line;
+		} else {
+			RB_REMOVE(memory_tree, &memory_tree_head, mem);
+			_memory_free(&mem);
+		}
+		XALLOC_UNLOCK(xalloc_mutex);
+	}
+#endif /* XALLOC_DEBUG */
+
+	real_free(*ptr_p);
+#ifdef XALLOC_DEBUG
+	if (debug_level <= 1)
+#endif /* XALLOC_DEBUG */
+	{
+		*ptr_p = NULL;
+	}
+}
+
+#ifdef XALLOC_WITH_XASPRINTF
+int
+xasprintf_c(const char *file, unsigned int line,
+	    char **str_p, const char *fmt, ...)
+{
+	int	ret;
+	va_list ap;
+	size_t	strsiz = 0;
+
+	if (!xalloc_initialized)
+		_xalloc_fatal("XALLOC: xasprintf(): Xalloc library not initialized\n");
+
+	if (str_p == NULL || fmt == NULL)
+		_xalloc_fatal("XALLOC: xasprintf(): Bad argument(s)\n");
+
+	va_start(ap, fmt);
+	ret = _xalloc_vasprintf(str_p, fmt, ap, &strsiz);
+	va_end(ap);
+	if (ret == -1)
+		_xalloc_error(errno, "XALLOC: xasprintf(): %s:%u: Allocating %lu bytes",
+			      file ? file : unknown_file, line, strsiz);
+
+# ifdef XALLOC_DEBUG
+	if (debug_level > 0) {
+		struct memory	*mem, *mem_exists;
+
+		if ((mem = real_calloc(1, sizeof(struct memory))) == NULL)
+			_xalloc_error(errno, "XALLOC: Internal error");
+		mem->ptr = *str_p;
+		mem->size = strsiz;
+		if (file)
+			mem->allocated_by = file;
+		else
+			mem->allocated_by = unknown_file;
+		mem->allocated_in_line = line;
+		XALLOC_LOCK(xalloc_mutex);
+		mem->id = ++xalloc_next_id;
+		if ((mem_exists = RB_INSERT(memory_tree, &memory_tree_head, mem)) != NULL) {
+			/* Freed pointer is being reused: */
+			if (mem_exists->id != 0)
+				_xalloc_fatal("XALLOC: Internal error: Assertion (mem_exists->id == 0) in %s:%u failed!\n",
+					      __FILE__, __LINE__);
+			RB_REMOVE(memory_tree, &memory_tree_head, mem_exists);
+			_memory_free(&mem_exists);
+			RB_INSERT(memory_tree, &memory_tree_head, mem);
+		}
+		xalloc_allocated += strsiz;
+		xalloc_total += strsiz;
+		if (xalloc_allocated > xalloc_peak)
+			xalloc_peak = xalloc_allocated;
+		XALLOC_UNLOCK(xalloc_mutex);
+	}
+# endif /* XALLOC_DEBUG */
+
+	return (ret);
+}
+#endif /* XALLOC_WITH_XASPRINTF */
+
+#endif /* USE_XALLOC */

Added: experimental/moritz/iceadmin/lib/xalloc.h
===================================================================
--- experimental/moritz/iceadmin/lib/xalloc.h	                        (rev 0)
+++ experimental/moritz/iceadmin/lib/xalloc.h	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,133 @@
+/*
+ * libxalloc - Portable memory allocation wrapper library, with extensive
+ *             error checking, debugging facilities and hooks for 3rd party
+ *             memory allocation functions.
+ *             This library also detects and prevents double-free errors,
+ *             and ensures that out-of-memory issues always cause the
+ *             application to exit.
+ *
+ * Copyright (C) 2007  Moritz Grimm <mdgrimm at gmx.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __XALLOC_H__
+#define __XALLOC_H__
+
+/*
+ * Define XALLOC_DEBUG to compile the debugging features. Doing so will make
+ * this library more expensive in every case, but not change its (visible)
+ * behavior unless the debugging level is set > 0. The debugging levels are:
+ *   0: disable debugging
+ *   1: enable most debugging features
+ *   2: additionally enable double-free checking
+ *      (Warning: This requires libxalloc to keep track of all allocations
+ *                and frees, which means that memory usage may increase a lot
+ *                over time!)
+ *
+ * Define XALLOC_SILENT to suppress all messages, which makes libxalloc
+ * abort() and exit() silently. This has no effect when THREAD_DEBUG is
+ * defined.
+ *
+ * Define XALLOC_WITH_XASPRINTF to expose the xasprintf() interface. Doing
+ * so will require libxalloc to be compiled with a compiler that supports C99
+ * variadic macros, and work only on systems with vasprintf() or vsnprintf(),
+ * and MS Windows. Note that doing so constitutes an incompatible ABI change!
+ *
+ * Note that none of the x*_c() functions should be used directly, unless it
+ * is ensured that /file/ is a const char * to a real string constant.
+ */
+/* #define XALLOC_DEBUG 1 */
+/* #define XALLOC_SILENT 1 */
+/* #define XALLOC_WITH_XASPRINTF 1 */
+
+/* The default output stream for messages: */
+#define XALLOC_DEFAULT_OUTPUT	stderr
+
+#if (defined(_REENTRANT) || defined(_POSIX_THREADS)) && !defined(THREAD_SAFE)
+# define THREAD_SAFE		1
+#endif
+
+
+#ifdef USE_XALLOC
+
+/*
+ * Library initialization and shutdown.
+ */
+
+#define xalloc_initialize()						\
+	xalloc_initialize_debug(0, NULL)
+void	xalloc_initialize_debug(unsigned int /* level */,
+				FILE * /* output stream */);
+
+void	xalloc_set_functions(void *(*)(size_t) /* malloc function */,
+			     void *(*)(size_t, size_t) /* calloc function */,
+			     void *(*)(void *, size_t) /* realloc function */,
+			     void (*)(void *) /* free function */);
+
+/* Memory leak checks happen during shutdown! */
+void	xalloc_shutdown(void);
+
+
+/*
+ * Memory management functions.
+ * Note that xrealloc() has calloc() semantics, to detect and prevent integer
+ * overflows.
+ */
+
+#define xmalloc(s)							\
+	xmalloc_c(s, __FILE__, __LINE__)
+void *	xmalloc_c(size_t /* size */,
+		  const char * /* file */, unsigned int /* line */);
+
+#define xcalloc(n, s)							\
+	xcalloc_c(n, s, 0, __FILE__, __LINE__)
+void *	xcalloc_c(size_t /* nmemb */, size_t /* size */, int /* may fail */,
+		  const char * /* file */, unsigned int /* line */);
+
+#define xrealloc(p, n, s)						\
+	xrealloc_c(p, n, s, __FILE__, __LINE__)
+void *	xrealloc_c(void *, size_t /* nmemb */, size_t /* size */,
+		   const char * /* file */, unsigned int /* line */);
+
+#define xstrdup(s)							\
+	xstrdup_c(s, __FILE__, __LINE__)
+char *	xstrdup_c(const char *,
+		  const char * /* file */, unsigned int /* line */);
+
+#define xfree(p)							\
+	xfree_c((void *)&(p), __FILE__, __LINE__)
+void	xfree_c(void **,
+		const char * /* file */, unsigned int /* line */);
+
+#ifdef XALLOC_WITH_XASPRINTF
+# define xasprintf(s, f, ...)						\
+	xasprintf_c(__FILE__, __LINE__, s, f, __VA_ARGS__)
+int	xasprintf_c(const char * /* file */, unsigned int /* line */,
+		    char ** /* string pointer */, const char * /* format */,
+		    ...);
+#endif /* XALLOC_WITH_XASPRINTF */
+
+#else /* USE_XALLOC */
+# define xalloc_initialize()			((void)0)
+# define xalloc_initialize_debug(x, y)		((void)0)
+# define xalloc_set_functions(a, b, c, d)	((void)0)
+# define xalloc_shutdown()			((void)0)
+# define xmalloc(s)				malloc(s)
+# define xcalloc(n, s)				calloc(n, s)
+# define xrealloc(p, n, s)			realloc(p, n * s)
+# define xstrdup(s)				strdup(s)
+# define xfree(p)				free(p)
+#endif /* USE_XALLOC */
+
+#endif /* __XALLOC_H__ */

Added: experimental/moritz/iceadmin/src/Makefile.am
===================================================================
--- experimental/moritz/iceadmin/src/Makefile.am	                        (rev 0)
+++ experimental/moritz/iceadmin/src/Makefile.am	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,13 @@
+AUTOMAKE_OPTIONS = 1.9 foreign
+
+bin_PROGRAMS =	iceadmin
+
+iceadmin_SOURCES = iceadmin.c $(top_srcdir)/lib/xalloc.c
+iceadmin_LDADD = $(top_builddir)/lib/libiceadmin.la @XML2_LIBS@
+
+INCLUDES =	@COMPAT_INCLUDES@ -I$(top_srcdir)/include -I$(top_srcdir)/lib
+AM_CFLAGS =	@XML2_CFLAGS@
+# AM_CPPFLAGS =	@XIPH_CPPFLAGS@ -DUSE_XALLOC=1
+AM_CPPFLAGS =	@XIPH_CPPFLAGS@
+
+CLEANFILES =	core *.core *~ .*~

Added: experimental/moritz/iceadmin/src/iceadmin.c
===================================================================
--- experimental/moritz/iceadmin/src/iceadmin.c	                        (rev 0)
+++ experimental/moritz/iceadmin/src/iceadmin.c	2007-08-18 12:05:20 UTC (rev 13566)
@@ -0,0 +1,115 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+
+#include <iceadmin.h>
+#include <xalloc.h>
+
+void	init_self(void);
+int	shutdown_self(int);
+
+void
+init_self(void)
+{
+	xalloc_initialize_debug(2, stderr);
+	iceadmin_initialize();
+}
+
+int
+shutdown_self(int ret)
+{
+	iceadmin_shutdown();
+	xalloc_shutdown();
+
+	return (ret);
+}
+
+int
+main(void)
+{
+	iceadmin_t	 iceadmin;
+	char		 errbuf[256];
+	char		*buf;
+	size_t		 bufsize;
+	int		 ret;
+
+	init_self();
+
+	if (iceadmin_new(&iceadmin, "/stream.ogg", "example.domain", "8000", "admin", "hackme") != ICEADMIN_SUCCESS) {
+		iceadmin_get_error(iceadmin, errbuf, sizeof(errbuf));
+		fprintf(stderr, "%s", errbuf);
+		iceadmin_delete(iceadmin);
+		return (shutdown_self(1));
+	}
+
+	if ((ret = iceadmin_cmd_get_stats(iceadmin, &buf, &bufsize)) != ICEADMIN_SUCCESS) {
+		iceadmin_get_error(iceadmin, errbuf, sizeof(errbuf));
+		fprintf(stderr, "Get status: %s", errbuf);
+		if (ret != ICEADMIN_FAILURE) {
+			iceadmin_delete(iceadmin);
+			return (shutdown_self(1));
+		}
+	}
+
+	printf("%s\n", buf);
+	iceadmin_free(buf);
+
+	if ((ret = iceadmin_cmd_list_mounts(iceadmin, &buf, &bufsize)) != ICEADMIN_SUCCESS) {
+		iceadmin_get_error(iceadmin, errbuf, sizeof(errbuf));
+		fprintf(stderr, "List mounts: %s", errbuf);
+		if (ret != ICEADMIN_FAILURE) {
+			iceadmin_delete(iceadmin);
+			return (shutdown_self(1));
+		}
+	}
+
+	printf("%s\n", buf);
+	iceadmin_free(buf);
+
+	if ((ret = iceadmin_cmd_list_clients(iceadmin, &buf, &bufsize)) != ICEADMIN_SUCCESS) {
+		iceadmin_get_error(iceadmin, errbuf, sizeof(errbuf));
+		fprintf(stderr, "List clients: %s", errbuf);
+		if (ret != ICEADMIN_FAILURE) {
+			iceadmin_delete(iceadmin);
+			return (shutdown_self(1));
+		}
+	}
+
+	printf("%s\n", buf);
+	iceadmin_free(buf);
+
+	/*
+	if ((ret = iceadmin_cmd_kill_source(iceadmin)) != ICEADMIN_SUCCESS) {
+		iceadmin_get_error(iceadmin, errbuf, sizeof(errbuf));
+		fprintf(stderr, "Kill source: %s", errbuf);
+		if (ret != ICEADMIN_FAILURE) {
+			iceadmin_delete(iceadmin);
+			return (shutdown_self(1));
+		}
+	}
+	*/
+
+	if ((ret = iceadmin_cmd_kill_client(iceadmin, 354)) != ICEADMIN_SUCCESS) {
+		iceadmin_get_error(iceadmin, errbuf, sizeof(errbuf));
+		fprintf(stderr, "Kill client: %s", errbuf);
+		if (ret != ICEADMIN_FAILURE) {
+			iceadmin_delete(iceadmin);
+			return (shutdown_self(1));
+		}
+	}
+
+	if ((ret = iceadmin_cmd_update_metadata(iceadmin, "Yay! Test!?!?!")) != ICEADMIN_SUCCESS) {
+		iceadmin_get_error(iceadmin, errbuf, sizeof(errbuf));
+		fprintf(stderr, "Update metadata: %s", errbuf);
+		if (ret != ICEADMIN_FAILURE) {
+			iceadmin_delete(iceadmin);
+			return (shutdown_self(1));
+		}
+	}
+
+	iceadmin_delete(iceadmin);
+
+	return (shutdown_self(0));
+}



More information about the commits mailing list