[SCM] kodi/master: Imported Upstream version 14.2+dfsg1

rbalint at users.alioth.debian.org rbalint at users.alioth.debian.org
Sat Mar 28 11:24:48 UTC 2015


The following commit has been merged in the master branch:
commit db474e3b921945220a9bd35f0dd154a7e58fb57c
Author: Balint Reczey <balint at balintreczey.hu>
Date:   Sat Mar 28 11:50:04 2015 +0100

    Imported Upstream version 14.2+dfsg1

diff --git a/lib/cpluff/kazlib/hash.c b/lib/cpluff/kazlib/hash.c
new file mode 100644
index 0000000..4cc281f
--- /dev/null
+++ b/lib/cpluff/kazlib/hash.c
@@ -0,0 +1,1041 @@
+/*
+ * Hash Table Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz at ashi.footprints.net>
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * $Id: hash.c,v 1.36.2.11 2000/11/13 01:36:45 kaz Exp $
+ * $Name: kazlib_1_20 $
+ */
+
+/*
+ * Modified by Johannes Lehtinen in 2006-2007.
+ * Included the definition of CP_HIDDEN macro and used it in declarations and
+ * definitions to hide Kazlib symbols when building a shared C-Pluff library.
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <assert.h>
+#include <string.h>
+#define HASH_IMPLEMENTATION
+#include "hash.h"
+
+#ifdef KAZLIB_RCSID
+static const char rcsid[] = "$Id: hash.c,v 1.36.2.11 2000/11/13 01:36:45 kaz Exp $";
+#endif
+
+#define INIT_BITS	6
+#define INIT_SIZE	(1UL << (INIT_BITS))	/* must be power of two		*/
+#define INIT_MASK	((INIT_SIZE) - 1)
+
+#define next hash_next
+#define key hash_key
+#define data hash_data
+#define hkey hash_hkey
+
+#define table hash_table
+#define nchains hash_nchains
+#define nodecount hash_nodecount
+#define maxcount hash_maxcount
+#define highmark hash_highmark
+#define lowmark hash_lowmark
+#define compare hash_compare
+#define function hash_function
+#define allocnode hash_allocnode
+#define freenode hash_freenode
+#define context hash_context
+#define mask hash_mask
+#define dynamic hash_dynamic
+
+#define table hash_table
+#define chain hash_chain
+
+static hnode_t *hnode_alloc(void *context);
+static void hnode_free(hnode_t *node, void *context);
+static hash_val_t hash_fun_default(const void *key);
+static int hash_comp_default(const void *key1, const void *key2);
+
+CP_HIDDEN int hash_val_t_bit;
+
+/*
+ * Compute the number of bits in the hash_val_t type.  We know that hash_val_t
+ * is an unsigned integral type. Thus the highest value it can hold is a
+ * Mersenne number (power of two, less one). We initialize a hash_val_t
+ * object with this value and then shift bits out one by one while counting.
+ * Notes:
+ * 1. HASH_VAL_T_MAX is a Mersenne number---one that is one less than a power
+ *    of two. This means that its binary representation consists of all one
+ *    bits, and hence ``val'' is initialized to all one bits.
+ * 2. While bits remain in val, we increment the bit count and shift it to the
+ *    right, replacing the topmost bit by zero.
+ */
+
+static void compute_bits(void)
+{
+    hash_val_t val = HASH_VAL_T_MAX;	/* 1 */
+    int bits = 0;
+
+    while (val) {	/* 2 */
+	bits++;
+	val >>= 1;
+    }
+
+    hash_val_t_bit = bits;
+}
+
+/*
+ * Verify whether the given argument is a power of two.
+ */
+
+static int is_power_of_two(hash_val_t arg)
+{
+    if (arg == 0)
+	return 0;
+    while ((arg & 1) == 0)
+	arg >>= 1;
+    return (arg == 1);
+}
+
+/*
+ * Compute a shift amount from a given table size 
+ */
+
+static hash_val_t compute_mask(hashcount_t size)
+{
+    assert (is_power_of_two(size));
+    assert (size >= 2);
+
+    return size - 1;
+}
+
+/*
+ * Initialize the table of pointers to null.
+ */
+
+static void clear_table(hash_t *hash)
+{
+    hash_val_t i;
+
+    for (i = 0; i < hash->nchains; i++)
+	hash->table[i] = NULL;
+}
+
+/*
+ * Double the size of a dynamic table. This works as follows. Each chain splits
+ * into two adjacent chains.  The shift amount increases by one, exposing an
+ * additional bit of each hashed key. For each node in the original chain, the
+ * value of this newly exposed bit will decide which of the two new chains will
+ * receive the node: if the bit is 1, the chain with the higher index will have
+ * the node, otherwise the lower chain will receive the node. In this manner,
+ * the hash table will continue to function exactly as before without having to
+ * rehash any of the keys.
+ * Notes:
+ * 1.  Overflow check.
+ * 2.  The new number of chains is twice the old number of chains.
+ * 3.  The new mask is one bit wider than the previous, revealing a
+ *     new bit in all hashed keys.
+ * 4.  Allocate a new table of chain pointers that is twice as large as the
+ *     previous one.
+ * 5.  If the reallocation was successful, we perform the rest of the growth
+ *     algorithm, otherwise we do nothing.
+ * 6.  The exposed_bit variable holds a mask with which each hashed key can be
+ *     AND-ed to test the value of its newly exposed bit.
+ * 7.  Now loop over each chain in the table and sort its nodes into two
+ *     chains based on the value of each node's newly exposed hash bit.
+ * 8.  The low chain replaces the current chain.  The high chain goes
+ *     into the corresponding sister chain in the upper half of the table.
+ * 9.  We have finished dealing with the chains and nodes. We now update
+ *     the various bookeeping fields of the hash structure.
+ */
+
+static void grow_table(hash_t *hash)
+{
+    hnode_t **newtable;
+
+    assert (2 * hash->nchains > hash->nchains);	/* 1 */
+
+    newtable = realloc(hash->table,
+	    sizeof *newtable * hash->nchains * 2);	/* 4 */
+
+    if (newtable) {	/* 5 */
+	hash_val_t mask = (hash->mask << 1) | 1;	/* 3 */
+	hash_val_t exposed_bit = mask ^ hash->mask;	/* 6 */
+	hash_val_t chain;
+
+	assert (mask != hash->mask);
+
+	for (chain = 0; chain < hash->nchains; chain++) { /* 7 */
+	    hnode_t *low_chain = 0, *high_chain = 0, *hptr, *next;
+
+	    for (hptr = newtable[chain]; hptr != 0; hptr = next) {
+		next = hptr->next;
+
+		if (hptr->hkey & exposed_bit) {
+		    hptr->next = high_chain;
+		    high_chain = hptr;
+		} else {
+		    hptr->next = low_chain;
+		    low_chain = hptr;
+		}
+	    }
+
+	    newtable[chain] = low_chain; 	/* 8 */
+	    newtable[chain + hash->nchains] = high_chain;
+	}
+
+	hash->table = newtable;			/* 9 */
+	hash->mask = mask;
+	hash->nchains *= 2;
+	hash->lowmark *= 2;
+	hash->highmark *= 2;
+    }
+    assert (hash_verify(hash));
+}
+
+/*
+ * Cut a table size in half. This is done by folding together adjacent chains
+ * and populating the lower half of the table with these chains. The chains are
+ * simply spliced together. Once this is done, the whole table is reallocated
+ * to a smaller object.
+ * Notes:
+ * 1.  It is illegal to have a hash table with one slot. This would mean that
+ *     hash->shift is equal to hash_val_t_bit, an illegal shift value.
+ *     Also, other things could go wrong, such as hash->lowmark becoming zero.
+ * 2.  Looping over each pair of sister chains, the low_chain is set to
+ *     point to the head node of the chain in the lower half of the table, 
+ *     and high_chain points to the head node of the sister in the upper half.
+ * 3.  The intent here is to compute a pointer to the last node of the
+ *     lower chain into the low_tail variable. If this chain is empty,
+ *     low_tail ends up with a null value.
+ * 4.  If the lower chain is not empty, we simply tack the upper chain onto it.
+ *     If the upper chain is a null pointer, nothing happens.
+ * 5.  Otherwise if the lower chain is empty but the upper one is not,
+ *     If the low chain is empty, but the high chain is not, then the
+ *     high chain is simply transferred to the lower half of the table.
+ * 6.  Otherwise if both chains are empty, there is nothing to do.
+ * 7.  All the chain pointers are in the lower half of the table now, so
+ *     we reallocate it to a smaller object. This, of course, invalidates
+ *     all pointer-to-pointers which reference into the table from the
+ *     first node of each chain.
+ * 8.  Though it's unlikely, the reallocation may fail. In this case we
+ *     pretend that the table _was_ reallocated to a smaller object.
+ * 9.  Finally, update the various table parameters to reflect the new size.
+ */
+
+static void shrink_table(hash_t *hash)
+{
+    hash_val_t chain, nchains;
+    hnode_t **newtable, *low_tail, *low_chain, *high_chain;
+
+    assert (hash->nchains >= 2);			/* 1 */
+    nchains = hash->nchains / 2;
+
+    for (chain = 0; chain < nchains; chain++) {
+	low_chain = hash->table[chain];		/* 2 */
+	high_chain = hash->table[chain + nchains];
+	for (low_tail = low_chain; low_tail && low_tail->next; low_tail = low_tail->next)
+	    ;	/* 3 */
+	if (low_chain != 0)				/* 4 */
+	    low_tail->next = high_chain;
+	else if (high_chain != 0)			/* 5 */
+	    hash->table[chain] = high_chain;
+	else
+	    assert (hash->table[chain] == NULL);	/* 6 */
+    }
+    newtable = realloc(hash->table,
+	    sizeof *newtable * nchains);		/* 7 */
+    if (newtable)					/* 8 */
+	hash->table = newtable;
+    hash->mask >>= 1;			/* 9 */
+    hash->nchains = nchains;
+    hash->lowmark /= 2;
+    hash->highmark /= 2;
+    assert (hash_verify(hash));
+}
+
+
+/*
+ * Create a dynamic hash table. Both the hash table structure and the table
+ * itself are dynamically allocated. Furthermore, the table is extendible in
+ * that it will automatically grow as its load factor increases beyond a
+ * certain threshold.
+ * Notes:
+ * 1. If the number of bits in the hash_val_t type has not been computed yet,
+ *    we do so here, because this is likely to be the first function that the
+ *    user calls.
+ * 2. Allocate a hash table control structure.
+ * 3. If a hash table control structure is successfully allocated, we
+ *    proceed to initialize it. Otherwise we return a null pointer.
+ * 4. We try to allocate the table of hash chains.
+ * 5. If we were able to allocate the hash chain table, we can finish
+ *    initializing the hash structure and the table. Otherwise, we must
+ *    backtrack by freeing the hash structure.
+ * 6. INIT_SIZE should be a power of two. The high and low marks are always set
+ *    to be twice the table size and half the table size respectively. When the
+ *    number of nodes in the table grows beyond the high size (beyond load
+ *    factor 2), it will double in size to cut the load factor down to about
+ *    about 1. If the table shrinks down to or beneath load factor 0.5,
+ *    it will shrink, bringing the load up to about 1. However, the table
+ *    will never shrink beneath INIT_SIZE even if it's emptied.
+ * 7. This indicates that the table is dynamically allocated and dynamically
+ *    resized on the fly. A table that has this value set to zero is
+ *    assumed to be statically allocated and will not be resized.
+ * 8. The table of chains must be properly reset to all null pointers.
+ */
+
+CP_HIDDEN hash_t *hash_create(hashcount_t maxcount, hash_comp_t compfun,
+	hash_fun_t hashfun)
+{
+    hash_t *hash;
+
+    if (hash_val_t_bit == 0)	/* 1 */
+	compute_bits();
+
+    hash = malloc(sizeof *hash);	/* 2 */
+
+    if (hash) {		/* 3 */
+	hash->table = malloc(sizeof *hash->table * INIT_SIZE);	/* 4 */
+	if (hash->table) {	/* 5 */
+	    hash->nchains = INIT_SIZE;		/* 6 */
+	    hash->highmark = INIT_SIZE * 2;
+	    hash->lowmark = INIT_SIZE / 2;
+	    hash->nodecount = 0;
+	    hash->maxcount = maxcount;
+	    hash->compare = compfun ? compfun : hash_comp_default;
+	    hash->function = hashfun ? hashfun : hash_fun_default;
+	    hash->allocnode = hnode_alloc;
+	    hash->freenode = hnode_free;
+	    hash->context = NULL;
+	    hash->mask = INIT_MASK;
+	    hash->dynamic = 1;			/* 7 */
+	    clear_table(hash);			/* 8 */
+	    assert (hash_verify(hash));
+	    return hash;
+	} 
+	free(hash);
+    }
+
+    return NULL;
+}
+
+/*
+ * Select a different set of node allocator routines.
+ */
+
+CP_HIDDEN void hash_set_allocator(hash_t *hash, hnode_alloc_t al,
+	hnode_free_t fr, void *context)
+{
+    assert (hash_count(hash) == 0);
+    assert ((al == 0 && fr == 0) || (al != 0 && fr != 0));
+
+    hash->allocnode = al ? al : hnode_alloc;
+    hash->freenode = fr ? fr : hnode_free;
+    hash->context = context;
+}
+
+/*
+ * Free every node in the hash using the hash->freenode() function pointer, and
+ * cause the hash to become empty.
+ */
+
+CP_HIDDEN void hash_free_nodes(hash_t *hash)
+{
+    hscan_t hs;
+    hnode_t *node;
+    hash_scan_begin(&hs, hash);
+    while ((node = hash_scan_next(&hs))) {
+	hash_scan_delete(hash, node);
+	hash->freenode(node, hash->context);
+    }
+    hash->nodecount = 0;
+    clear_table(hash);
+}
+
+/*
+ * Obsolescent function for removing all nodes from a table,
+ * freeing them and then freeing the table all in one step.
+ */
+
+CP_HIDDEN void hash_free(hash_t *hash)
+{
+#ifdef KAZLIB_OBSOLESCENT_DEBUG
+    assert ("call to obsolescent function hash_free()" && 0);
+#endif
+    hash_free_nodes(hash);
+    hash_destroy(hash);
+}
+
+/*
+ * Free a dynamic hash table structure.
+ */
+
+CP_HIDDEN void hash_destroy(hash_t *hash)
+{
+    assert (hash_val_t_bit != 0);
+    assert (hash_isempty(hash));
+    free(hash->table);
+    free(hash);
+}
+
+/*
+ * Initialize a user supplied hash structure. The user also supplies a table of
+ * chains which is assigned to the hash structure. The table is static---it
+ * will not grow or shrink.
+ * 1. See note 1. in hash_create().
+ * 2. The user supplied array of pointers hopefully contains nchains nodes.
+ * 3. See note 7. in hash_create().
+ * 4. We must dynamically compute the mask from the given power of two table
+ *    size. 
+ * 5. The user supplied table can't be assumed to contain null pointers,
+ *    so we reset it here.
+ */
+
+CP_HIDDEN hash_t *hash_init(hash_t *hash, hashcount_t maxcount,
+	hash_comp_t compfun, hash_fun_t hashfun, hnode_t **table,
+	hashcount_t nchains)
+{
+    if (hash_val_t_bit == 0)	/* 1 */
+	compute_bits();
+
+    assert (is_power_of_two(nchains));
+
+    hash->table = table;	/* 2 */
+    hash->nchains = nchains;
+    hash->nodecount = 0;
+    hash->maxcount = maxcount;
+    hash->compare = compfun ? compfun : hash_comp_default;
+    hash->function = hashfun ? hashfun : hash_fun_default;
+    hash->dynamic = 0;		/* 3 */
+    hash->mask = compute_mask(nchains);	/* 4 */
+    clear_table(hash);		/* 5 */
+
+    assert (hash_verify(hash));
+
+    return hash;
+}
+
+/*
+ * Reset the hash scanner so that the next element retrieved by
+ * hash_scan_next() shall be the first element on the first non-empty chain. 
+ * Notes:
+ * 1. Locate the first non empty chain.
+ * 2. If an empty chain is found, remember which one it is and set the next
+ *    pointer to refer to its first element.
+ * 3. Otherwise if a chain is not found, set the next pointer to NULL
+ *    so that hash_scan_next() shall indicate failure.
+ */
+
+CP_HIDDEN void hash_scan_begin(hscan_t *scan, hash_t *hash)
+{
+    hash_val_t nchains = hash->nchains;
+    hash_val_t chain;
+
+    scan->table = hash;
+
+    /* 1 */
+
+    for (chain = 0; chain < nchains && hash->table[chain] == 0; chain++)
+	;
+
+    if (chain < nchains) {	/* 2 */
+	scan->chain = chain;
+	scan->next = hash->table[chain];
+    } else {			/* 3 */
+	scan->next = NULL;
+    }
+}
+
+/*
+ * Retrieve the next node from the hash table, and update the pointer
+ * for the next invocation of hash_scan_next(). 
+ * Notes:
+ * 1. Remember the next pointer in a temporary value so that it can be
+ *    returned.
+ * 2. This assertion essentially checks whether the module has been properly
+ *    initialized. The first point of interaction with the module should be
+ *    either hash_create() or hash_init(), both of which set hash_val_t_bit to
+ *    a non zero value.
+ * 3. If the next pointer we are returning is not NULL, then the user is
+ *    allowed to call hash_scan_next() again. We prepare the new next pointer
+ *    for that call right now. That way the user is allowed to delete the node
+ *    we are about to return, since we will no longer be needing it to locate
+ *    the next node.
+ * 4. If there is a next node in the chain (next->next), then that becomes the
+ *    new next node, otherwise ...
+ * 5. We have exhausted the current chain, and must locate the next subsequent
+ *    non-empty chain in the table.
+ * 6. If a non-empty chain is found, the first element of that chain becomes
+ *    the new next node. Otherwise there is no new next node and we set the
+ *    pointer to NULL so that the next time hash_scan_next() is called, a null
+ *    pointer shall be immediately returned.
+ */
+
+
+CP_HIDDEN hnode_t *hash_scan_next(hscan_t *scan)
+{
+    hnode_t *next = scan->next;		/* 1 */
+    hash_t *hash = scan->table;
+    hash_val_t chain = scan->chain + 1;
+    hash_val_t nchains = hash->nchains;
+
+    assert (hash_val_t_bit != 0);	/* 2 */
+
+    if (next) {			/* 3 */
+	if (next->next) {	/* 4 */
+	    scan->next = next->next;
+	} else {
+	    while (chain < nchains && hash->table[chain] == 0)	/* 5 */
+	    	chain++;
+	    if (chain < nchains) {	/* 6 */
+		scan->chain = chain;
+		scan->next = hash->table[chain];
+	    } else {
+		scan->next = NULL;
+	    }
+	}
+    }
+    return next;
+}
+
+/*
+ * Insert a node into the hash table.
+ * Notes:
+ * 1. It's illegal to insert more than the maximum number of nodes. The client
+ *    should verify that the hash table is not full before attempting an
+ *    insertion.
+ * 2. The same key may not be inserted into a table twice.
+ * 3. If the table is dynamic and the load factor is already at >= 2,
+ *    grow the table.
+ * 4. We take the bottom N bits of the hash value to derive the chain index,
+ *    where N is the base 2 logarithm of the size of the hash table. 
+ */
+
+CP_HIDDEN void hash_insert(hash_t *hash, hnode_t *node, const void *key)
+{
+    hash_val_t hkey, chain;
+
+    assert (hash_val_t_bit != 0);
+    assert (node->next == NULL);
+    assert (hash->nodecount < hash->maxcount);	/* 1 */
+    assert (hash_lookup(hash, key) == NULL);	/* 2 */
+
+    if (hash->dynamic && hash->nodecount >= hash->highmark)	/* 3 */
+	grow_table(hash);
+
+    hkey = hash->function(key);
+    chain = hkey & hash->mask;	/* 4 */
+
+    node->key = key;
+    node->hkey = hkey;
+    node->next = hash->table[chain];
+    hash->table[chain] = node;
+    hash->nodecount++;
+
+    assert (hash_verify(hash));
+}
+
+/*
+ * Find a node in the hash table and return a pointer to it.
+ * Notes:
+ * 1. We hash the key and keep the entire hash value. As an optimization, when
+ *    we descend down the chain, we can compare hash values first and only if
+ *    hash values match do we perform a full key comparison. 
+ * 2. To locate the chain from among 2^N chains, we look at the lower N bits of
+ *    the hash value by anding them with the current mask.
+ * 3. Looping through the chain, we compare the stored hash value inside each
+ *    node against our computed hash. If they match, then we do a full
+ *    comparison between the unhashed keys. If these match, we have located the
+ *    entry.
+ */
+
+CP_HIDDEN hnode_t *hash_lookup(hash_t *hash, const void *key)
+{
+    hash_val_t hkey, chain;
+    hnode_t *nptr;
+
+    hkey = hash->function(key);		/* 1 */
+    chain = hkey & hash->mask;		/* 2 */
+
+    for (nptr = hash->table[chain]; nptr; nptr = nptr->next) {	/* 3 */
+	if (nptr->hkey == hkey && hash->compare(nptr->key, key) == 0)
+	    return nptr;
+    }
+
+    return NULL;
+}
+
+/*
+ * Delete the given node from the hash table.  Since the chains
+ * are singly linked, we must locate the start of the node's chain
+ * and traverse.
+ * Notes:
+ * 1. The node must belong to this hash table, and its key must not have
+ *    been tampered with.
+ * 2. If this deletion will take the node count below the low mark, we
+ *    shrink the table now. 
+ * 3. Determine which chain the node belongs to, and fetch the pointer
+ *    to the first node in this chain.
+ * 4. If the node being deleted is the first node in the chain, then
+ *    simply update the chain head pointer.
+ * 5. Otherwise advance to the node's predecessor, and splice out
+ *    by updating the predecessor's next pointer.
+ * 6. Indicate that the node is no longer in a hash table.
+ */
+
+CP_HIDDEN hnode_t *hash_delete(hash_t *hash, hnode_t *node)
+{
+    hash_val_t chain;
+    hnode_t *hptr;
+
+    assert (hash_lookup(hash, node->key) == node);	/* 1 */
+    assert (hash_val_t_bit != 0);
+
+    if (hash->dynamic && hash->nodecount <= hash->lowmark
+	    && hash->nodecount > INIT_SIZE)
+	shrink_table(hash);				/* 2 */
+
+    chain = node->hkey & hash->mask;			/* 3 */
+    hptr = hash->table[chain];
+
+    if (hptr == node) {					/* 4 */
+	hash->table[chain] = node->next;
+    } else {
+	while (hptr->next != node) {			/* 5 */
+	    assert (hptr != 0);
+	    hptr = hptr->next;
+	}
+	assert (hptr->next == node);
+	hptr->next = node->next;
+    }
+	
+    hash->nodecount--;
+    assert (hash_verify(hash));
+
+    node->next = NULL;					/* 6 */
+    return node;
+}
+
+CP_HIDDEN int hash_alloc_insert(hash_t *hash, const void *key, void *data)
+{
+    hnode_t *node = hash->allocnode(hash->context);
+
+    if (node) {
+	hnode_init(node, data);
+	hash_insert(hash, node, key);
+	return 1;
+    }
+    return 0;
+}
+
+CP_HIDDEN void hash_delete_free(hash_t *hash, hnode_t *node)
+{
+    hash_delete(hash, node);
+    hash->freenode(node, hash->context);
+}
+
+/*
+ *  Exactly like hash_delete, except does not trigger table shrinkage. This is to be
+ *  used from within a hash table scan operation. See notes for hash_delete.
+ */
+
+CP_HIDDEN hnode_t *hash_scan_delete(hash_t *hash, hnode_t *node)
+{
+    hash_val_t chain;
+    hnode_t *hptr;
+
+    assert (hash_lookup(hash, node->key) == node);
+    assert (hash_val_t_bit != 0);
+
+    chain = node->hkey & hash->mask;
+    hptr = hash->table[chain];
+
+    if (hptr == node) {
+	hash->table[chain] = node->next;
+    } else {
+	while (hptr->next != node) 
+	    hptr = hptr->next;
+	hptr->next = node->next;
+    }
+	
+    hash->nodecount--;
+    assert (hash_verify(hash));
+    node->next = NULL;
+
+    return node;
+}
+
+/*
+ * Like hash_delete_free but based on hash_scan_delete.
+ */
+
+CP_HIDDEN void hash_scan_delfree(hash_t *hash, hnode_t *node)
+{
+    hash_scan_delete(hash, node);
+    hash->freenode(node, hash->context);
+}
+
+/*
+ * Verify whether the given object is a valid hash table. This means
+ * Notes:
+ * 1. If the hash table is dynamic, verify whether the high and
+ *    low expansion/shrinkage thresholds are powers of two.
+ * 2. Count all nodes in the table, and test each hash value
+ *    to see whether it is correct for the node's chain.
+ */
+
+CP_HIDDEN int hash_verify(hash_t *hash)
+{
+    hashcount_t count = 0;
+    hash_val_t chain;
+    hnode_t *hptr;
+
+    if (hash->dynamic) {	/* 1 */
+	if (hash->lowmark >= hash->highmark)
+	    return 0;
+	if (!is_power_of_two(hash->highmark))
+	    return 0;
+	if (!is_power_of_two(hash->lowmark))
+	    return 0;
+    }
+
+    for (chain = 0; chain < hash->nchains; chain++) {	/* 2 */
+	for (hptr = hash->table[chain]; hptr != 0; hptr = hptr->next) {
+	    if ((hptr->hkey & hash->mask) != chain)
+		return 0;
+	    count++;
+	}
+    }
+
+    if (count != hash->nodecount)
+	return 0;
+
+    return 1;
+}
+
+/*
+ * Test whether the hash table is full and return 1 if this is true,
+ * 0 if it is false.
+ */
+
+#undef hash_isfull
+CP_HIDDEN int hash_isfull(hash_t *hash)
+{
+    return hash->nodecount == hash->maxcount;
+}
+
+/*
+ * Test whether the hash table is empty and return 1 if this is true,
+ * 0 if it is false.
+ */
+
+#undef hash_isempty
+CP_HIDDEN int hash_isempty(hash_t *hash)
+{
+    return hash->nodecount == 0;
+}
+
+static hnode_t *hnode_alloc(void *context)
+{
+    return malloc(sizeof *hnode_alloc(NULL));
+}
+
+static void hnode_free(hnode_t *node, void *context)
+{
+    free(node);
+}
+
+
+/*
+ * Create a hash table node dynamically and assign it the given data.
+ */
+
+CP_HIDDEN hnode_t *hnode_create(void *data)
+{
+    hnode_t *node = malloc(sizeof *node);
+    if (node) {
+	node->data = data;
+	node->next = NULL;
+    }
+    return node;
+}
+
+/*
+ * Initialize a client-supplied node 
+ */
+
+CP_HIDDEN hnode_t *hnode_init(hnode_t *hnode, void *data)
+{
+    hnode->data = data;
+    hnode->next = NULL;
+    return hnode;
+}
+
+/*
+ * Destroy a dynamically allocated node.
+ */
+
+CP_HIDDEN void hnode_destroy(hnode_t *hnode)
+{
+    free(hnode);
+}
+
+#undef hnode_put
+CP_HIDDEN void hnode_put(hnode_t *node, void *data)
+{
+    node->data = data;
+}
+
+#undef hnode_get
+CP_HIDDEN void *hnode_get(hnode_t *node)
+{
+    return node->data;
+}
+
+#undef hnode_getkey
+CP_HIDDEN const void *hnode_getkey(hnode_t *node)
+{
+    return node->key;
+}
+
+#undef hash_count
+CP_HIDDEN hashcount_t hash_count(hash_t *hash)
+{
+    return hash->nodecount;
+}
+
+#undef hash_size
+CP_HIDDEN hashcount_t hash_size(hash_t *hash)
+{
+    return hash->nchains;
+}
+
+static hash_val_t hash_fun_default(const void *key)
+{
+    static unsigned long randbox[] = {
+	0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
+	0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
+	0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
+	0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
+    };
+
+    const unsigned char *str = key;
+    hash_val_t acc = 0;
+
+    while (*str) {
+	acc ^= randbox[(*str + acc) & 0xf];
+	acc = (acc << 1) | (acc >> 31);
+	acc &= 0xffffffffU;
+	acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
+	acc = (acc << 2) | (acc >> 30);
+	acc &= 0xffffffffU;
+    }
+    return acc;
+}
+
+static int hash_comp_default(const void *key1, const void *key2)
+{
+    return strcmp(key1, key2);
+}
+
+#ifdef KAZLIB_TEST_MAIN
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+typedef char input_t[256];
+
+static int tokenize(char *string, ...)
+{
+    char **tokptr; 
+    va_list arglist;
+    int tokcount = 0;
+
+    va_start(arglist, string);
+    tokptr = va_arg(arglist, char **);
+    while (tokptr) {
+	while (*string && isspace((unsigned char) *string))
+	    string++;
+	if (!*string)
+	    break;
+	*tokptr = string;
+	while (*string && !isspace((unsigned char) *string))
+	    string++;
+	tokptr = va_arg(arglist, char **);
+	tokcount++;
+	if (!*string)
+	    break;
+	*string++ = 0;
+    }
+    va_end(arglist);
+
+    return tokcount;
+}
+
+static char *dupstring(char *str)
+{
+    int sz = strlen(str) + 1;
+    char *new = malloc(sz);
+    if (new)
+	memcpy(new, str, sz);
+    return new;
+}
+
+static hnode_t *new_node(void *c)
+{
+    static hnode_t few[5];
+    static int count;
+
+    if (count < 5)
+	return few + count++;
+
+    return NULL;
+}
+
+static void del_node(hnode_t *n, void *c)
+{
+}
+
+int main(void)
+{
+    input_t in;
+    hash_t *h = hash_create(HASHCOUNT_T_MAX, 0, 0);
+    hnode_t *hn;
+    hscan_t hs;
+    char *tok1, *tok2, *val;
+    const char *key;
+    int prompt = 0;
+
+    char *help =
+	"a <key> <val>          add value to hash table\n"
+	"d <key>                delete value from hash table\n"
+	"l <key>                lookup value in hash table\n"
+	"n                      show size of hash table\n"
+	"c                      show number of entries\n"
+	"t                      dump whole hash table\n"
+	"+                      increase hash table (private func)\n"
+	"-                      decrease hash table (private func)\n"
+	"b                      print hash_t_bit value\n"
+	"p                      turn prompt on\n"
+	"s                      switch to non-functioning allocator\n"
+	"q                      quit";
+
+    if (!h)
+	puts("hash_create failed");
+
+    for (;;) {
+	if (prompt)
+	    putchar('>');
+	fflush(stdout);
+
+	if (!fgets(in, sizeof(input_t), stdin))
+	    break;
+
+	switch(in[0]) {
+	    case '?':
+		puts(help);
+		break;
+	    case 'b':
+		printf("%d\n", hash_val_t_bit);
+		break;
+	    case 'a':
+		if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
+		    puts("what?");
+		    break;
+		}
+		key = dupstring(tok1);
+		val = dupstring(tok2);
+
+		if (!key || !val) {
+		    puts("out of memory");
+		    free((void *) key);
+		    free(val);
+		}
+
+		if (!hash_alloc_insert(h, key, val)) {
+		    puts("hash_alloc_insert failed");
+		    free((void *) key);
+		    free(val);
+		    break;
+		}
+		break;
+	    case 'd':
+		if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+		    puts("what?");
+		    break;
+		}
+		hn = hash_lookup(h, tok1);
+		if (!hn) {
+		    puts("hash_lookup failed");
+		    break;
+		}
+		val = hnode_get(hn);
+		key = hnode_getkey(hn);
+		hash_scan_delfree(h, hn);
+		free((void *) key);
+		free(val);
+		break;
+	    case 'l':
+		if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+		    puts("what?");
+		    break;
+		}
+		hn = hash_lookup(h, tok1);
+		if (!hn) {
+		    puts("hash_lookup failed");
+		    break;
+		}
+		val = hnode_get(hn);
+		puts(val);
+		break;
+	    case 'n':
+		printf("%lu\n", (unsigned long) hash_size(h));
+		break;
+	    case 'c':
+		printf("%lu\n", (unsigned long) hash_count(h));
+		break;
+	    case 't':
+		hash_scan_begin(&hs, h);
+		while ((hn = hash_scan_next(&hs)))
+		    printf("%s\t%s\n", (char*) hnode_getkey(hn),
+			    (char*) hnode_get(hn));
+		break;
+	    case '+':
+		grow_table(h);		/* private function	*/
+		break;
+	    case '-':
+		shrink_table(h);	/* private function	*/
+		break;
+	    case 'q':
+		exit(0);
+		break;
+	    case '\0':
+		break;
+	    case 'p':
+		prompt = 1;
+		break;
+	    case 's':
+		hash_set_allocator(h, new_node, del_node, NULL);
+		break;
+	    default:
+		putchar('?');
+		putchar('\n');
+		break;
+	}
+    }
+
+    return 0;
+}
+
+#endif
diff --git a/lib/cpluff/kazlib/hash.h b/lib/cpluff/kazlib/hash.h
new file mode 100644
index 0000000..50297ee
--- /dev/null
+++ b/lib/cpluff/kazlib/hash.h
@@ -0,0 +1,252 @@
+/*
+ * Hash Table Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz at ashi.footprints.net>
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * $Id: hash.h,v 1.22.2.7 2000/11/13 01:36:45 kaz Exp $
+ * $Name: kazlib_1_20 $
+ */
+
+/*
+ * Modified by Johannes Lehtinen in 2006-2007.
+ * Included the definition of CP_HIDDEN macro and used it in declarations and
+ * definitions to hide Kazlib symbols when building a shared C-Pluff library.
+ */
+
+#ifndef HASH_H
+#define HASH_H
+
+#ifdef _WIN32
+#include "../libcpluff/win32/cpluffdef.h"
+#else
+#include "../libcpluff/cpluffdef.h"
+#endif
+
+#include <limits.h>
+#ifdef KAZLIB_SIDEEFFECT_DEBUG
+#include "sfx.h"
+#endif
+
+/*
+ * Blurb for inclusion into C++ translation units
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned long hashcount_t;
+#define HASHCOUNT_T_MAX ULONG_MAX
+
+typedef unsigned long hash_val_t;
+#define HASH_VAL_T_MAX ULONG_MAX
+
+CP_HIDDEN extern int hash_val_t_bit;
+
+#ifndef HASH_VAL_T_BIT
+#define HASH_VAL_T_BIT ((int) hash_val_t_bit)
+#endif
+
+/*
+ * Hash chain node structure.
+ * Notes:
+ * 1. This preprocessing directive is for debugging purposes.  The effect is
+ *    that if the preprocessor symbol KAZLIB_OPAQUE_DEBUG is defined prior to the
+ *    inclusion of this header,  then the structure shall be declared as having
+ *    the single member   int __OPAQUE__.   This way, any attempts by the
+ *    client code to violate the principles of information hiding (by accessing
+ *    the structure directly) can be diagnosed at translation time. However,
+ *    note the resulting compiled unit is not suitable for linking.
+ * 2. This is a pointer to the next node in the chain. In the last node of a
+ *    chain, this pointer is null.
+ * 3. The key is a pointer to some user supplied data that contains a unique
+ *    identifier for each hash node in a given table. The interpretation of
+ *    the data is up to the user. When creating or initializing a hash table,
+ *    the user must supply a pointer to a function for comparing two keys,
+ *    and a pointer to a function for hashing a key into a numeric value.
+ * 4. The value is a user-supplied pointer to void which may refer to
+ *    any data object. It is not interpreted in any way by the hashing
+ *    module.
+ * 5. The hashed key is stored in each node so that we don't have to rehash
+ *    each key when the table must grow or shrink.
+ */
+
+typedef struct hnode_t {
+    #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)	/* 1 */
+    struct hnode_t *hash_next;		/* 2 */
+    const void *hash_key;		/* 3 */
+    void *hash_data;			/* 4 */
+    hash_val_t hash_hkey;		/* 5 */
+    #else
+    int hash_dummy;
+    #endif
+} hnode_t;
+
+/*
+ * The comparison function pointer type. A comparison function takes two keys
+ * and produces a value of -1 if the left key is less than the right key, a
+ * value of 0 if the keys are equal, and a value of 1 if the left key is
+ * greater than the right key.
+ */
+
+typedef int (*hash_comp_t)(const void *, const void *);
+
+/*
+ * The hashing function performs some computation on a key and produces an
+ * integral value of type hash_val_t based on that key. For best results, the
+ * function should have a good randomness properties in *all* significant bits
+ * over the set of keys that are being inserted into a given hash table. In
+ * particular, the most significant bits of hash_val_t are most significant to
+ * the hash module. Only as the hash table expands are less significant bits
+ * examined. Thus a function that has good distribution in its upper bits but
+ * not lower is preferrable to one that has poor distribution in the upper bits
+ * but not the lower ones.
+ */
+
+typedef hash_val_t (*hash_fun_t)(const void *);
+
+/*
+ * allocator functions
+ */
+
+typedef hnode_t *(*hnode_alloc_t)(void *);
+typedef void (*hnode_free_t)(hnode_t *, void *);
+
+/*
+ * This is the hash table control structure. It keeps track of information
+ * about a hash table, as well as the hash table itself.
+ * Notes:
+ * 1.  Pointer to the hash table proper. The table is an array of pointers to
+ *     hash nodes (of type hnode_t). If the table is empty, every element of
+ *     this table is a null pointer. A non-null entry points to the first
+ *     element of a chain of nodes.
+ * 2.  This member keeps track of the size of the hash table---that is, the
+ *     number of chain pointers.
+ * 3.  The count member maintains the number of elements that are presently
+ *     in the hash table.
+ * 4.  The maximum count is the greatest number of nodes that can populate this
+ *     table. If the table contains this many nodes, no more can be inserted,
+ *     and the hash_isfull() function returns true.
+ * 5.  The high mark is a population threshold, measured as a number of nodes,
+ *     which, if exceeded, will trigger a table expansion. Only dynamic hash
+ *     tables are subject to this expansion.
+ * 6.  The low mark is a minimum population threshold, measured as a number of
+ *     nodes. If the table population drops below this value, a table shrinkage
+ *     will occur. Only dynamic tables are subject to this reduction.  No table
+ *     will shrink beneath a certain absolute minimum number of nodes.
+ * 7.  This is the a pointer to the hash table's comparison function. The
+ *     function is set once at initialization or creation time.
+ * 8.  Pointer to the table's hashing function, set once at creation or
+ *     initialization time.
+ * 9.  The current hash table mask. If the size of the hash table is 2^N,
+ *     this value has its low N bits set to 1, and the others clear. It is used
+ *     to select bits from the result of the hashing function to compute an
+ *     index into the table.
+ * 10. A flag which indicates whether the table is to be dynamically resized. It
+ *     is set to 1 in dynamically allocated tables, 0 in tables that are
+ *     statically allocated.
+ */
+
+typedef struct hash_t {
+    #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+    struct hnode_t **hash_table;		/* 1 */
+    hashcount_t hash_nchains;			/* 2 */
+    hashcount_t hash_nodecount;			/* 3 */
+    hashcount_t hash_maxcount;			/* 4 */
+    hashcount_t hash_highmark;			/* 5 */
+    hashcount_t hash_lowmark;			/* 6 */
+    hash_comp_t hash_compare;			/* 7 */
+    hash_fun_t hash_function;			/* 8 */
+    hnode_alloc_t hash_allocnode;
+    hnode_free_t hash_freenode;
+    void *hash_context;
+    hash_val_t hash_mask;			/* 9 */
+    int hash_dynamic;				/* 10 */
+    #else
+    int hash_dummy;
+    #endif
+} hash_t;
+
+/*
+ * Hash scanner structure, used for traversals of the data structure.
+ * Notes:
+ * 1. Pointer to the hash table that is being traversed.
+ * 2. Reference to the current chain in the table being traversed (the chain
+ *    that contains the next node that shall be retrieved).
+ * 3. Pointer to the node that will be retrieved by the subsequent call to
+ *    hash_scan_next().
+ */
+
+typedef struct hscan_t {
+    #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+    hash_t *hash_table;		/* 1 */
+    hash_val_t hash_chain;	/* 2 */
+    hnode_t *hash_next;		/* 3 */
+    #else
+    int hash_dummy;
+    #endif
+} hscan_t;
+
+CP_HIDDEN extern hash_t *hash_create(hashcount_t, hash_comp_t, hash_fun_t);
+CP_HIDDEN extern void hash_set_allocator(hash_t *, hnode_alloc_t, hnode_free_t, void *);
+CP_HIDDEN extern void hash_destroy(hash_t *);
+CP_HIDDEN extern void hash_free_nodes(hash_t *);
+CP_HIDDEN extern void hash_free(hash_t *);
+CP_HIDDEN extern hash_t *hash_init(hash_t *, hashcount_t, hash_comp_t,
+	hash_fun_t, hnode_t **, hashcount_t);
+CP_HIDDEN extern void hash_insert(hash_t *, hnode_t *, const void *);
+CP_HIDDEN extern hnode_t *hash_lookup(hash_t *, const void *);
+CP_HIDDEN extern hnode_t *hash_delete(hash_t *, hnode_t *);
+CP_HIDDEN extern int hash_alloc_insert(hash_t *, const void *, void *);
+CP_HIDDEN extern void hash_delete_free(hash_t *, hnode_t *);
+
+CP_HIDDEN extern void hnode_put(hnode_t *, void *);
+CP_HIDDEN extern void *hnode_get(hnode_t *);
+CP_HIDDEN extern const void *hnode_getkey(hnode_t *);
+CP_HIDDEN extern hashcount_t hash_count(hash_t *);
+CP_HIDDEN extern hashcount_t hash_size(hash_t *);
+
+CP_HIDDEN extern int hash_isfull(hash_t *);
+CP_HIDDEN extern int hash_isempty(hash_t *);
+
+CP_HIDDEN extern void hash_scan_begin(hscan_t *, hash_t *);
+CP_HIDDEN extern hnode_t *hash_scan_next(hscan_t *);
+CP_HIDDEN extern hnode_t *hash_scan_delete(hash_t *, hnode_t *);
+CP_HIDDEN extern void hash_scan_delfree(hash_t *, hnode_t *);
+
+CP_HIDDEN extern int hash_verify(hash_t *);
+
+CP_HIDDEN extern hnode_t *hnode_create(void *);
+CP_HIDDEN extern hnode_t *hnode_init(hnode_t *, void *);
+CP_HIDDEN extern void hnode_destroy(hnode_t *);
+
+#if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+#ifdef KAZLIB_SIDEEFFECT_DEBUG
+#define hash_isfull(H) (SFX_CHECK(H)->hash_nodecount == (H)->hash_maxcount)
+#else
+#define hash_isfull(H) ((H)->hash_nodecount == (H)->hash_maxcount)
+#endif
+#define hash_isempty(H) ((H)->hash_nodecount == 0)
+#define hash_count(H) ((H)->hash_nodecount)
+#define hash_size(H) ((H)->hash_nchains)
+#define hnode_get(N) ((N)->hash_data)
+#define hnode_getkey(N) ((N)->hash_key)
+#define hnode_put(N, V) ((N)->hash_data = (V))
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/cpluff/kazlib/list.c b/lib/cpluff/kazlib/list.c
new file mode 100644
index 0000000..dc8eed0
--- /dev/null
+++ b/lib/cpluff/kazlib/list.c
@@ -0,0 +1,935 @@
+/*
+ * List Abstract Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz at ashi.footprints.net>
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * $Id: list.c,v 1.19.2.1 2000/04/17 01:07:21 kaz Exp $
+ * $Name: kazlib_1_20 $
+ */
+
+/*
+ * Modified by Johannes Lehtinen in 2006-2007.
+ * Included the definition of CP_HIDDEN macro and used it in declarations and
+ * definitions to hide Kazlib symbols when building a shared C-Pluff library.
+ */
+
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <assert.h>
+#define LIST_IMPLEMENTATION
+#include "list.h"
+
+#define next list_next
+#define prev list_prev
+#define data list_data
+
+#define pool list_pool
+#define fre list_free
+#define size list_size
+
+#define nilnode list_nilnode
+#define nodecount list_nodecount
+#define maxcount list_maxcount
+
+#define list_nil(L)		(&(L)->nilnode)
+#define list_first_priv(L)	((L)->nilnode.next)
+#define list_last_priv(L)	((L)->nilnode.prev)
+#define lnode_next(N)		((N)->next)
+#define lnode_prev(N)		((N)->prev)
+
+#ifdef KAZLIB_RCSID
+static const char rcsid[] = "$Id: list.c,v 1.19.2.1 2000/04/17 01:07:21 kaz Exp $";
+#endif
+
+/*
+ * Initialize a list object supplied by the client such that it becomes a valid
+ * empty list. If the list is to be ``unbounded'', the maxcount should be
+ * specified as LISTCOUNT_T_MAX, or, alternately, as -1. The value zero
+ * is not permitted.
+ */
+
+CP_HIDDEN list_t *list_init(list_t *list, listcount_t maxcount)
+{
+    assert (maxcount != 0);
+    list->nilnode.next = &list->nilnode;
+    list->nilnode.prev = &list->nilnode;
+    list->nodecount = 0;
+    list->maxcount = maxcount;
+    return list;
+}
+
+/*
+ * Dynamically allocate a list object using malloc(), and initialize it so that
+ * it is a valid empty list. If the list is to be ``unbounded'', the maxcount
+ * should be specified as LISTCOUNT_T_MAX, or, alternately, as -1.
+ */
+
+CP_HIDDEN list_t *list_create(listcount_t maxcount)
+{
+    list_t *new = malloc(sizeof *new);
+    if (new) {
+	assert (maxcount != 0);
+	new->nilnode.next = &new->nilnode;
+	new->nilnode.prev = &new->nilnode;
+	new->nodecount = 0;
+	new->maxcount = maxcount;
+    }
+    return new;
+}
+
+/*
+ * Destroy a dynamically allocated list object.
+ * The client must remove the nodes first.
+ */
+
+CP_HIDDEN void list_destroy(list_t *list)
+{
+    assert (list_isempty(list));
+    free(list);
+}
+
+/*
+ * Free all of the nodes of a list. The list must contain only 
+ * dynamically allocated nodes. After this call, the list
+ * is empty.
+ */
+
+CP_HIDDEN void list_destroy_nodes(list_t *list)
+{
+    lnode_t *lnode = list_first_priv(list), *nil = list_nil(list), *tmp;
+
+    while (lnode != nil) {
+	tmp = lnode->next;
+	lnode->next = NULL;
+	lnode->prev = NULL;
+	lnode_destroy(lnode);
+	lnode = tmp;
+    }
+
+    list_init(list, list->maxcount);
+}
+
+/*
+ * Return all of the nodes of a list to a node pool. The nodes in
+ * the list must all have come from the same pool.
+ */
+
+CP_HIDDEN void list_return_nodes(list_t *list, lnodepool_t *pool)
+{
+    lnode_t *lnode = list_first_priv(list), *tmp, *nil = list_nil(list);
+
+    while (lnode != nil) {
+	tmp = lnode->next;
+	lnode->next = NULL;
+	lnode->prev = NULL;
+	lnode_return(pool, lnode);
+	lnode = tmp;
+    }
+
+    list_init(list, list->maxcount);
+}
+
+/*
+ * Insert the node ``new'' into the list immediately after ``this'' node.
+ */
+
+CP_HIDDEN void list_ins_after(list_t *list, lnode_t *new, lnode_t *this)
+{
+    lnode_t *that = this->next;
+
+    assert (new != NULL);
+    assert (!list_contains(list, new));
+    assert (!lnode_is_in_a_list(new));
+    assert (this == list_nil(list) || list_contains(list, this));
+    assert (list->nodecount + 1 > list->nodecount);
+
+    new->prev = this;
+    new->next = that;
+    that->prev = new;
+    this->next = new;
+    list->nodecount++;
+
+    assert (list->nodecount <= list->maxcount);
+}
+
+/*
+ * Insert the node ``new'' into the list immediately before ``this'' node.
+ */
+
+CP_HIDDEN void list_ins_before(list_t *list, lnode_t *new, lnode_t *this)
+{
+    lnode_t *that = this->prev;
+
+    assert (new != NULL);
+    assert (!list_contains(list, new));
+    assert (!lnode_is_in_a_list(new));
+    assert (this == list_nil(list) || list_contains(list, this));
+    assert (list->nodecount + 1 > list->nodecount);
+
+    new->next = this;
+    new->prev = that;
+    that->next = new;
+    this->prev = new;
+    list->nodecount++;
+
+    assert (list->nodecount <= list->maxcount);
+}
+
+/*
+ * Delete the given node from the list.
+ */
+
+CP_HIDDEN lnode_t *list_delete(list_t *list, lnode_t *del)
+{
+    lnode_t *next = del->next;
+    lnode_t *prev = del->prev;
+
+    assert (list_contains(list, del));
+
+    prev->next = next;
+    next->prev = prev;
+    list->nodecount--;
+
+    del->next = del->prev = NULL;
+
+    return del;
+}
+
+/*
+ * For each node in the list, execute the given function. The list,
+ * current node and the given context pointer are passed on each
+ * call to the function.
+ */
+
+CP_HIDDEN void list_process(list_t *list, void *context,
+	void (* function)(list_t *list, lnode_t *lnode, void *context))
+{
+    lnode_t *node = list_first_priv(list), *next, *nil = list_nil(list);
+
+    while (node != nil) {
+	/* check for callback function deleting	*/
+	/* the next node from under us		*/
+	assert (list_contains(list, node));
+	next = node->next;
+	function(list, node, context);
+	node = next;
+    }
+}
+
+/*
+ * Dynamically allocate a list node and assign it the given piece of data.
+ */
+
+CP_HIDDEN lnode_t *lnode_create(void *data)
+{
+    lnode_t *new = malloc(sizeof *new);
+    if (new) {
+	new->data = data;
+	new->next = NULL;
+	new->prev = NULL;
+    }
+    return new;
+}
+
+/*
+ * Initialize a user-supplied lnode.
+ */
+
+CP_HIDDEN lnode_t *lnode_init(lnode_t *lnode, void *data)
+{
+    lnode->data = data;
+    lnode->next = NULL;
+    lnode->prev = NULL;
+    return lnode;
+}
+
+/*
+ * Destroy a dynamically allocated node.
+ */
+
+CP_HIDDEN void lnode_destroy(lnode_t *lnode)
+{
+    assert (!lnode_is_in_a_list(lnode));
+    free(lnode);
+}
+
+/*
+ * Initialize a node pool object to use a user-supplied set of nodes.
+ * The ``nodes'' pointer refers to an array of lnode_t objects, containing
+ * ``n'' elements.
+ */
+
+CP_HIDDEN lnodepool_t *lnode_pool_init(lnodepool_t *pool, lnode_t *nodes, listcount_t n)
+{
+    listcount_t i;
+
+    assert (n != 0);
+
+    pool->pool = nodes;
+    pool->fre = nodes;
+    pool->size = n;
+    for (i = 0; i < n - 1; i++) {
+	nodes[i].next = nodes + i + 1;
+    }
+    nodes[i].next = NULL;
+    nodes[i].prev = nodes;	/* to make sure node is marked ``on list'' */
+    return pool;
+}
+
+/*
+ * Create a dynamically allocated pool of n nodes.
+ */
+
+CP_HIDDEN lnodepool_t *lnode_pool_create(listcount_t n)
+{
+    lnodepool_t *pool;
+    lnode_t *nodes;
+
+    assert (n != 0);
+
+    pool = malloc(sizeof *pool);
+    if (!pool)
+	return NULL;
+    nodes = malloc(n * sizeof *nodes);
+    if (!nodes) {
+	free(pool);
+	return NULL;
+    }
+    lnode_pool_init(pool, nodes, n);
+    return pool;
+}
+
+/*
+ * Determine whether the given pool is from this pool.
+ */
+
+CP_HIDDEN int lnode_pool_isfrom(lnodepool_t *pool, lnode_t *node)
+{
+    listcount_t i;
+
+    /* this is carefully coded this way because ANSI C forbids pointers
+       to different objects from being subtracted or compared other
+       than for exact equality */
+
+    for (i = 0; i < pool->size; i++) {
+	if (pool->pool + i == node)
+	    return 1;
+    }
+    return 0;
+}
+
+/*
+ * Destroy a dynamically allocated pool of nodes.
+ */
+
+CP_HIDDEN void lnode_pool_destroy(lnodepool_t *p)
+{
+    free(p->pool);
+    free(p);
+}
+
+/*
+ * Borrow a node from a node pool. Returns a null pointer if the pool
+ * is exhausted. 
+ */
+
+CP_HIDDEN lnode_t *lnode_borrow(lnodepool_t *pool, void *data)
+{
+    lnode_t *new = pool->fre;
+    if (new) {
+	pool->fre = new->next;
+	new->data = data;
+	new->next = NULL;
+	new->prev = NULL;
+    }
+    return new;
+}
+
+/*
+ * Return a node to a node pool. A node must be returned to the pool
+ * from which it came.
+ */
+
+CP_HIDDEN void lnode_return(lnodepool_t *pool, lnode_t *node)
+{
+    assert (lnode_pool_isfrom(pool, node));
+    assert (!lnode_is_in_a_list(node));
+
+    node->next = pool->fre;
+    node->prev = node;
+    pool->fre = node;
+}
+
+/*
+ * Determine whether the given list contains the given node.
+ * According to this function, a list does not contain its nilnode.
+ */
+
+CP_HIDDEN int list_contains(list_t *list, lnode_t *node)
+{
+    lnode_t *n, *nil = list_nil(list);
+
+    for (n = list_first_priv(list); n != nil; n = lnode_next(n)) {
+	if (node == n)
+	    return 1;
+    }
+
+    return 0;
+}
+
+/*
+ * A more generalized variant of list_transfer. This one removes a
+ * ``slice'' from the source list and appends it to the destination
+ * list.
+ */
+
+CP_HIDDEN void list_extract(list_t *dest, list_t *source, lnode_t *first, lnode_t *last)
+{
+    listcount_t moved = 1;
+
+    assert (first == NULL || list_contains(source, first));
+    assert (last == NULL || list_contains(source, last));
+
+    if (first == NULL || last == NULL)
+	return;
+
+    /* adjust the destination list so that the slice is spliced out */
+
+    first->prev->next = last->next;
+    last->next->prev = first->prev;
+
+    /* graft the splice at the end of the dest list */
+
+    last->next = &dest->nilnode;
+    first->prev = dest->nilnode.prev;
+    dest->nilnode.prev->next = first;
+    dest->nilnode.prev = last;
+
+    while (first != last) {
+	first = first->next;
+	assert (first != list_nil(source));	/* oops, last before first! */
+	moved++;
+    }
+    
+    /* assert no overflows */
+    assert (source->nodecount - moved <= source->nodecount);
+    assert (dest->nodecount + moved >= dest->nodecount);
+
+    /* assert no weirdness */
+    assert (moved <= source->nodecount);
+
+    source->nodecount -= moved;
+    dest->nodecount += moved;
+
+    /* assert list sanity */
+    assert (list_verify(source));
+    assert (list_verify(dest));
+}
+
+
+/*
+ * Split off a trailing sequence of nodes from the source list and relocate
+ * them to the tail of the destination list. The trailing sequence begins
+ * with node ``first'' and terminates with the last node of the source
+ * list. The nodes are added to the end of the new list in their original
+ * order.
+ */
+
+CP_HIDDEN void list_transfer(list_t *dest, list_t *source, lnode_t *first)
+{
+    listcount_t moved = 1;
+    lnode_t *last;
+
+    assert (first == NULL || list_contains(source, first));
+
+    if (first == NULL)
+	return;
+
+    last = source->nilnode.prev;
+
+    source->nilnode.prev = first->prev;
+    first->prev->next = &source->nilnode;
+
+    last->next = &dest->nilnode;
+    first->prev = dest->nilnode.prev;
+    dest->nilnode.prev->next = first;
+    dest->nilnode.prev = last;
+
+    while (first != last) {
+	first = first->next;
+	moved++;
+    }
+    
+    /* assert no overflows */
+    assert (source->nodecount - moved <= source->nodecount);
+    assert (dest->nodecount + moved >= dest->nodecount);
+
+    /* assert no weirdness */
+    assert (moved <= source->nodecount);
+
+    source->nodecount -= moved;
+    dest->nodecount += moved;
+
+    /* assert list sanity */
+    assert (list_verify(source));
+    assert (list_verify(dest));
+}
+
+CP_HIDDEN void list_merge(list_t *dest, list_t *sour,
+	int compare (const void *, const void *))
+{
+    lnode_t *dn, *sn, *tn;
+    lnode_t *d_nil = list_nil(dest), *s_nil = list_nil(sour);
+
+    /* Nothing to do if source and destination list are the same. */
+    if (dest == sour)
+	return;
+
+    /* overflow check */
+    assert (list_count(sour) + list_count(dest) >= list_count(sour));
+
+    /* lists must be sorted */
+    assert (list_is_sorted(sour, compare));
+    assert (list_is_sorted(dest, compare));
+
+    dn = list_first_priv(dest);
+    sn = list_first_priv(sour);
+
+    while (dn != d_nil && sn != s_nil) {
+	if (compare(lnode_get(dn), lnode_get(sn)) >= 0) {
+	    tn = lnode_next(sn);
+	    list_delete(sour, sn);
+	    list_ins_before(dest, sn, dn);
+	    sn = tn;
+	} else {
+	    dn = lnode_next(dn);
+	}
+    }
+
+    if (dn != d_nil)
+	return;
+
+    if (sn != s_nil)
+	list_transfer(dest, sour, sn);
+}
+
+CP_HIDDEN void list_sort(list_t *list, int compare(const void *, const void *))
+{
+    list_t extra;
+    listcount_t middle;
+    lnode_t *node;
+
+    if (list_count(list) > 1) {
+	middle = list_count(list) / 2;
+	node = list_first_priv(list);
+
+	list_init(&extra, list_count(list) - middle);
+
+	while (middle--)
+	    node = lnode_next(node);
+	
+	list_transfer(&extra, list, node);
+	list_sort(list, compare);
+	list_sort(&extra, compare);
+	list_merge(list, &extra, compare);
+    } 
+    assert (list_is_sorted(list, compare));
+}
+
+CP_HIDDEN lnode_t *list_find(list_t *list, const void *key, int compare(const void *, const void *))
+{
+    lnode_t *node;
+
+    for (node = list_first_priv(list); node != list_nil(list); node = node->next) {
+	if (compare(lnode_get(node), key) == 0)
+	    return node;
+    }
+    
+    return 0;
+}
+
+
+/*
+ * Return 1 if the list is in sorted order, 0 otherwise
+ */
+
+CP_HIDDEN int list_is_sorted(list_t *list, int compare(const void *, const void *))
+{
+    lnode_t *node, *next, *nil;
+
+    next = nil = list_nil(list);
+    node = list_first_priv(list);
+
+    if (node != nil)
+	next = lnode_next(node);
+
+    for (; next != nil; node = next, next = lnode_next(next)) {
+	if (compare(lnode_get(node), lnode_get(next)) > 0)
+	    return 0;
+    }
+
+    return 1;
+}
+
+/*
+ * Get rid of macro functions definitions so they don't interfere
+ * with the actual definitions
+ */
+
+#undef list_isempty
+#undef list_isfull
+#undef lnode_pool_isempty
+#undef list_append
+#undef list_prepend
+#undef list_first
+#undef list_last
+#undef list_next
+#undef list_prev
+#undef list_count
+#undef list_del_first
+#undef list_del_last
+#undef lnode_put
+#undef lnode_get
+
+/*
+ * Return 1 if the list is empty, 0 otherwise
+ */
+
+CP_HIDDEN int list_isempty(list_t *list)
+{
+    return list->nodecount == 0;
+}
+
+/*
+ * Return 1 if the list is full, 0 otherwise
+ * Permitted only on bounded lists. 
+ */
+
+CP_HIDDEN int list_isfull(list_t *list)
+{
+    return list->nodecount == list->maxcount;
+}
+
+/*
+ * Check if the node pool is empty.
+ */
+
+CP_HIDDEN int lnode_pool_isempty(lnodepool_t *pool)
+{
+    return (pool->fre == NULL);
+}
+
+/*
+ * Add the given node at the end of the list
+ */
+
+CP_HIDDEN void list_append(list_t *list, lnode_t *node)
+{
+    list_ins_before(list, node, &list->nilnode);
+}
+
+/*
+ * Add the given node at the beginning of the list.
+ */
+
+CP_HIDDEN void list_prepend(list_t *list, lnode_t *node)
+{
+    list_ins_after(list, node, &list->nilnode);
+}
+
+/*
+ * Retrieve the first node of the list
+ */
+
+CP_HIDDEN lnode_t *list_first(list_t *list)
+{
+    if (list->nilnode.next == &list->nilnode)
+	return NULL;
+    return list->nilnode.next;
+}
+
+/*
+ * Retrieve the last node of the list
+ */
+
+CP_HIDDEN lnode_t *list_last(list_t *list)
+{
+    if (list->nilnode.prev == &list->nilnode)
+	return NULL;
+    return list->nilnode.prev;
+}
+
+/*
+ * Retrieve the count of nodes in the list
+ */
+
+CP_HIDDEN listcount_t list_count(list_t *list)
+{
+    return list->nodecount;
+}
+
+/*
+ * Remove the first node from the list and return it.
+ */
+
+CP_HIDDEN lnode_t *list_del_first(list_t *list)
+{
+    return list_delete(list, list->nilnode.next);
+}
+
+/*
+ * Remove the last node from the list and return it.
+ */
+
+CP_HIDDEN lnode_t *list_del_last(list_t *list)
+{
+    return list_delete(list, list->nilnode.prev);
+}
+
+
+/*
+ * Associate a data item with the given node.
+ */
+
+CP_HIDDEN void lnode_put(lnode_t *lnode, void *data)
+{
+    lnode->data = data;
+}
+
+/*
+ * Retrieve the data item associated with the node.
+ */
+
+CP_HIDDEN void *lnode_get(lnode_t *lnode)
+{
+    return lnode->data;
+}
+
+/*
+ * Retrieve the node's successor. If there is no successor, 
+ * NULL is returned.
+ */
+
+CP_HIDDEN lnode_t *list_next(list_t *list, lnode_t *lnode)
+{
+    assert (list_contains(list, lnode));
+
+    if (lnode->next == list_nil(list))
+	return NULL;
+    return lnode->next;
+}
+
+/*
+ * Retrieve the node's predecessor. See comment for lnode_next().
+ */
+
+CP_HIDDEN lnode_t *list_prev(list_t *list, lnode_t *lnode)
+{
+    assert (list_contains(list, lnode));
+
+    if (lnode->prev == list_nil(list))
+	return NULL;
+    return lnode->prev;
+}
+
+/*
+ * Return 1 if the lnode is in some list, otherwise return 0.
+ */
+
+CP_HIDDEN int lnode_is_in_a_list(lnode_t *lnode)
+{
+    return (lnode->next != NULL || lnode->prev != NULL);
+}
+
+
+CP_HIDDEN int list_verify(list_t *list)
+{
+    lnode_t *node = list_first_priv(list), *nil = list_nil(list);
+    listcount_t count = list_count(list);
+
+    if (node->prev != nil)
+	return 0;
+
+    if (count > list->maxcount)
+	return 0;
+
+    while (node != nil && count--) {
+	if (node->next->prev != node)
+	    return 0;
+	node = node->next;
+    }
+
+    if (count != 0 || node != nil)
+	return 0;
+    
+    return 1;
+}
+
+#ifdef KAZLIB_TEST_MAIN
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+typedef char input_t[256];
+
+static int tokenize(char *string, ...)
+{
+    char **tokptr; 
+    va_list arglist;
+    int tokcount = 0;
+
+    va_start(arglist, string);
+    tokptr = va_arg(arglist, char **);
+    while (tokptr) {
+	while (*string && isspace((unsigned char) *string))
+	    string++;
+	if (!*string)
+	    break;
+	*tokptr = string;
+	while (*string && !isspace((unsigned char) *string))
+	    string++;
+	tokptr = va_arg(arglist, char **);
+	tokcount++;
+	if (!*string)
+	    break;
+	*string++ = 0;
+    }
+    va_end(arglist);
+
+    return tokcount;
+}
+
+static int comparef(const void *key1, const void *key2)
+{
+    return strcmp(key1, key2);
+}
+
+static char *dupstring(char *str)
+{
+    int sz = strlen(str) + 1;
+    char *new = malloc(sz);
+    if (new)
+	memcpy(new, str, sz);
+    return new;
+}
+
+int main(void)
+{
+    input_t in;
+    list_t *l = list_create(LISTCOUNT_T_MAX);
+    lnode_t *ln;
+    char *tok1, *val;
+    int prompt = 0;
+
+    char *help =
+	"a <val>                append value to list\n"
+	"d <val>                delete value from list\n"
+	"l <val>                lookup value in list\n"
+	"s                      sort list\n"
+	"c                      show number of entries\n"
+	"t                      dump whole list\n"
+	"p                      turn prompt on\n"
+	"q                      quit";
+
+    if (!l)
+	puts("list_create failed");
+
+    for (;;) {
+	if (prompt)
+	    putchar('>');
+	fflush(stdout);
+
+	if (!fgets(in, sizeof(input_t), stdin))
+	    break;
+
+	switch(in[0]) {
+	    case '?':
+		puts(help);
+		break;
+	    case 'a':
+		if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+		    puts("what?");
+		    break;
+		}
+		val = dupstring(tok1);
+		ln = lnode_create(val);
+	
+		if (!val || !ln) {
+		    puts("allocation failure");
+		    if (ln)
+			lnode_destroy(ln);
+		    free(val);
+		    break;
+		}
+    
+		list_append(l, ln);
+		break;
+	    case 'd':
+		if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+		    puts("what?");
+		    break;
+		}
+		ln = list_find(l, tok1, comparef);
+		if (!ln) {
+		    puts("list_find failed");
+		    break;
+		}
+		list_delete(l, ln);
+		val = lnode_get(ln);
+		lnode_destroy(ln);
+		free(val);
+		break;
+	    case 'l':
+		if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+		    puts("what?");
+		    break;
+		}
+		ln = list_find(l, tok1, comparef);
+		if (!ln)
+		    puts("list_find failed");
+		else
+		    puts("found");
+		break;
+	    case 's':
+		list_sort(l, comparef);
+		break;
+	    case 'c':
+		printf("%lu\n", (unsigned long) list_count(l));
+		break;
+	    case 't':
+		for (ln = list_first(l); ln != 0; ln = list_next(l, ln))
+		    puts(lnode_get(ln));
+		break;
+	    case 'q':
+		exit(0);
+		break;
+	    case '\0':
+		break;
+	    case 'p':
+		prompt = 1;
+		break;
+	    default:
+		putchar('?');
+		putchar('\n');
+		break;
+	}
+    }
+
+    return 0;
+}
+
+#endif	/* defined TEST_MAIN */
diff --git a/lib/cpluff/kazlib/list.h b/lib/cpluff/kazlib/list.h
new file mode 100644
index 0000000..ccad262
--- /dev/null
+++ b/lib/cpluff/kazlib/list.h
@@ -0,0 +1,166 @@
+/*
+ * List Abstract Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz at ashi.footprints.net>
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * $Id: list.h,v 1.19 1999/11/14 20:46:19 kaz Exp $
+ * $Name: kazlib_1_20 $
+ */
+
+/*
+ * Modified by Johannes Lehtinen in 2006-2007.
+ * Included the definition of CP_HIDDEN macro and used it in declarations and
+ * definitions to hide Kazlib symbols when building a shared C-Pluff library.
+ */
+
+#ifndef LIST_H
+#define LIST_H
+
+#ifdef _WIN32
+#include "../libcpluff/win32/cpluffdef.h"
+#else
+#include "../libcpluff/cpluffdef.h"
+#endif
+
+#include <limits.h>
+
+#ifdef KAZLIB_SIDEEFFECT_DEBUG
+#include "sfx.h"
+#define LIST_SFX_CHECK(E) SFX_CHECK(E)
+#else
+#define LIST_SFX_CHECK(E) (E)
+#endif
+
+/*
+ * Blurb for inclusion into C++ translation units
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned long listcount_t;
+#define LISTCOUNT_T_MAX ULONG_MAX
+
+typedef struct lnode_t {
+    #if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+    struct lnode_t *list_next;
+    struct lnode_t *list_prev;
+    void *list_data;
+    #else
+    int list_dummy;
+    #endif
+} lnode_t;
+
+typedef struct lnodepool_t {
+    #if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+    struct lnode_t *list_pool;
+    struct lnode_t *list_free;
+    listcount_t list_size;
+    #else
+    int list_dummy;
+    #endif
+} lnodepool_t;
+
+typedef struct list_t {
+    #if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+    lnode_t list_nilnode;
+    listcount_t list_nodecount;
+    listcount_t list_maxcount;
+    #else
+    int list_dummy;
+    #endif
+} list_t;
+
+CP_HIDDEN lnode_t *lnode_create(void *);
+CP_HIDDEN lnode_t *lnode_init(lnode_t *, void *);
+CP_HIDDEN void lnode_destroy(lnode_t *);
+CP_HIDDEN void lnode_put(lnode_t *, void *);
+CP_HIDDEN void *lnode_get(lnode_t *);
+CP_HIDDEN int lnode_is_in_a_list(lnode_t *);
+
+#if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+#define lnode_put(N, D)		((N)->list_data = (D))
+#define lnode_get(N)		((N)->list_data)
+#endif
+
+CP_HIDDEN lnodepool_t *lnode_pool_init(lnodepool_t *, lnode_t *, listcount_t);
+CP_HIDDEN lnodepool_t *lnode_pool_create(listcount_t);
+CP_HIDDEN void lnode_pool_destroy(lnodepool_t *);
+CP_HIDDEN lnode_t *lnode_borrow(lnodepool_t *, void *);
+CP_HIDDEN void lnode_return(lnodepool_t *, lnode_t *);
+CP_HIDDEN int lnode_pool_isempty(lnodepool_t *);
+CP_HIDDEN int lnode_pool_isfrom(lnodepool_t *, lnode_t *);
+
+CP_HIDDEN list_t *list_init(list_t *, listcount_t);
+CP_HIDDEN list_t *list_create(listcount_t);
+CP_HIDDEN void list_destroy(list_t *);
+CP_HIDDEN void list_destroy_nodes(list_t *);
+CP_HIDDEN void list_return_nodes(list_t *, lnodepool_t *);
+
+CP_HIDDEN listcount_t list_count(list_t *);
+CP_HIDDEN int list_isempty(list_t *);
+CP_HIDDEN int list_isfull(list_t *);
+CP_HIDDEN int list_contains(list_t *, lnode_t *);
+
+CP_HIDDEN void list_append(list_t *, lnode_t *);
+CP_HIDDEN void list_prepend(list_t *, lnode_t *);
+CP_HIDDEN void list_ins_before(list_t *, lnode_t *, lnode_t *);
+CP_HIDDEN void list_ins_after(list_t *, lnode_t *, lnode_t *);
+
+CP_HIDDEN lnode_t *list_first(list_t *);
+CP_HIDDEN lnode_t *list_last(list_t *);
+CP_HIDDEN lnode_t *list_next(list_t *, lnode_t *);
+CP_HIDDEN lnode_t *list_prev(list_t *, lnode_t *);
+
+CP_HIDDEN lnode_t *list_del_first(list_t *);
+CP_HIDDEN lnode_t *list_del_last(list_t *);
+CP_HIDDEN lnode_t *list_delete(list_t *, lnode_t *);
+
+CP_HIDDEN void list_process(list_t *, void *, void (*)(list_t *, lnode_t *, void *));
+
+CP_HIDDEN int list_verify(list_t *);
+
+#if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+#define lnode_pool_isempty(P)	((P)->list_free == 0)
+#define list_count(L)		((L)->list_nodecount)
+#define list_isempty(L)		((L)->list_nodecount == 0)
+#define list_isfull(L)		(LIST_SFX_CHECK(L)->list_nodecount == (L)->list_maxcount)
+#define list_next(L, N)		(LIST_SFX_CHECK(N)->list_next == &(L)->list_nilnode ? NULL : (N)->list_next)
+#define list_prev(L, N)		(LIST_SFX_CHECK(N)->list_prev == &(L)->list_nilnode ? NULL : (N)->list_prev)
+#define list_first(L)		list_next(LIST_SFX_CHECK(L), &(L)->list_nilnode)
+#define list_last(L)		list_prev(LIST_SFX_CHECK(L), &(L)->list_nilnode)
+#endif
+
+#if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+#define list_append(L, N)	list_ins_before(LIST_SFX_CHECK(L), N, &(L)->list_nilnode)
+#define list_prepend(L, N)	list_ins_after(LIST_SFX_CHECK(L), N, &(L)->list_nilnode)
+#define list_del_first(L)	list_delete(LIST_SFX_CHECK(L), list_first(L))
+#define list_del_last(L)	list_delete(LIST_SFX_CHECK(L), list_last(L))
+#endif
+
+/* destination list on the left, source on the right */
+
+CP_HIDDEN void list_extract(list_t *, list_t *, lnode_t *, lnode_t *);
+CP_HIDDEN void list_transfer(list_t *, list_t *, lnode_t *first);
+CP_HIDDEN void list_merge(list_t *, list_t *, int (const void *, const void *));
+CP_HIDDEN void list_sort(list_t *, int (const void *, const void *));
+CP_HIDDEN lnode_t *list_find(list_t *, const void *, int (const void *, const void *));
+CP_HIDDEN int list_is_sorted(list_t *, int (const void *, const void *));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

-- 
kodi packaging



More information about the pkg-multimedia-commits mailing list