[med-svn] [kmer-tools] 04/13: Imported Upstream version 0~20150903+r2013
Afif Elghraoui
afif-guest at moszumanska.debian.org
Sat Jan 2 07:24:11 UTC 2016
This is an automated email from the git hooks/post-receive script.
afif-guest pushed a commit to branch master
in repository kmer-tools.
commit f2f4f9570561175ea5af577ff239cdc99ee87786
Author: Afif Elghraoui <afif at ghraoui.name>
Date: Fri Jan 1 20:06:05 2016 -0800
Imported Upstream version 0~20150903+r2013
---
PACKAGING | 104 +
configure.sh | 6 +-
libutil/kazlib/Make.include | 27 +
libutil/kazlib/blast.pl | 33 +
libutil/kazlib/dict.c | 1238 +++++++++
libutil/kazlib/dict.h | 142 +
libutil/kazlib/docs/CHANGES | 290 +++
libutil/kazlib/docs/MUST_READ | 25 +
libutil/kazlib/docs/README | 66 +
libutil/kazlib/docs/docs.ist | 4 +
libutil/kazlib/docs/docs.ltx | 4155 ++++++++++++++++++++++++++++++
libutil/kazlib/drivers/dict-main.c | 300 +++
libutil/kazlib/drivers/except-main.c | 57 +
libutil/kazlib/drivers/hash-main.c | 187 ++
libutil/kazlib/drivers/list-main.c | 152 ++
libutil/kazlib/drivers/sfx-main.c | 41 +
libutil/kazlib/except.c | 347 +++
libutil/kazlib/except.h | 147 ++
libutil/kazlib/hash.c | 837 ++++++
libutil/kazlib/hash.h | 238 ++
libutil/kazlib/list.c | 766 ++++++
libutil/kazlib/list.h | 152 ++
libutil/kazlib/sfx.c | 1138 ++++++++
libutil/kazlib/sfx.h | 46 +
meryl/args.C | 2 +-
meryl/build.C | 70 +-
meryl/estimate.C | 136 +-
meryl/kmer-mask.C | 344 ++-
meryl/mapMers.C | 26 +-
meryl/meryl.H | 7 +-
sim4dbutils/reportAlignmentDifferences.C | 2 +-
31 files changed, 10810 insertions(+), 275 deletions(-)
diff --git a/PACKAGING b/PACKAGING
new file mode 100644
index 0000000..dde039c
--- /dev/null
+++ b/PACKAGING
@@ -0,0 +1,104 @@
+
+ALL
+---
+
+rm -rf .svn
+
+
+
+ATAC
+----
+
+rm -rf ESTmapper
+rm -rf ESTmapper\ GSAC.pdf
+rm -rf ESTmapper\ GSAC.ppt
+rm -rf ESTmapper\ LaTeX
+rm -rf Makefile.wiki
+rm -rf PACKAGING
+rm -rf README.leaff
+rm -rf README.meryl
+rm -rf README.sim4db
+rm -rf developer-doc
+rm -rf libsim4
+rm -rf seagen
+rm -rf sim4db
+rm -rf sim4dbutils
+rm -rf snapper
+rm -rf tapper
+rm -rf trie
+
+
+MERYL
+-----
+
+rm -rf ESTmapper
+rm -rf ESTmapper\ GSAC.pdf
+rm -rf ESTmapper\ GSAC.ppt
+rm -rf ESTmapper\ LaTeX
+rm -rf Makefile.wiki
+rm -rf PACKAGING
+rm -rf README.atac
+rm -rf README.leaff
+rm -rf README.sim4db
+rm -rf atac-driver
+rm -rf developer-doc
+rm -rf leaff
+rm -rf libsim4
+rm -rf seagen
+rm -rf seatac
+rm -rf sim4db
+rm -rf sim4dbutils
+rm -rf snapper
+rm -rf tapper
+rm -rf trie
+
+
+SIM4DB
+------
+
+rm -rf ESTmapper
+rm -rf ESTmapper\ GSAC.pdf
+rm -rf ESTmapper\ GSAC.ppt
+rm -rf ESTmapper\ LaTeX
+rm -rf Makefile.wiki
+rm -rf PACKAGING
+rm -rf README.atac
+rm -rf README.meryl
+rm -rf atac-driver
+rm -rf developer-doc
+rm -rf libkmer
+rm -rf libmeryl
+rm -rf meryl
+rm -rf seagen
+rm -rf seatac
+rm -rf snapper
+rm -rf tapper
+rm -rf trie
+
+
+ESTmapper
+---------
+
+rm -rf ESTmapper\ GSAC.pdf
+rm -rf ESTmapper\ GSAC.ppt
+rm -rf ESTmapper\ LaTeX
+rm -rf Makefile.wiki
+rm -rf PACKAGING
+rm -rf README.atac
+rm -rf README.leaff
+rm -rf README.meryl
+rm -rf README.sim4db
+rm -rf atac-driver
+rm -rf developer-doc
+rm -rf seatac
+rm -rf snapper
+rm -rf tapper
+rm -rf trie
+
+
+rm -rf ATAC-r2008/.svn ESTmapper-r2008/.svn meryl-r2008/.svn sim4db-r2008/.svn
+
+tar -cf ATAC-r2008.tar ATAC-r2008
+tar -cf ESTmapper-r2008.tar ESTmapper-r2008
+tar -cf meryl-r2008.tar meryl-r2008
+tar -cf sim4db-r2008.tar sim4db-r2008
diff --git a/configure.sh b/configure.sh
index 95a0bf0..f1a12a4 100755
--- a/configure.sh
+++ b/configure.sh
@@ -360,7 +360,7 @@ esac
cat <<EOF >> Make.compilers
-PERL := /usr/bin/perl
+PERL := /usr/bin/env perl
.EXE :=
.SO := .so
.A := .a
@@ -369,8 +369,8 @@ CLD := \${CC}
CXXLD := \${CXX}
CCDEP := \${CC} -MM -MG
CXXDEP := \${CXX} -MM -MG
-CLIBS += -lm -lbz2
-CXXLIBS += -lm -lbz2
+CLIBS += -lm
+CXXLIBS += -lm
PYTHON := $PYTHON
PYTHON_H := $CFLAGS_PYTHON/Python.h
CFLAGS_PYTHON := -I$CFLAGS_PYTHON
diff --git a/libutil/kazlib/Make.include b/libutil/kazlib/Make.include
new file mode 100644
index 0000000..7ecb369
--- /dev/null
+++ b/libutil/kazlib/Make.include
@@ -0,0 +1,27 @@
+# -*- makefile -*-
+
+src := $/dict.c \
+ $/dict.h \
+ $/except.c \
+ $/except.h \
+ $/hash.c \
+ $/hash.h \
+ $/list.c \
+ $/list.h \
+ $/sfx.c \
+ $/sfx.h
+
+tst := $/dict-main.c \
+ $/except-main.c \
+ $/hash-main.c \
+ $/list-main.c \
+ $/sfx-main.c
+
+$/.C_SRCS :=$(filter %.c,${src})
+$/.CXX_SRCS :=$(filter %.C,${src})
+$/.CXX_LIBS :=$/libkaz.a
+
+$/.CLEAN := $/*.o
+
+$/libkaz.a: ${$/.C_SRCS:.c=.o} ${$/.CXX_SRCS:.C=.o}
+
diff --git a/libutil/kazlib/blast.pl b/libutil/kazlib/blast.pl
new file mode 100755
index 0000000..63351c9
--- /dev/null
+++ b/libutil/kazlib/blast.pl
@@ -0,0 +1,33 @@
+#!/usr/bin/perl
+
+#
+# This is a program whose output can be piped to the test drivers for
+# hash.c and dict.c. It inserts a bunch of data and then deletes it all.
+#
+# The $modulus should be a prime number. This ensures that the $modulus - 1
+# generated keys are all distinct. The $factor_i and $factor_d values need not
+# be prime, but it should not be a multiple of $modulus (including zero),
+# otherwise a sequence of duplicate keys will be generated: choose numbers
+# in the range [1, $modulus - 1]. Choosing 1 means that
+# insertions (or deletions) will take place in order.
+# The purpose of using the prime modulus number is to generate a repeatable
+# sequence of unique keys that is (possibly) not in sorted order.
+#
+
+# $modulus = 200003;
+# $factor_i = 100;
+# $factor_d = 301;
+
+$modulus = 6113;
+$factor_i = 1669;
+$factor_d = 2036;
+
+for ($i = 1; $i < $modulus; $i++) {
+ printf("a %d %d\n", ($i * $factor_i) % $modulus, $i);
+}
+
+for ($i = 1; $i < $modulus; $i++) {
+ printf("d %d\n", ($i * $factor_d) % $modulus);
+}
+
+print "t\nq\n"
diff --git a/libutil/kazlib/dict.c b/libutil/kazlib/dict.c
new file mode 100644
index 0000000..cd98498
--- /dev/null
+++ b/libutil/kazlib/dict.c
@@ -0,0 +1,1238 @@
+/*
+ * Dictionary 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.
+ *
+ */
+
+#define NDEBUG
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <assert.h>
+#define DICT_IMPLEMENTATION
+#include "dict.h"
+
+// bpw 20050309 define this to use a qsort(3) compatible sort function,
+// requiring two dereferences to get the data instead of one.
+//
+#define BE_QSORT_COMPATIBLE
+
+/*
+ * These macros provide short convenient names for structure members,
+ * which are embellished with dict_ prefixes so that they are
+ * properly confined to the documented namespace. It's legal for a
+ * program which uses dict to define, for instance, a macro called ``parent''.
+ * Such a macro would interfere with the dnode_t struct definition.
+ * In general, highly portable and reusable C modules which expose their
+ * structures need to confine structure member names to well-defined spaces.
+ * The resulting identifiers aren't necessarily convenient to use, nor
+ * readable, in the implementation, however!
+ */
+
+#define left dict_left
+#define right dict_right
+#define parent dict_parent
+#define color dict_color
+#define key dict_key
+#define data dict_data
+
+#define nilnode dict_nilnode
+#define nodecount dict_nodecount
+#define maxcount dict_maxcount
+#define compare dict_compare
+#define allocnode dict_allocnode
+#define freenode dict_freenode
+#define context dict_context
+#define dupes dict_dupes
+
+#define dictptr dict_dictptr
+
+#define dict_root(D) ((D)->nilnode.left)
+#define dict_nil(D) (&(D)->nilnode)
+#define DICT_DEPTH_MAX 64
+
+static dnode_t *dnode_alloc(void *context);
+static void dnode_free(dnode_t *node, void *context);
+
+/*
+ * Perform a ``left rotation'' adjustment on the tree. The given node P and
+ * its right child C are rearranged so that the P instead becomes the left
+ * child of C. The left subtree of C is inherited as the new right subtree
+ * for P. The ordering of the keys within the tree is thus preserved.
+ */
+
+static void rotate_left(dnode_t *upper)
+{
+ dnode_t *lower, *lowleft, *upparent;
+
+ lower = upper->right;
+ upper->right = lowleft = lower->left;
+ lowleft->parent = upper;
+
+ lower->parent = upparent = upper->parent;
+
+ /* don't need to check for root node here because root->parent is
+ the sentinel nil node, and root->parent->left points back to root */
+
+ if (upper == upparent->left) {
+ upparent->left = lower;
+ } else {
+ assert (upper == upparent->right);
+ upparent->right = lower;
+ }
+
+ lower->left = upper;
+ upper->parent = lower;
+}
+
+/*
+ * This operation is the ``mirror'' image of rotate_left. It is
+ * the same procedure, but with left and right interchanged.
+ */
+
+static void rotate_right(dnode_t *upper)
+{
+ dnode_t *lower, *lowright, *upparent;
+
+ lower = upper->left;
+ upper->left = lowright = lower->right;
+ lowright->parent = upper;
+
+ lower->parent = upparent = upper->parent;
+
+ if (upper == upparent->right) {
+ upparent->right = lower;
+ } else {
+ assert (upper == upparent->left);
+ upparent->left = lower;
+ }
+
+ lower->right = upper;
+ upper->parent = lower;
+}
+
+/*
+ * Do a postorder traversal of the tree rooted at the specified
+ * node and free everything under it. Used by dict_free().
+ */
+
+static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
+{
+ if (node == nil)
+ return;
+ free_nodes(dict, node->left, nil);
+ free_nodes(dict, node->right, nil);
+ dict->freenode(node, dict->context);
+}
+
+/*
+ * This procedure performs a verification that the given subtree is a binary
+ * search tree. It performs an inorder traversal of the tree using the
+ * dict_next() successor function, verifying that the key of each node is
+ * strictly lower than that of its successor, if duplicates are not allowed,
+ * or lower or equal if duplicates are allowed. This function is used for
+ * debugging purposes.
+ */
+
+static int verify_bintree(dict_t *dict)
+{
+ dnode_t *first, *next;
+
+ first = dict_first(dict);
+
+ if (dict->dupes) {
+ while (first && (next = dict_next(dict, first))) {
+#ifdef BE_QSORT_COMPATIBLE
+ if (dict->compare(&first->key, &next->key) > 0)
+ return 0;
+#else
+ if (dict->compare(first->key, next->key) > 0)
+ return 0;
+#endif
+ first = next;
+ }
+ } else {
+ while (first && (next = dict_next(dict, first))) {
+#ifdef BE_QSORT_COMPATIBLE
+ if (dict->compare(&first->key, &next->key) >= 0)
+ return 0;
+#else
+ if (dict->compare(first->key, next->key) >= 0)
+ return 0;
+#endif
+ first = next;
+ }
+ }
+ return 1;
+}
+
+
+/*
+ * This function recursively verifies that the given binary subtree satisfies
+ * three of the red black properties. It checks that every red node has only
+ * black children. It makes sure that each node is either red or black. And it
+ * checks that every path has the same count of black nodes from root to leaf.
+ * It returns the blackheight of the given subtree; this allows blackheights to
+ * be computed recursively and compared for left and right siblings for
+ * mismatches. It does not check for every nil node being black, because there
+ * is only one sentinel nil node. The return value of this function is the
+ * black height of the subtree rooted at the node ``root'', or zero if the
+ * subtree is not red-black.
+ */
+
+static unsigned int verify_redblack(dnode_t *nil, dnode_t *root)
+{
+ unsigned height_left, height_right;
+
+ if (root != nil) {
+ height_left = verify_redblack(nil, root->left);
+ height_right = verify_redblack(nil, root->right);
+ if (height_left == 0 || height_right == 0)
+ return 0;
+ if (height_left != height_right)
+ return 0;
+ if (root->color == dnode_red) {
+ if (root->left->color != dnode_black)
+ return 0;
+ if (root->right->color != dnode_black)
+ return 0;
+ return height_left;
+ }
+ if (root->color != dnode_black)
+ return 0;
+ return height_left + 1;
+ }
+ return 1;
+}
+
+/*
+ * Compute the actual count of nodes by traversing the tree and
+ * return it. This could be compared against the stored count to
+ * detect a mismatch.
+ */
+
+static dictcount_t verify_node_count(dnode_t *nil, dnode_t *root)
+{
+ if (root == nil)
+ return 0;
+ else
+ return 1 + verify_node_count(nil, root->left)
+ + verify_node_count(nil, root->right);
+}
+
+/*
+ * Verify that the tree contains the given node. This is done by
+ * traversing all of the nodes and comparing their pointers to the
+ * given pointer. Returns 1 if the node is found, otherwise
+ * returns zero. It is intended for debugging purposes.
+ */
+
+static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
+{
+ if (root != nil) {
+ return root == node
+ || verify_dict_has_node(nil, root->left, node)
+ || verify_dict_has_node(nil, root->right, node);
+ }
+ return 0;
+}
+
+
+/*
+ * Dynamically allocate and initialize a dictionary object.
+ */
+
+dict_t *dict_create(dictcount_t maxcount, dict_comp_t comp)
+{
+ dict_t *new = malloc(sizeof *new);
+
+ if (new) {
+ new->compare = comp;
+ new->allocnode = dnode_alloc;
+ new->freenode = dnode_free;
+ new->context = NULL;
+ new->nodecount = 0;
+ new->maxcount = maxcount;
+ new->nilnode.left = &new->nilnode;
+ new->nilnode.right = &new->nilnode;
+ new->nilnode.parent = &new->nilnode;
+ new->nilnode.color = dnode_black;
+ new->dupes = 0;
+ }
+ return new;
+}
+
+/*
+ * Select a different set of node allocator routines.
+ */
+
+void dict_set_allocator(dict_t *dict, dnode_alloc_t al,
+ dnode_free_t fr, void *context)
+{
+ assert (dict_count(dict) == 0);
+ assert ((al == NULL && fr == NULL) || (al != NULL && fr != NULL));
+
+ dict->allocnode = al ? al : dnode_alloc;
+ dict->freenode = fr ? fr : dnode_free;
+ dict->context = context;
+}
+
+/*
+ * Free a dynamically allocated dictionary object. Removing the nodes
+ * from the tree before deleting it is required.
+ */
+
+void dict_destroy(dict_t *dict)
+{
+ assert (dict_isempty(dict));
+ free(dict);
+}
+
+/*
+ * Free all the nodes in the dictionary by using the dictionary's
+ * installed free routine. The dictionary is emptied.
+ */
+
+void dict_free_nodes(dict_t *dict)
+{
+ dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
+ free_nodes(dict, root, nil);
+ dict->nodecount = 0;
+ dict->nilnode.left = &dict->nilnode;
+ dict->nilnode.right = &dict->nilnode;
+}
+
+/*
+ * Obsolescent function, equivalent to dict_free_nodes
+ */
+
+void dict_free(dict_t *dict)
+{
+#ifdef KAZLIB_OBSOLESCENT_DEBUG
+ assert ("call to obsolescent function dict_free()" && 0);
+#endif
+ dict_free_nodes(dict);
+}
+
+/*
+ * Initialize a user-supplied dictionary object.
+ */
+
+dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp)
+{
+ dict->compare = comp;
+ dict->allocnode = dnode_alloc;
+ dict->freenode = dnode_free;
+ dict->context = NULL;
+ dict->nodecount = 0;
+ dict->maxcount = maxcount;
+ dict->nilnode.left = &dict->nilnode;
+ dict->nilnode.right = &dict->nilnode;
+ dict->nilnode.parent = &dict->nilnode;
+ dict->nilnode.color = dnode_black;
+ dict->dupes = 0;
+ return dict;
+}
+
+/*
+ * Initialize a dictionary in the likeness of another dictionary
+ */
+
+void dict_init_like(dict_t *dict, const dict_t *template)
+{
+ dict->compare = template->compare;
+ dict->allocnode = template->allocnode;
+ dict->freenode = template->freenode;
+ dict->context = template->context;
+ dict->nodecount = 0;
+ dict->maxcount = template->maxcount;
+ dict->nilnode.left = &dict->nilnode;
+ dict->nilnode.right = &dict->nilnode;
+ dict->nilnode.parent = &dict->nilnode;
+ dict->nilnode.color = dnode_black;
+ dict->dupes = template->dupes;
+
+ assert (dict_similar(dict, template));
+}
+
+/*
+ * Remove all nodes from the dictionary (without freeing them in any way).
+ */
+
+static void dict_clear(dict_t *dict)
+{
+ dict->nodecount = 0;
+ dict->nilnode.left = &dict->nilnode;
+ dict->nilnode.right = &dict->nilnode;
+ dict->nilnode.parent = &dict->nilnode;
+ assert (dict->nilnode.color == dnode_black);
+}
+
+
+/*
+ * Verify the integrity of the dictionary structure. This is provided for
+ * debugging purposes, and should be placed in assert statements. Just because
+ * this function succeeds doesn't mean that the tree is not corrupt. Certain
+ * corruptions in the tree may simply cause undefined behavior.
+ */
+
+int dict_verify(dict_t *dict)
+{
+ dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
+
+ /* check that the sentinel node and root node are black */
+ if (root->color != dnode_black)
+ return(0 * fprintf(stderr, "dict_verify()-- Root node not black!\n"));
+ if (nil->color != dnode_black)
+ return(0 * fprintf(stderr, "dict_verify()-- Nil node not black!\n"));
+ if (nil->right != nil)
+ return(0 * fprintf(stderr, "dict_verify()-- Nul->right not Nil!\n"));
+ /* nil->left is the root node; check that its parent pointer is nil */
+ if (nil->left->parent != nil)
+ return(0 * fprintf(stderr, "dict_verify()-- Nul->left->parent is not Nil!\n"));
+ /* perform a weak test that the tree is a binary search tree */
+ if (!verify_bintree(dict))
+ return(0 * fprintf(stderr, "dict_verify()-- Not a binary search tree!\n"));
+ /* verify that the tree is a red-black tree */
+ if (!verify_redblack(nil, root))
+ return(0 * fprintf(stderr, "dict_verify()-- Not a red-black tree!\n"));
+ if (verify_node_count(nil, root) != dict_count(dict))
+ return(0 * fprintf(stderr, "dict_verify()-- Node count is wrong!\n"));
+ return 1;
+}
+
+/*
+ * Determine whether two dictionaries are similar: have the same comparison and
+ * allocator functions, and same status as to whether duplicates are allowed.
+ */
+
+int dict_similar(const dict_t *left, const dict_t *right)
+{
+ if (left->compare != right->compare)
+ return 0;
+
+ if (left->allocnode != right->allocnode)
+ return 0;
+
+ if (left->freenode != right->freenode)
+ return 0;
+
+ if (left->context != right->context)
+ return 0;
+
+ if (left->dupes != right->dupes)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Locate a node in the dictionary having the given key.
+ * If the node is not found, a null a pointer is returned (rather than
+ * a pointer that dictionary's nil sentinel node), otherwise a pointer to the
+ * located node is returned.
+ */
+
+dnode_t *dict_lookup(dict_t *dict, const void *key)
+{
+ dnode_t *root = dict_root(dict);
+ dnode_t *nil = dict_nil(dict);
+ dnode_t *saved;
+ int result;
+
+ /* simple binary search adapted for trees that contain duplicate keys */
+
+ while (root != nil) {
+#ifdef BE_QSORT_COMPATIBLE
+ result = dict->compare(&key, &root->key);
+#else
+ result = dict->compare(key, root->key);
+#endif
+ if (result < 0)
+ root = root->left;
+ else if (result > 0)
+ root = root->right;
+ else {
+ if (!dict->dupes) { /* no duplicates, return match */
+ return root;
+ } else { /* could be dupes, find leftmost one */
+ do {
+ saved = root;
+ root = root->left;
+#ifdef BE_QSORT_COMPATIBLE
+ while (root != nil && dict->compare(&key, &root->key))
+ root = root->right;
+#else
+ while (root != nil && dict->compare(key, root->key))
+ root = root->right;
+#endif
+ } while (root != nil);
+ return saved;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Look for the node corresponding to the lowest key that is equal to or
+ * greater than the given key. If there is no such node, return null.
+ */
+
+dnode_t *dict_lower_bound(dict_t *dict, const void *key)
+{
+ dnode_t *root = dict_root(dict);
+ dnode_t *nil = dict_nil(dict);
+ dnode_t *tentative = 0;
+
+ while (root != nil) {
+#ifdef BE_QSORT_COMPATIBLE
+ int result = dict->compare(&key, &root->key);
+#else
+ int result = dict->compare(key, root->key);
+#endif
+
+ if (result > 0) {
+ root = root->right;
+ } else if (result < 0) {
+ tentative = root;
+ root = root->left;
+ } else {
+ if (!dict->dupes) {
+ return root;
+ } else {
+ tentative = root;
+ root = root->left;
+ }
+ }
+ }
+
+ return tentative;
+}
+
+/*
+ * Look for the node corresponding to the greatest key that is equal to or
+ * lower than the given key. If there is no such node, return null.
+ */
+
+dnode_t *dict_upper_bound(dict_t *dict, const void *key)
+{
+ dnode_t *root = dict_root(dict);
+ dnode_t *nil = dict_nil(dict);
+ dnode_t *tentative = 0;
+
+ while (root != nil) {
+#ifdef BE_QSORT_COMPATIBLE
+ int result = dict->compare(&key, &root->key);
+#else
+ int result = dict->compare(key, root->key);
+#endif
+
+ if (result < 0) {
+ root = root->left;
+ } else if (result > 0) {
+ tentative = root;
+ root = root->right;
+ } else {
+ if (!dict->dupes) {
+ return root;
+ } else {
+ tentative = root;
+ root = root->right;
+ }
+ }
+ }
+
+ return tentative;
+}
+
+/*
+ * Insert a node into the dictionary. The node should have been
+ * initialized with a data field. All other fields are ignored.
+ * The behavior is undefined if the user attempts to insert into
+ * a dictionary that is already full (for which the dict_isfull()
+ * function returns true).
+ */
+
+void dict_insert(dict_t *dict, dnode_t *node, const void *key)
+{
+ dnode_t *where = dict_root(dict), *nil = dict_nil(dict);
+ dnode_t *parent = nil, *uncle, *grandpa;
+ int result = -1;
+
+ node->key = key;
+
+ assert (!dict_isfull(dict));
+ assert (!dict_contains(dict, node));
+ assert (!dnode_is_in_a_dict(node));
+
+ /* basic binary tree insert */
+
+ while (where != nil) {
+ parent = where;
+#ifdef BE_QSORT_COMPATIBLE
+ result = dict->compare(&key, &where->key);
+#else
+ result = dict->compare(key, where->key);
+#endif
+ /* trap attempts at duplicate key insertion unless it's explicitly allowed */
+ assert (dict->dupes || result != 0);
+ if (result < 0)
+ where = where->left;
+ else
+ where = where->right;
+ }
+
+ assert (where == nil);
+
+ if (result < 0)
+ parent->left = node;
+ else
+ parent->right = node;
+
+ node->parent = parent;
+ node->left = nil;
+ node->right = nil;
+
+ dict->nodecount++;
+
+ /* red black adjustments */
+
+ node->color = dnode_red;
+
+ while (parent->color == dnode_red) {
+ grandpa = parent->parent;
+ if (parent == grandpa->left) {
+ uncle = grandpa->right;
+ if (uncle->color == dnode_red) { /* red parent, red uncle */
+ parent->color = dnode_black;
+ uncle->color = dnode_black;
+ grandpa->color = dnode_red;
+ node = grandpa;
+ parent = grandpa->parent;
+ } else { /* red parent, black uncle */
+ if (node == parent->right) {
+ rotate_left(parent);
+ parent = node;
+ assert (grandpa == parent->parent);
+ /* rotation between parent and child preserves grandpa */
+ }
+ parent->color = dnode_black;
+ grandpa->color = dnode_red;
+ rotate_right(grandpa);
+ break;
+ }
+ } else { /* symmetric cases: parent == parent->parent->right */
+ uncle = grandpa->left;
+ if (uncle->color == dnode_red) {
+ parent->color = dnode_black;
+ uncle->color = dnode_black;
+ grandpa->color = dnode_red;
+ node = grandpa;
+ parent = grandpa->parent;
+ } else {
+ if (node == parent->left) {
+ rotate_right(parent);
+ parent = node;
+ assert (grandpa == parent->parent);
+ }
+ parent->color = dnode_black;
+ grandpa->color = dnode_red;
+ rotate_left(grandpa);
+ break;
+ }
+ }
+ }
+
+ dict_root(dict)->color = dnode_black;
+
+ assert (dict_verify(dict));
+}
+
+/*
+ * Delete the given node from the dictionary. If the given node does not belong
+ * to the given dictionary, undefined behavior results. A pointer to the
+ * deleted node is returned.
+ */
+
+dnode_t *dict_delete(dict_t *dict, dnode_t *delete)
+{
+ dnode_t *nil = dict_nil(dict), *child, *delparent = delete->parent;
+
+ /* basic deletion */
+
+ assert (!dict_isempty(dict));
+ assert (dict_contains(dict, delete));
+
+ /*
+ * If the node being deleted has two children, then we replace it with its
+ * successor (i.e. the leftmost node in the right subtree.) By doing this,
+ * we avoid the traditional algorithm under which the successor's key and
+ * value *only* move to the deleted node and the successor is spliced out
+ * from the tree. We cannot use this approach because the user may hold
+ * pointers to the successor, or nodes may be inextricably tied to some
+ * other structures by way of embedding, etc. So we must splice out the
+ * node we are given, not some other node, and must not move contents from
+ * one node to another behind the user's back.
+ */
+
+ if (delete->left != nil && delete->right != nil) {
+ dnode_t *next = dict_next(dict, delete);
+ dnode_t *nextparent = next->parent;
+ dnode_color_t nextcolor = next->color;
+
+ assert (next != nil);
+ assert (next->parent != nil);
+ assert (next->left == nil);
+
+ /*
+ * First, splice out the successor from the tree completely, by
+ * moving up its right child into its place.
+ */
+
+ child = next->right;
+ child->parent = nextparent;
+
+ if (nextparent->left == next) {
+ nextparent->left = child;
+ } else {
+ assert (nextparent->right == next);
+ nextparent->right = child;
+ }
+
+ /*
+ * Now that the successor has been extricated from the tree, install it
+ * in place of the node that we want deleted.
+ */
+
+ next->parent = delparent;
+ next->left = delete->left;
+ next->right = delete->right;
+ next->left->parent = next;
+ next->right->parent = next;
+ next->color = delete->color;
+ delete->color = nextcolor;
+
+ if (delparent->left == delete) {
+ delparent->left = next;
+ } else {
+ assert (delparent->right == delete);
+ delparent->right = next;
+ }
+
+ } else {
+ assert (delete != nil);
+ assert (delete->left == nil || delete->right == nil);
+
+ child = (delete->left != nil) ? delete->left : delete->right;
+
+ child->parent = delparent = delete->parent;
+
+ if (delete == delparent->left) {
+ delparent->left = child;
+ } else {
+ assert (delete == delparent->right);
+ delparent->right = child;
+ }
+ }
+
+ delete->parent = NULL;
+ delete->right = NULL;
+ delete->left = NULL;
+
+ dict->nodecount--;
+
+ assert (verify_bintree(dict));
+
+ /* red-black adjustments */
+
+ if (delete->color == dnode_black) {
+ dnode_t *parent, *sister;
+
+ dict_root(dict)->color = dnode_red;
+
+ while (child->color == dnode_black) {
+ parent = child->parent;
+ if (child == parent->left) {
+ sister = parent->right;
+ assert (sister != nil);
+ if (sister->color == dnode_red) {
+ sister->color = dnode_black;
+ parent->color = dnode_red;
+ rotate_left(parent);
+ sister = parent->right;
+ assert (sister != nil);
+ }
+ if (sister->left->color == dnode_black
+ && sister->right->color == dnode_black) {
+ sister->color = dnode_red;
+ child = parent;
+ } else {
+ if (sister->right->color == dnode_black) {
+ assert (sister->left->color == dnode_red);
+ sister->left->color = dnode_black;
+ sister->color = dnode_red;
+ rotate_right(sister);
+ sister = parent->right;
+ assert (sister != nil);
+ }
+ sister->color = parent->color;
+ sister->right->color = dnode_black;
+ parent->color = dnode_black;
+ rotate_left(parent);
+ break;
+ }
+ } else { /* symmetric case: child == child->parent->right */
+ assert (child == parent->right);
+ sister = parent->left;
+ assert (sister != nil);
+ if (sister->color == dnode_red) {
+ sister->color = dnode_black;
+ parent->color = dnode_red;
+ rotate_right(parent);
+ sister = parent->left;
+ assert (sister != nil);
+ }
+ if (sister->right->color == dnode_black
+ && sister->left->color == dnode_black) {
+ sister->color = dnode_red;
+ child = parent;
+ } else {
+ if (sister->left->color == dnode_black) {
+ assert (sister->right->color == dnode_red);
+ sister->right->color = dnode_black;
+ sister->color = dnode_red;
+ rotate_left(sister);
+ sister = parent->left;
+ assert (sister != nil);
+ }
+ sister->color = parent->color;
+ sister->left->color = dnode_black;
+ parent->color = dnode_black;
+ rotate_right(parent);
+ break;
+ }
+ }
+ }
+
+ child->color = dnode_black;
+ dict_root(dict)->color = dnode_black;
+ }
+
+ assert (dict_verify(dict));
+
+ return delete;
+}
+
+/*
+ * Allocate a node using the dictionary's allocator routine, give it
+ * the data item.
+ */
+
+int dict_alloc_insert(dict_t *dict, const void *key, void *data)
+{
+ dnode_t *node = dict->allocnode(dict->context);
+
+ if (node) {
+ dnode_init(node, data);
+ dict_insert(dict, node, key);
+ return 1;
+ }
+ return 0;
+}
+
+void dict_delete_free(dict_t *dict, dnode_t *node)
+{
+ dict_delete(dict, node);
+ dict->freenode(node, dict->context);
+}
+
+/*
+ * Return the node with the lowest (leftmost) key. If the dictionary is empty
+ * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
+ */
+
+dnode_t *dict_first(dict_t *dict)
+{
+ dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left;
+
+ if (root != nil)
+ while ((left = root->left) != nil)
+ root = left;
+
+ return (root == nil) ? NULL : root;
+}
+
+/*
+ * Return the node with the highest (rightmost) key. If the dictionary is empty
+ * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
+ */
+
+dnode_t *dict_last(dict_t *dict)
+{
+ dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *right;
+
+ if (root != nil)
+ while ((right = root->right) != nil)
+ root = right;
+
+ return (root == nil) ? NULL : root;
+}
+
+/*
+ * Return the given node's successor node---the node which has the
+ * next key in the the left to right ordering. If the node has
+ * no successor, a null pointer is returned rather than a pointer to
+ * the nil node.
+ */
+
+dnode_t *dict_next(dict_t *dict, dnode_t *curr)
+{
+ dnode_t *nil = dict_nil(dict), *parent, *left;
+
+ if (curr->right != nil) {
+ curr = curr->right;
+ while ((left = curr->left) != nil)
+ curr = left;
+ return curr;
+ }
+
+ parent = curr->parent;
+
+ while (parent != nil && curr == parent->right) {
+ curr = parent;
+ parent = curr->parent;
+ }
+
+ return (parent == nil) ? NULL : parent;
+}
+
+/*
+ * Return the given node's predecessor, in the key order.
+ * The nil sentinel node is returned if there is no predecessor.
+ */
+
+dnode_t *dict_prev(dict_t *dict, dnode_t *curr)
+{
+ dnode_t *nil = dict_nil(dict), *parent, *right;
+
+ if (curr->left != nil) {
+ curr = curr->left;
+ while ((right = curr->right) != nil)
+ curr = right;
+ return curr;
+ }
+
+ parent = curr->parent;
+
+ while (parent != nil && curr == parent->left) {
+ curr = parent;
+ parent = curr->parent;
+ }
+
+ return (parent == nil) ? NULL : parent;
+}
+
+void dict_allow_dupes(dict_t *dict)
+{
+ dict->dupes = 1;
+}
+
+#undef dict_count
+#undef dict_isempty
+#undef dict_isfull
+#undef dnode_get
+#undef dnode_put
+#undef dnode_getkey
+
+dictcount_t dict_count(dict_t *dict)
+{
+ return dict->nodecount;
+}
+
+int dict_isempty(dict_t *dict)
+{
+ return dict->nodecount == 0;
+}
+
+int dict_isfull(dict_t *dict)
+{
+ return dict->nodecount == dict->maxcount;
+}
+
+int dict_contains(dict_t *dict, dnode_t *node)
+{
+ return verify_dict_has_node(dict_nil(dict), dict_root(dict), node);
+}
+
+static dnode_t *dnode_alloc(void *context)
+{
+ return malloc(sizeof *dnode_alloc(NULL));
+}
+
+static void dnode_free(dnode_t *node, void *context)
+{
+ free(node);
+}
+
+dnode_t *dnode_create(void *data)
+{
+ dnode_t *new = malloc(sizeof *new);
+ if (new) {
+ new->data = data;
+ new->parent = NULL;
+ new->left = NULL;
+ new->right = NULL;
+ }
+ return new;
+}
+
+dnode_t *dnode_init(dnode_t *dnode, void *data)
+{
+ dnode->data = data;
+ dnode->parent = NULL;
+ dnode->left = NULL;
+ dnode->right = NULL;
+ return dnode;
+}
+
+void dnode_destroy(dnode_t *dnode)
+{
+ assert (!dnode_is_in_a_dict(dnode));
+ free(dnode);
+}
+
+void *dnode_get(dnode_t *dnode)
+{
+ return dnode->data;
+}
+
+const void *dnode_getkey(dnode_t *dnode)
+{
+ return dnode->key;
+}
+
+void dnode_put(dnode_t *dnode, void *data)
+{
+ dnode->data = data;
+}
+
+int dnode_is_in_a_dict(dnode_t *dnode)
+{
+ return (dnode->parent && dnode->left && dnode->right);
+}
+
+void dict_process(dict_t *dict, void *context, dnode_process_t function)
+{
+ dnode_t *node = dict_first(dict), *next;
+
+ while (node != NULL) {
+ /* check for callback function deleting */
+ /* the next node from under us */
+ assert (dict_contains(dict, node));
+ next = dict_next(dict, node);
+ function(dict, node, context);
+ node = next;
+ }
+}
+
+static void load_begin_internal(dict_load_t *load, dict_t *dict)
+{
+ load->dictptr = dict;
+ load->nilnode.left = &load->nilnode;
+ load->nilnode.right = &load->nilnode;
+}
+
+void dict_load_begin(dict_load_t *load, dict_t *dict)
+{
+ assert (dict_isempty(dict));
+ load_begin_internal(load, dict);
+}
+
+void dict_load_next(dict_load_t *load, dnode_t *newnode, const void *key)
+{
+ dict_t *dict = load->dictptr;
+ dnode_t *nil = &load->nilnode;
+
+ assert (!dnode_is_in_a_dict(newnode));
+ assert (dict->nodecount < dict->maxcount);
+
+#ifndef NDEBUG
+ if (dict->nodecount > 0) {
+#ifdef BE_QSORT_COMPATIBLE
+ if (dict->dupes)
+ assert (dict->compare(&nil->left->key, &key) <= 0);
+ else
+ assert (dict->compare(&nil->left->key, &key) < 0);
+#else
+ if (dict->dupes)
+ assert (dict->compare(nil->left->key, key) <= 0);
+ else
+ assert (dict->compare(nil->left->key, key) < 0);
+#endif
+ }
+#endif
+
+ newnode->key = key;
+ nil->right->left = newnode;
+ nil->right = newnode;
+ newnode->left = nil;
+ dict->nodecount++;
+}
+
+void dict_load_end(dict_load_t *load)
+{
+ dict_t *dict = load->dictptr;
+ dnode_t *tree[DICT_DEPTH_MAX] = { 0 };
+ dnode_t *curr, *dictnil = dict_nil(dict), *loadnil = &load->nilnode, *next;
+ dnode_t *complete = 0;
+ dictcount_t fullcount = DICTCOUNT_T_MAX, nodecount = dict->nodecount;
+ dictcount_t botrowcount;
+ unsigned baselevel = 0, level = 0, i;
+
+ assert (dnode_red == 0 && dnode_black == 1);
+
+ while (fullcount >= nodecount && fullcount)
+ fullcount >>= 1;
+
+ botrowcount = nodecount - fullcount;
+
+ for (curr = loadnil->left; curr != loadnil; curr = next) {
+ next = curr->left;
+
+ if (complete == NULL && botrowcount-- == 0) {
+ assert (baselevel == 0);
+ assert (level == 0);
+ baselevel = level = 1;
+ complete = tree[0];
+
+ if (complete != 0) {
+ tree[0] = 0;
+ complete->right = dictnil;
+ while (tree[level] != 0) {
+ tree[level]->right = complete;
+ complete->parent = tree[level];
+ complete = tree[level];
+ tree[level++] = 0;
+ }
+ }
+ }
+
+ if (complete == NULL) {
+ curr->left = dictnil;
+ curr->right = dictnil;
+ curr->color = level % 2;
+ complete = curr;
+
+ assert (level == baselevel);
+ while (tree[level] != 0) {
+ tree[level]->right = complete;
+ complete->parent = tree[level];
+ complete = tree[level];
+ tree[level++] = 0;
+ }
+ } else {
+ curr->left = complete;
+ curr->color = (level + 1) % 2;
+ complete->parent = curr;
+ tree[level] = curr;
+ complete = 0;
+ level = baselevel;
+ }
+ }
+
+ if (complete == NULL)
+ complete = dictnil;
+
+ for (i = 0; i < DICT_DEPTH_MAX; i++) {
+ if (tree[i] != 0) {
+ tree[i]->right = complete;
+ complete->parent = tree[i];
+ complete = tree[i];
+ }
+ }
+
+ dictnil->color = dnode_black;
+ dictnil->right = dictnil;
+ complete->parent = dictnil;
+ complete->color = dnode_black;
+ dict_root(dict) = complete;
+
+ assert (dict_verify(dict));
+}
+
+void dict_merge(dict_t *dest, dict_t *source)
+{
+ dict_load_t load;
+ dnode_t *leftnode = dict_first(dest), *rightnode = dict_first(source);
+
+ assert (dict_similar(dest, source));
+
+ if (source == dest)
+ return;
+
+ dest->nodecount = 0;
+ load_begin_internal(&load, dest);
+
+ for (;;) {
+ if (leftnode != NULL && rightnode != NULL) {
+#ifdef BE_QSORT_COMPATIBLE
+ if (dest->compare(&leftnode->key, &rightnode->key) < 0)
+ goto copyleft;
+ else
+ goto copyright;
+#else
+ if (dest->compare(leftnode->key, rightnode->key) < 0)
+ goto copyleft;
+ else
+ goto copyright;
+#endif
+ } else if (leftnode != NULL) {
+ goto copyleft;
+ } else if (rightnode != NULL) {
+ goto copyright;
+ } else {
+ assert (leftnode == NULL && rightnode == NULL);
+ break;
+ }
+
+ copyleft:
+ {
+ dnode_t *next = dict_next(dest, leftnode);
+ #ifndef NDEBUG
+ leftnode->left = NULL; /* suppress assertion in dict_load_next */
+ #endif
+ dict_load_next(&load, leftnode, leftnode->key);
+ leftnode = next;
+ continue;
+ }
+
+ copyright:
+ {
+ dnode_t *next = dict_next(source, rightnode);
+#ifndef NDEBUG
+ rightnode->left = NULL;
+#endif
+ dict_load_next(&load, rightnode, rightnode->key);
+ rightnode = next;
+ continue;
+ }
+ }
+
+ dict_clear(source);
+ dict_load_end(&load);
+}
diff --git a/libutil/kazlib/dict.h b/libutil/kazlib/dict.h
new file mode 100644
index 0000000..2bab634
--- /dev/null
+++ b/libutil/kazlib/dict.h
@@ -0,0 +1,142 @@
+/*
+ * Dictionary 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.
+ *
+ */
+
+#ifndef DICT_H
+#define DICT_H
+
+#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 dictcount_t;
+#define DICTCOUNT_T_MAX ULONG_MAX
+
+/*
+ * The dictionary is implemented as a red-black tree
+ */
+
+typedef enum { dnode_red, dnode_black } dnode_color_t;
+
+typedef struct dnode_t {
+#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+ struct dnode_t *dict_left;
+ struct dnode_t *dict_right;
+ struct dnode_t *dict_parent;
+ dnode_color_t dict_color;
+ const void *dict_key;
+ void *dict_data;
+#else
+ int dict_dummy;
+#endif
+} dnode_t;
+
+typedef int (*dict_comp_t)(const void *, const void *);
+typedef dnode_t *(*dnode_alloc_t)(void *);
+typedef void (*dnode_free_t)(dnode_t *, void *);
+
+typedef struct dict_t {
+#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+ dnode_t dict_nilnode;
+ dictcount_t dict_nodecount;
+ dictcount_t dict_maxcount;
+ dict_comp_t dict_compare;
+ dnode_alloc_t dict_allocnode;
+ dnode_free_t dict_freenode;
+ void *dict_context;
+ int dict_dupes;
+#else
+ int dict_dummmy;
+#endif
+} dict_t;
+
+typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
+
+typedef struct dict_load_t {
+#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+ dict_t *dict_dictptr;
+ dnode_t dict_nilnode;
+#else
+ int dict_dummmy;
+#endif
+} dict_load_t;
+
+extern dict_t *dict_create(dictcount_t, dict_comp_t);
+extern void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *);
+extern void dict_destroy(dict_t *);
+extern void dict_free_nodes(dict_t *);
+extern void dict_free(dict_t *);
+extern dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t);
+extern void dict_init_like(dict_t *, const dict_t *);
+extern int dict_verify(dict_t *);
+extern int dict_similar(const dict_t *, const dict_t *);
+extern dnode_t *dict_lookup(dict_t *, const void *);
+extern dnode_t *dict_lower_bound(dict_t *, const void *);
+extern dnode_t *dict_upper_bound(dict_t *, const void *);
+extern void dict_insert(dict_t *, dnode_t *, const void *);
+extern dnode_t *dict_delete(dict_t *, dnode_t *);
+extern int dict_alloc_insert(dict_t *, const void *, void *);
+extern void dict_delete_free(dict_t *, dnode_t *);
+extern dnode_t *dict_first(dict_t *);
+extern dnode_t *dict_last(dict_t *);
+extern dnode_t *dict_next(dict_t *, dnode_t *);
+extern dnode_t *dict_prev(dict_t *, dnode_t *);
+extern dictcount_t dict_count(dict_t *);
+extern int dict_isempty(dict_t *);
+extern int dict_isfull(dict_t *);
+extern int dict_contains(dict_t *, dnode_t *);
+extern void dict_allow_dupes(dict_t *);
+extern int dnode_is_in_a_dict(dnode_t *);
+extern dnode_t *dnode_create(void *);
+extern dnode_t *dnode_init(dnode_t *, void *);
+extern void dnode_destroy(dnode_t *);
+extern void *dnode_get(dnode_t *);
+extern const void *dnode_getkey(dnode_t *);
+extern void dnode_put(dnode_t *, void *);
+extern void dict_process(dict_t *, void *, dnode_process_t);
+extern void dict_load_begin(dict_load_t *, dict_t *);
+extern void dict_load_next(dict_load_t *, dnode_t *, const void *);
+extern void dict_load_end(dict_load_t *);
+extern void dict_merge(dict_t *, dict_t *);
+
+#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+#ifdef KAZLIB_SIDEEFFECT_DEBUG
+#define dict_isfull(D) (SFX_CHECK(D)->dict_nodecount == (D)->dict_maxcount)
+#else
+#define dict_isfull(D) ((D)->dict_nodecount == (D)->dict_maxcount)
+#endif
+#define dict_count(D) ((D)->dict_nodecount)
+#define dict_isempty(D) ((D)->dict_nodecount == 0)
+#define dnode_get(N) ((N)->dict_data)
+#define dnode_getkey(N) ((N)->dict_key)
+#define dnode_put(N, X) ((N)->dict_data = (X))
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libutil/kazlib/docs/CHANGES b/libutil/kazlib/docs/CHANGES
new file mode 100644
index 0000000..3c949eb
--- /dev/null
+++ b/libutil/kazlib/docs/CHANGES
@@ -0,0 +1,290 @@
+New in 1.20
+
+ 1. Bugfix in except.h. Modified non-volatile auto variables were
+ being accessed after longjmp.
+
+New in 1.19
+
+ 1. Rewrite of broken dict_free.
+ 2. Fixed embarassing build breakages that accidentally went into 1.18
+ 3. Function hash_scan_delete_free renamed to hash_scan_delfree to be
+ distinct from hash_scan_delete in the first 14 characters.
+ 4. To resolve inconsistencies between hash_free and dict_free,
+ and a difference between the actual behavior of hash_free and
+ the documented behavior, these two functions are marked obsolescent.
+ The functions dict_free_nodes and hash_free_nodes are provided.
+ The obsolescent functions continue to work as before, for now.
+ 5. Documentation of hash_free is fixed to say that it also subjects
+ the hash to hash_destroy, which is what the implementation does.
+ 6. Documentation states what release it is for.
+
+New in 1.18
+
+ 1. Error in assert expression in list_merge fixed.
+ 2. Semantics of list_merge extended to allow list to be merged
+ onto itself (which is a noop).
+ 3. Clarified interface specification of list_transfer and list_extract;
+ the source and destination list may be the same object.
+ 4. New functions:
+ dict_init_like: create a dictionary similar to another one;
+ dict_similar: determine whether two dictionaries are similar;
+ dict_merge: merge contents of one dictionary to another.
+ 5. Dictionary test main can juggle multiple dictionaries, and test
+ dict_merge.
+ 6. If a hash node is inserted into some hash, it is a now a constraint
+ violation to insert it again into some hash.
+ 7. The hash_scan_delete_free function has been implemented; it is to
+ hash_scan_delete what hash_delete_free is to hash_delete.
+
+New in 1.17
+
+ Carl van Tast <vanTast at netway.at>:
+ 1. Removed references to ``safe malloc'' from some comments.
+ 2. Swapped ``allowed'' and ``not allowed'' in comment to
+ verify_bintree.
+ 3. Fixed comment to list_next: this function never returns the
+ sentinel.
+ 4. lnode_pool_init: nodes[i].prev = nodes instead of nodes + 1. This
+ saves one or two CPU cycles :-) and it gives a valid address even
+ if we have a (somewhat pathological) pool with just one element.
+
+ Kaz:
+ 5. Dropped extra parameter from tree rotation functions in dict.c. Should
+ shave a few cycles.
+ 6. Fixed error in the duplicate key iteration idiom example in the
+ documentation (see the section on dict_upper_bound).
+ 7. Forgotten #include <string.h> added to hash.c
+
+New in 1.16
+
+ 1. Added an interface for loading the contents of a dictionary from an
+ ordered sequence. This is done in O(n) time by a direct bottom-up
+ construction of the red-black tree, making it much faster than
+ the O(n log n) process of inserting each element.
+ 2. Miscellaneous cleanup: missing const qualifiers were added
+ to key pointer parameters, some incorrect comments fixed;
+ spelling errors corrected in documentation.
+
+New in 1.15
+
+ 1. Another potential exception handling memory leak fixed. This one
+ has to do with throwing an exception from within a try-catch region
+ in which an exception was just caught. The new exception replaces
+ the old without the old's dynamic memory being disposed of.
+ 2. Restrictions added on except_rethrow.
+ 3. Exception module must now be explicitly initialized with except_init.
+ 4. Structure members in exception header renamed to adhere to documented
+ namespace.
+ 5. The exwrap.[ch] source files are gone. There is support for memory
+ allocation with exception handling in except.c, which supports user
+ defined allocators.
+ 6. Three bugfixes to sfx parser. First, unary operators take a cast
+ expression, not a unary expression. Secondly, sizeof doesn't throw a syntax
+ error anymore on things that look like casts, but maybe are not.
+ Thirdly, empty parentheses weren't handled right in treatment of
+ ambiguous expressions, e.g. (a)() was declared a syntax error.
+ 7. Changed the representation of hash table chains. They are now
+ singly linked lists, which means that the overhead of managing
+ back pointers is gone. Only deletion is slightly more complicated
+ now because it has to search from the beginning of the chain.
+ [Rationale: this is okay, since chains are supposed to be short
+ in a hash table!]
+ 8. Rewritten test main() in list.c. It's now more like the others
+ with a menu. Previously it was essentially a file sorting program.
+ 9. New function: list_find. Exhaustively searches the list for a
+ matching entry, returns pointer to node if found.
+
+New in 1.14
+
+ 1. Got rid of some overbearing copyright restrictions. There is no need for
+ executables to contain copyright notices. In fact, there are no
+ restrictions on the use, or distribution in executable form.
+ 2. Tiny tweak in red-black fixup code of dict_insert.
+ 3. Keys in hash and dict are declared const void * now in all functions
+ rather than plain void *. This means that casts are no longer
+ necessary when calling insert or lookup functions with const
+ data as the key. But casts of the return value of hnode_getkey
+ or dnode_getkey may be required.
+ 4. Fixed compile breakage of except.c when posix thread support enabled.
+ 5. Side effect assertion interface now performs caching, to avoid
+ parsing the same expressions over and over again. Thus debugging with
+ KAZLIB_SIDEEFFECT_DEBUG incurs a smaller performance hit.
+ 6. Major bugfix to sfx expression parser. The function dealing with
+ disambiguating casts had to be rewritten to do more sophisticated
+ lookahead and backtracking. It all started with Mark Brady discovered
+ that (a++)+b was being incorrectly diagnosed as a syntax error.
+ 7. Added documentation. more examples for uses of dictionaries, and
+ exception handling. Some documentation about the internals
+ of exception handling added. Changed document format for narrower
+ margins, reducing page count and increasing readability.
+ 8. Bugfix in except_rethrow. It was freeing the dynamic data of the
+ exception even though it's not handled yet.
+
+New in 1.13
+
+ 1. Fixed some potential memory leaks in except.c.
+ 2. Finished all interface documentation. All that is left now
+ is to flesh out the implementation notes.
+ 3. Fixed a bug in POSIX threaded variant of except.c. Null
+ function pointer dereference in unhandled exception case.
+ 4. Macros beginning with E[A-Z] have been renamed to stay out
+ of space reserved for <errno.h>.
+ 5. Identifiers in exwrap.[ch] have been renamed from having
+ ex_ prefixed to having exwrap_ prefixes.
+
+New in 1.12
+
+ 1. COOL! New module for detecting side effects in C expressions.
+ 2. Serious bugfix in hash_init(). The computation of the initial hash
+ mask was completely botched up. Historically this code has seen little
+ testing because hashing over a user supplied table is not extendible.
+ Users of hash_create() are not affected.
+ 3. Tried to make computation of hash_val_t_bit more threadsafe. It should
+ be okay if writes to int objects are atomic, and concurrent writes of
+ the same int value to a given object are safe.
+ 4. Makefile renamed to Makefile.gcc. Makevile.vc added. The rename
+ is retroactive to all prior releases.
+ 5. OPAQUE_DEBUG becomes KAZLIB_OPAQUE_DEBUG and TEST_MAIN becomes
+ KAZLIB_TEST_MAIN. In general, macros that affect how the modules
+ build should be confined to a special namespace.
+ 6. New KAZLIB_SIDEEFFECT_DEBUG feature to enable diagnosis of side
+ effect expressions being passed to macros that evaluate their arguments
+ more than once.
+
+New in 1.11
+
+ 1. Improvements in experimental exception handling module:
+ except_throwf has been added which takes printf-like arguments;
+ except_checked_cleanup_pop has been added to provide a measure
+ of safety; there is now a way to pass arbitrary data from the throw site
+ to the catch.
+ 2. Improvements in dict_insert. A redundant call to the comparison function
+ has been eliminated, resulting in one fewer comparisons per insert
+ operation! Also a redundant test has been removed from the controlling
+ expression of the fixup loop, taking advantage of the fact that nil
+ is always black, and hence the root node always has a black parent.
+ 3. Small change in dict_delete. A test in the fixup loop has been eliminated
+ by temporarily coloring the root node red. See comment and diff between
+ dict.c revision 1.25 and 1.26.
+ 4. Test program blast.pl deletes keys out of order; to get in order
+ delete, initialize $factor_d to 1.
+
+New in 1.10
+
+ 1. The dict_init function now correctly initializes allocator-related
+ members of the dict structure.
+ 2. Tiny optimization in dict_lookup---less frequent cases tested last.
+ 3. Added list_extract, for extracting list slices (more general than
+ list_transfer).
+ 4. Incorporated changes from Loic Dachary: hash_free() has been
+ added for deleting all nodes; hash and compare functions
+ from the hash.c test code are now available to the user as
+ defaults if null pointers are given to hash_init() or
+ hash_create(); and hash_set_allocator restores the default
+ allocator routines if null pointers are given to it.
+ 5. Changes to dict analogous to hash: dict_free() added, etc.
+ 6. New exception handling module added (experimental).
+ 7. Much new documentation.
+
+New in 1.9
+
+ 1. Third argument of list_transfer may be null, in which case no nodes
+ are transferred. [Rationale: allows empty source list to be treated
+ without special case testing when all nodes are being transferred.]
+ 2. Two new functions added to dict: dict_upper_bound and dict_lower_bound.
+ These allow for inexact and range searches.
+
+New in 1.8
+
+ 1. New improved hashing function in the hash.c test code. It turns out that
+ when I changed the hash table algorithm, the blast.pl testcase was
+ hashing all to a single chain due to the pathologically bad hashing
+ function. The new hashing function should be good enough for general use.
+ It uses each nybble of the key to index a table of 16 random 32 bit integers.
+ These integers are XOR-ed into the hash value which is rotated after each
+ XOR.
+ 2. Spurious semicolon removed from the #define of HASH_VAL_T_BIT.
+ 3. I fixed some incorrect comments in hash.c which still talked about the
+ old algorithm from release 1.5 and older.
+ 4. The smalloc.c module is no longer supported. It's still in RCS but it's not
+ tagged as being part of release 1.8, and is not used by any of the other
+ sources. The standard library memory allocation functions are now used
+ directly. [Rationale: smalloc.c is overkill and interferes with
+ integration of the other source files into projects. Conscientious programmer
+ already ahve their own tools for debugging allocator corruption, anyway.]
+
+New in 1.7
+
+ 1. Missing #include <stdlib.h> added to smalloc.h
+ 2. The dict_delete() functions internals have been changed to make it much
+ more sane. This function no longer has the potential to return a node
+ other than the one that is passed to it.
+ 3. The changes to dict_delete() also fix a serious bug in dict_process().
+ The dict_process computes a pointer to a node's successor before
+ invoking the user callback to process a node. If the user callback calls
+ dict_delete() on the node, under the old dict_delete() semantics it was
+ possible for the successor to get deleted instead. Thus dict_process()
+ could end up with an invalid pointer.
+ 4. The changes to dict_delete() also mean that key and value information will
+ never be relocated from one node to another. User code can now rely on this
+ convenient assumption.
+
+New in 1.6
+
+ 1. The extendible hashing algorithm internals have changed. This
+ has a potential impact on the behavior with respect to hashing functions
+ which were written to work well specifically with the old hashing
+ scheme. For a silly reason, in the old hashing scheme, the top N bits
+ were always taken from the results of a hashing function, for a hash
+ table size of 2^N chains. In the new scheme, the bottom N bits are taken
+ instead. [Rationale: This is change makes it easier to write portable
+ hashing functions and simplifies the functions that expand or contract
+ the table, making them more efficient.]
+ 2. Added const qualifiers to the rcsid[] and right[] char arrays,
+ which shuts up the GCC compiler from complaining that these are
+ unused statics.
+
+New in 1.5
+
+ 1. First two arguments to list_prune_graft() are reversed. The leftmost
+ argument is now the destination list. Moreover, the function has been
+ renamed list_transfer(). [Rationale: this ordering of parameters is
+ consistent with list_merge(), and the standard C <string.h> functions
+ also pass destination pointers on the left. Renaming the function
+ protects against incorrect use.]
+
+ 2. Red-Black tree dictionaries now support duplicate keys. [Rationale:
+ duplicate keys could be useful in some applications.] When a dictionary
+ is created or initialized, it does not allow duplicate keys. The
+ function dict_allow_dupes() is used to set a flag in a dictionary to
+ henceforth allow duplicates. Once made, the decision to allow
+ duplicates cannot be reversed. [Rationale: toggling between allowing
+ and disallowing duplicates does not seem useful. Once duplicates are
+ admitted, there is no point in disallowing duplicates.] When a key is
+ sought in tree that currently allows duplicates, the leftmost node
+ containing that key is chosen from among the nodes that contain
+ duplicates of the key. Then dict_next() can be used to fetch the
+ remaining duplicates one by one. No particular order among the
+ duplicates may be assumed. However, for what it may be worth, the order
+ between any two duplicates is preserved for as long as they both remain
+ in the dictionary.
+
+ 3. The function prototypes in the header files have been modified to eliminate
+ parameter names. [Rationale: parameter names in prototypes have only
+ documentary value, and may clash with macro identifiers defined in other
+ headers.]
+
+ 4. Dictionary and hash table now has support for automatic allocation of
+ nodes in the insert and delete operations, which means that the user
+ can add items in one operation instead of the two operations of
+ allocating a node and inserting it. [Rationale: ease of use.] There is
+ support for user-defined allocators; the default allocators use the
+ smalloc.c routines. For any instance of a dict_t or hash_t object, the
+ user can override the allocator functions by supplying his or her
+ own pointers to suitable functions, and a context pointer that
+ will be passed to these functions when they are called through that
+ particular dict_t or hash_t instance. [Rationale: flexibility, ease of
+ use, promotes good design.] The funtion pointers can only be set when
+ the data structure is empty. [Rationale: it is undesirable to switch to
+ a different allocator when there are nodes in the dictionary; it might
+ lead to the error of freeing a node with an incorrect allocator.]
diff --git a/libutil/kazlib/docs/MUST_READ b/libutil/kazlib/docs/MUST_READ
new file mode 100644
index 0000000..20ca12e
--- /dev/null
+++ b/libutil/kazlib/docs/MUST_READ
@@ -0,0 +1,25 @@
+Greetings, Programmer!
+
+I gather that because you are reading this, you are probably considering using
+the C language translation units included here in your own software. If that
+is the case, I would like to know who you are and urge you to contact me.
+
+Here is why: I rove over this code periodically looking for defects. In fact,
+I use it in my own programming projects. If I discover a defect, I will
+notify everyone who I know is a user of this software. If there is a serious
+defect in some code that you are using in your software project, wouldn't you
+want to be informed? In fact, there is no question that you _need_ to be
+informed!
+
+Here is what you do: simply send an e-mail message to kaz at ashi.footprints.net
+with the subject "kazlib" and the body "I am a user". Be sure that your message
+has a good return address. I will manually add your e-mail address to a list
+which I will use only for the purpose of notifications regarding Kazlib. You
+will receive a reply to the effect that you are added.
+
+If ever you should wish to be removed from this list, simply ask and it shall
+be done.
+
+Yours in earnest,
+
+ Kaz Kylheku
diff --git a/libutil/kazlib/docs/README b/libutil/kazlib/docs/README
new file mode 100644
index 0000000..08f14a1
--- /dev/null
+++ b/libutil/kazlib/docs/README
@@ -0,0 +1,66 @@
+This collection of data structures is maintained by
+Kaz Kylheku <kaz at ashi.footprints.net>
+
+INSTRUCTIONS
+
+Simply add the necessary .c and .h files to your project. Include the
+appropriate .h file in any translation unit that interfaces with one or more of
+the kazlib modules. Then compile and link the modules together with your program.
+
+To use kazlib in a C++ project, don't compile them with a C++ compiler.
+Compile with a C compiler, and include the header files in
+your C++ translation units. Then link together the translated C and C++.
+As of release 1.2, the header files should work with C++.
+
+IMPORTANT NOTES
+
+1. Self checks
+
+The modules in this collection perform extensive self-checks, some of
+which make the performance really poor (by actually raising the overall
+asymptotic complexity of an operation, for example from O(log N) to O(N). The
+instrumentation assertions can be disabled by compiling with the NDEBUG macro
+defined.
+
+You can check that your project does not violate the principles of
+implementation hiding in connection with its use of the kazlib modules. This
+is accomplished by defining the macro KAZLIB_OPAQUE_DEBUG at the beginning of
+any translation unit which includes the kazlib header files. Note that
+whereas this will detect violations, it will not result in a translation
+that can be linked against the kazlib. When you are done checking, turn
+off KAZLIB_OPAQUE_DEBUG and recompile. If your compiler has a special ``check only''
+mode which enables it to perform syntax and type checking without doing
+an actual translation (similar to lint), it may be a time-saving idea to
+use it in conjunction with KAZLIB_OPAQUE_DEBUG.
+
+2. Macros with side effects
+
+Some of the kazlib header files define macros that evaluate their arguments
+more than once. This means that if expressions with side effects are passed
+to these macros, undesirable and undefined behavior will happen. There is
+support in Kazlib for catching these kinds of bugs: compile with
+KAZLIB_SIDEEFFECT_DEBUG, and add the except.c and sfx.c modules to your
+object. The macros will now parse their expressions at run time to diagnose
+the presence of side effects and function calls. It's easy to add this support
+to your own code!
+
+3. Thread support
+
+POSIX thread support is enabled by predefining KAZLIB_POSIX_THREADS. Currently
+only the exception-handling module has any need for this. When compiled that
+way, it provides thread-safe exception handling. Threads can independently
+throw exceptions and each thread can install its own specific catcher
+for unhandled exceptions. Moreover, each thread can register its own
+memory allocator functions.
+
+Note: this variant of the code also depends on the ability to cast between void
+* and function pointers, which is a common language extension.
+
+4. CVS identification
+
+The source files contain declarations of a static char array variable called
+rcsid. This contains an expansion of the CVS identification of each module,
+making it possible to determine the ``bill of materials'' that went into an
+executable build. I have now wrapped the declarations of these rcsid[] arrays
+so they are conditional on KAZLIB_RCSID being defined. For many users, these
+are just a waste of space.
diff --git a/libutil/kazlib/docs/docs.ist b/libutil/kazlib/docs/docs.ist
new file mode 100644
index 0000000..808c029
--- /dev/null
+++ b/libutil/kazlib/docs/docs.ist
@@ -0,0 +1,4 @@
+preamble
+"\\begin{theindex}\n\\addcontentsline{toc}{section}{Index}\n"
+postamble
+"\n\\end{theindex}\n"
diff --git a/libutil/kazlib/docs/docs.ltx b/libutil/kazlib/docs/docs.ltx
new file mode 100644
index 0000000..139f212
--- /dev/null
+++ b/libutil/kazlib/docs/docs.ltx
@@ -0,0 +1,4155 @@
+\documentclass{article}
+\usepackage{makeidx}
+\usepackage[margin=1.0in]{geometry}
+\makeatletter
+\newcommand{\defsubsection}{\@startsection
+ {subsection}
+ {2}
+ {0pt}
+ {2.0ex plus 0.1ex minus 0.05ex}
+ {-0pt}
+ {\normalfont\normalsize\bfseries}}
+\newcommand{\defsubsubsection}{\@startsection
+ {subsection}
+ {3}
+ {0ex}
+ {2.0ex plus 0.1ex minus 0.05ex}
+ {1.0ex}
+ {\normalfont\normalsize\bfseries}}
+\renewcommand{\paragraph}{\@startsection
+ {paragraph}
+ {4}
+ {0ex}
+ {2.0ex plus 0.1ex minus 0.05ex}
+ {1.0ex}
+ {\normalsize\bfseries}}
+\makeatother
+\title{Kazlib---Reusable Components\\for C Programming}
+\author{Kaz Kylheku}
+\date{Release 1.20\\July 24, 2001}
+\makeindex
+\setcounter{tocdepth}{1}
+\setcounter{secnumdepth}{4}
+\begin{document}
+\catcode`\_=11
+\def\indextype#1{\index{#1@{\tt #1} type}}
+\def\indexmacro#1{\index{#1@{\tt #1} macro}}
+\def\indexobject#1{\index{#1@{\tt #1} object}}
+\def\indexfunc#1{\index{#1@{\tt #1} function}}
+\def\indexenum#1{\index{#1@{\tt #1} enum constant}}
+\def\synopsis{\paragraph*{Synopsis}}
+\def\constraints{\paragraph*{Constraints}}
+\def\description{\paragraph*{Description}}
+\def\example{\paragraph*{Example}}
+\maketitle
+\abstract{The aim of the Kazlib project is to provide a well-documented
+programming interface featuring commonly needed programming abstractions,
+accompanied by a high quality, portable reference implementation.
+Kazlib consists of four independent components: a list module, a hash table
+module, a dictionary module and an exception handling module. The reference
+implementations of the first three of these are based on, respectively, the
+following algorithms: doubly linked circular list with sentinel node,
+extendible hashing, and red-black tree.}
+\tableofcontents
+\section{Introduction}
+This document establishes the provisions required of an implementation of the
+Kazlib library, and describes a reference implementation thereof.
+This document specifies
+\begin{itemize}
+\item the names and types of identifiers and preprocessor symbols made
+ available by each component;
+\item identifier name spaces reserved for future use by each component;
+\item the interface syntax and semantics of each component operation;
+\item the conditions required for the well-defined execution of each operation;
+\item the externally visible behavior of each component, including global
+ side effects and the effects on the subject data structures;
+
+\item and the implementation language of Kazlib.
+\end{itemize}
+Furthermore, this document describes, but does not specify
+\begin{itemize}
+\item the implementation details of structure objects manipulated by the
+ operations of each component;
+\item objects and functions that are defined by the implementation of
+ each component but are not externally visible;
+\item the algorithms and implementation details of the operations.
+\end{itemize}
+Finally, this document does {\em not\/} specify or describe
+\begin{itemize}
+\item the specific choices for parameters which may be adjusted by an
+ installation or implementation of Kazlib.
+\item the size of any data structure which will exceed the capacity of
+ a particular installation.
+\item the mechanisms or procedures for the translation of Kazlib and
+ their integration with other translation units.
+\end{itemize}
+
+\section{References}
+\label{sec:references}
+
+\begin{trivlist}
+\item ISO 9899:1990, {\it Programming Languages---C.}
+\item {\it Introduction to Algorithms}, Thomas H. Cormen, Charles E.
+Leiserson, Ronald L. Rivest, eighth printing, 1992.
+\end{trivlist}
+
+\section{Definitions and conventions}
+The following terms shall be interpreted in accordance with the definitions
+below. Other terms appearing in this document shall be defined upon their
+first mention, indicated by {\it italic\/} type. Any terms not explicitly
+defined in this document should be interpreted according to ISO 9899-1990,
+clause 3. Failing that, they should be interpreted according to other works
+listed in section \ref{sec:references}.
+\nobreak
+\defsubsection{implementation}: A library and set of C language headers
+which conforms to the specifications of this Document.
+\index{production mode}
+\indexmacro{NDEBUG}
+\defsubsection{production mode}: A mode of operating the implementation
+in such a way that maximum efficiency of execution is achieved at the expense
+of the verification of constraints. An implementation shall provide
+a production mode, which is enabled in an implementation-defined
+manner.\footnote{An implementation may have to supply a separate set of
+libraries for production and for verification use, for instance. The
+manner of selecting libraries varies with each programming environment.} Each
+translation unit of the program which includes a Kazlib header shall ensure that the macro {\tt
+NDEBUG} is defined prior to the inclusion of that header, otherwise the
+implementation is not said to be operated in production mode.
+\index{verification mode}
+\defsubsection{verification mode}: A mode of operating the implementation in
+such a way that maximum error checking is obtained at the cost of
+execution efficiency. An implementation shall provide a verification mode, which
+is enabled in an implementation-defined manner. If any translation unit which
+includes a Kazlib header defines the macro name {\tt NDEBUG}\footnote{The
+intent is that the standard {\tt assert} macro may be exploited
+by the implementation's headers for the purpose of provisioning verification
+mode.} prior to including that header, the implementation is not said to be in
+verification mode. The least requirements of a Kazlib implementation operated
+in verification mode, is that it shall stop translation or execution of any
+program which violates a constraint.
+\index{undefined behavior}
+\defsubsection{undefined behavior}: Behavior of a program, upon violation of a
+requirement with respect to the use of Kazlib, or upon use of corrupt or
+incorrect data, for which this document does not impose any requirements.
+Additional undefined behaviors are:
+\begin{itemize}
+\item any behavior that is undefined by the C language standard;
+\item evaluation of an object whose contents are indeterminate;
+\item a violation of any explicit constraint stated in
+this document, if that program was built using Kazlib in production
+mode;\footnote{The intent is that violations of constraints are diagnosed by
+the implementation in verification mode, and hence do not lead to undefined
+behavior.}
+\item a violation of any requirement stated in this document that
+is not designated as a constraint, and is introduced using the word
+{\it shall}; and
+\item any other construct for which no definition of behavior can be deduced
+from this document.
+\end{itemize}
+If a program invokes undefined behavior of any kind, the Kazlib implementation
+is absolved from any requirements as to what events should ensue. The
+implementation may respond by invoking undefined behavior in the C language
+sense, or it may detect the behavior and terminate with a diagnostic message.
+\defsubsection{implementation-defined}: An adjective which, when appearing
+in the description of a feature, represents a requirement that the
+implementor must supply a definition, and document that
+definition. This adjective is applied to both behavior and to results.
+Implementation-defined behavior is behavior which depends on the
+characteristics of an implementation.\footnote{It is not considered adequate
+for the implementor to allow implementation-defined behavior to produce
+unpredictable effects or to terminate the program when such behavior is
+invoked.} When said of a result,
+implementation-defined means that a value is successfully computed, but depends
+on the characteristics of the implementation. It is possible for the presence of a
+requirement on a program to be described as implementation-defined, giving the
+implementor a choice whether to make that requirement or not. If a program
+violates a requirement whose presence is implementation-defined, that program's
+behavior is undefined in any implementation which elects to in fact impose that
+requirement.
+\index{implementation-defined}
+\defsubsection{unpredictable result}: A successfully computed value which is
+unreliable because some procedure or data failed to satisfy a property required
+by the computation.
+\defsubsection{constraint}: A semantic restriction with which a program must
+comply. Some sections of this Document contain paragraphs under the heading
+{\it Constraints\/} which list all constraints pertaining to the described
+feature. When operated in production mode, the Kazlib implementation
+is not required to diagnose constraint violations. When operated in
+verification mode, the Kazlib implementation must halt translation or
+execution of a program which violates a constraint.
+\index{constraint}
+\defsubsection{comparison function}: A function which accepts two arguments
+\index{comparison function}
+of type \verb|const void *| and returns a value of type int based on
+a ranking comparison of these arguments, and which satisfies the following
+additional semantic properties. If the two arguments are deemed to be equal, the
+function must return zero. If the first argument is determined to have a
+greater rank than the second, a positive value is returned. Otherwise if the
+first argument is determined to have a lesser rank than the second, a negative
+value is returned. The rank is computed as if each value has associated with it
+an integer, not necessarily unique, and as if these integers are compared for ordinary equality or
+inequality when values are said to be compared. The assignment of integers is
+up to the designer of the comparison function, and does not change between
+successive invocations of the function.\footnote{Of course, an actual
+comparison function need not assign actual integer ranks to data items, but it
+must behave as if such ranks were assigned.}
+If a comparison function is invoked in the context of an operation on some data
+structure, it shall not invoke any operation on any component of that same
+structure.\footnote{Thus, if a comparison function is invoked from, for
+instance, {\tt list_sort}, it must not call any list operations that
+inspect or modify the list being sorted, or any of its constituent nodes.}
+\defsubsection{opaque data type}: A data type whose precise definition is
+not documented, and which is intended to be manipulated only using the
+documented interface, which consists of a set of functions. Many data types in
+Kazlib are described as opaque. A program which bypasses the documented
+interfaces in inspecting or manipulating these data types invokes undefined
+behavior, and is not portable among Kazlib implementations.
+\defsubsection{user}: \index{user} The program which uses Kazlib.
+\defsubsection{user data}: \index{user data} Data provided by the program
+to which Kazlib stores a pointer, but otherwise does not inspect or modify.
+
+\section{Environment}
+\label{sec:environment}
+
+The translation and use of Kazlib requires a conforming, hosted implementation
+of the C language which meets the following additional minimal requirements:
+\begin{enumerate}
+\item The C implementation distinguishes external names by at least their
+initial 15 characters\footnote{The ISO 9899:1990 standard demands only that
+external names be distinguished by their initial six characters.}. External
+names that are distinct in their first 15 characters are treated by the
+implementation as distinct names. Upper and lower case letters in external
+identifiers need not be treated as distinct.
+\item The C implementation does not claim the identifier \verb|__cplusplus|
+for its internal use as a preprocessor symbol or keyword.
+\end{enumerate}
+If Kazlib headers are used by a C++ program, the C++ implementation
+meets these additional requirements:
+\begin{enumerate}
+\item the C++ implementation identifies itself by predefining the preprocessor
+symbol \verb|__cplusplus|;
+\item the C++ implementation is be capable of linkage against
+the C implementation with which the Kazlib source files units were translated.
+\end{enumerate}
+The Kazlib headers shall not make use of any names that are claimed
+by the C++ programming language, and shall ensure that the \verb|extern "C"|
+mechanism is used for all declarations when they are included into a C++
+translation unit, or otherwise provide compatibility with C++.\footnote{The
+intent is that the Kazlib implementation could, in principle, provide
+a separate set of headers for use with each language.}
+
+In programming environments that support the programming mechanism of multiple
+threads of execution an implementation of Kazlib may be designated as {\it
+thread safe}. To be called thread safe, it must guarantee that the use of an
+object by one thread cannot visibly interact or interfere with the concurrent
+or interleaved use of another object by another thread. If a Kazlib
+implementation that is not thread safe is provided for an environment which
+supports threads, it shall be accompanied by documentation which describes
+the extent of this limitation.
+
+A Kazlib implementation can also be designated as being {\it async safe}.
+The minimum requirement for this designation is that an operation on an object
+can be interrupted by delivery of an asynchronous signal and from within the
+catching function for that signal, it is safe to perform an operation on
+another object. An implementation shall document that it is async safe,
+or the extent to which it fails to be async safe.
+
+\section{General restrictions}
+
+\subsection{Headers}
+
+The Kazlib headers may be included in any order, and may be included more than
+once. Prior to the inclusion of a Kazlib header, the translation unit shall not
+define any macro name that has the same spelling as a C language keyword. The
+Kazlib headers may behave as though they include arbitrary standard C headers,
+so any requirements related to the inclusion of standard headers apply to
+Kazlib headers. A header shall be included before the first reference to any
+of the functions, types or macros that it defines.
+
+If one or more preprocessor symbols whose names begin with the sequence
+\verb|KAZLIB_| are defined prior to the inclusion of a Kazlib header,
+the behavior is implementation-defined.
+
+\subsection{Reserved macros}
+
+A Kazlib header defines all of the macros explicitly listed in the section of
+this document that defines the contents of that header. It may also define
+additional macros that belong to the macro namespace reserved by that header.
+The translation unit that includes the header shall not \verb|#define| or
+\verb|#undef| any of these macros.
+
+A header may define function-like macros that supplement existing functions,
+provided that such macros do not cause multiple evaluation of arguments except
+as explicitly permitted, and are safe to use wherever the corresponding
+function call would be. These function-like macros may be subject to
+\verb|#undef|.\footnote{In principle, an implementation may provide, within the
+reserved namespaces, additional functions not specified in this document, and
+function-like macro equivalents of these functions. A program that uses such
+identifiers in a block or function scope should use {\tt \#undef} on these
+identifiers prior to their use.}
+
+\subsection{Reserved symbols}
+
+Each Kazlib header provides file scope declarations for the typedef names,
+struct tags, enum constants and function names listed in its corresponding
+section in this document. Moreover, each header may define additional such
+names that fall into the documented reserved namespaces.
+
+The behavior is undefined if a translation unit that includes a Kazlib header
+defines any identifier that is the same as an identifier reserved by the header
+in the same scope and namespace.\footnote{Therefore, it is permitted to redeclare
+or redefine the identifiers reserved by a previously included Kazlib header,
+provided that the declarations or definitions are in a different namespace or
+scope. Reserved names may be redeclared in a block scope, or used as
+statement labels which have function scope and are in their own namespace.}
+
+The behavior is also undefined if the program contains a definition of an
+object or function with external linkage whose name matches an external object
+of unction defined by Kazlib component that is used as part of the
+program, or whose name is in a namespace reserved by that
+component.\footnote{This restriction exists whether or not the corresponding Kazlib
+header is included.}
+
+Lastly, the behavior is undefined if a translation unit defines a macro whose
+name is in the space of reserved symbols of a Kazlib header that is included in
+that translation unit.
+
+\subsection{Argument aliasing}
+
+Kazlib provides functions that operate on objects of various types. Pointers
+to objects are passed to these functions, thereby giving rise to the
+possibility of {\it aliasing}---passing of objects that wholly or partially
+overlap. The program shall not present aliased objects to any Kazlib function.
+Objects of distinct types shall not be aliased in a function call under any
+circumstances.
+The aliasing of two or more objects of compatible type is permitted only as
+explicitly documented in the description of a function; in all such
+circumstances, only exact overlapping is permitted.\footnote{That is to say,
+where explicitly allowed, a pointer to the same object may be specified for two
+(or more) parameters of like type.}
+
+\subsection{Object initialization}
+
+The Kazlib opaque data types can only be initialized with the initialization
+functions provided by the Kazlib library, or by implementation-defined
+initialization functions.\footnote{Of course, the use of implementation-defined
+functions results in programs that are not portable among library
+implementations.} An opaque object that is initialized by a method other than
+by being passed to an appropriate initialization function, or that is not
+initialized at all, has indeterminate contents. A pointer to an object having
+indeterminate contents may be passed to an initialization function; the object
+then has well-determined contents.
+
+An object whose initialization function is capable of indicating failure is
+considered indeterminate if the attempt to initialize that object using that
+function does in fact fail. The program shall not attempt to deinitialize such
+an object. The implementation shall reclaim any resources that were allocated
+for an object whose initialization failed. This reclamation need
+not be immediate, but may be delayed; however, the delay shall not
+give rise to the possibility of resource leaks in any correct program.
+
+Those objects for which deinitialization operations are defined should be
+subject to these operations when these objects are no longer needed. Failure
+to apply the deinitialization functions may result in the leakage of resources.
+
+\subsection{Object copying}
+
+Certain data types may be sensitive to their own location in memory. This
+means that copying their values by assignment or \verb|memcpy| results in the
+copy having an indeterminate value which cannot be used. All opaque types in
+Kazlib are assumed to have this property; copying the value of an opaquely
+typed object to another suitably typed object causes the destination
+object to have indeterminate contents.
+
+\section{List component}
+
+The List component provides a set of functions, macros and type declarations
+which together provide a library for maintaining a possibly empty ordered set
+of elements, called a {\it list}. This list has the following properties:
+\index{List}\begin{enumerate}
+\item If the list is not empty, a first and last element can be identified.
+ In a list having only one element, that one element is both the first and
+ last element.
+\item Each element that is not the last element has another element as its
+ {\it successor}.
+ \index{successor!of a list element}
+ \index{List!successor of an element}
+\item Each element that is not the first element has a {\it
+ predecessor}.
+ \index{predecessor!of a list element}
+ \index{List!predecessor of an element}
+\item No element is the predecessor or successor of more than one element.
+\item If one element is the successor of another, the other is necessarily the
+ predecessor of the first.
+\item Each element is associated with arbitrary {\it satellite\/} data.
+\end{enumerate}
+The {\it size} of a list, also known as the {\it list count}, is simply the
+number of elements contained in it.\index{size!of a list}\index{List!count}
+
+A list imposes a maximum value on the number of nodes that may be in it
+simultaneously. This is known as the list's {\it capacity}. A list that
+has the maximum number of nodes is said to be full.
+
+\subsection{Interface}
+
+\subsubsection{The {\tt list.h} header}
+
+Each C or C++ translation unit that is to use the functionality of
+the List component shall include the header \verb|list.h|. This header
+shall contain declarations of types and external functions, and definitions of
+macros.
+The following typedef names shall be defined:\index{List!typedef names}
+\index{typedefs!defined by List}
+\begin{verbatim}
+ list_t listcount_t
+ lnode_t lnodepool_t
+\end{verbatim}
+In addition, the following structure tags may be defined:\index{List!tag names}
+\index{tags!defined by List}
+\begin{verbatim}
+ struct list_t
+ struct lnode_t
+ struct lnodepool_t
+\end{verbatim}
+The following external function names shall be declared:
+\index{List!function names}\index{functions!defined by List}
+\begin{verbatim}
+ list_append list_prev
+ list_contains list_process
+ list_count list_return_nodes
+ list_create list_sort
+ list_del_first list_find
+ list_del_last list_transfer
+ list_delete list_verify
+ list_destroy lnode_borrow
+ list_destroy_nodes lnode_create
+ list_extract lnode_destroy
+ list_first lnode_get
+ list_init lnode_init
+ list_ins_after lnode_is_in_a_list
+ list_ins_before lnode_pool_create
+ list_is_sorted lnode_pool_destroy
+ list_isempty lnode_pool_init
+ list_isfull lnode_pool_isempty
+ list_last lnode_pool_isfrom
+ list_merge lnode_put
+ list_next lnode_return
+ list_prepend
+\end{verbatim}
+The following preprocessor symbols (macros) shall be defined:
+\index{List!macro names}\index{macros!defined by List}
+\indexmacro{LISTCOUNT_T_MAX}
+\indexmacro{LIST_H}
+\begin{verbatim}
+ LISTCOUNT_T_MAX
+ LIST_H\end{verbatim}
+\index{symbols!reserved by List}\index{List!reserved symbols}
+Macro identifiers which begin with the upper-case prefix \verb|LIST| are
+reserved for future extensions to the \verb|list.h| header, as are
+names in the ordinary and tag namespaces which begin with
+\verb|list_| or \verb|lnode_|. External names which begin with \verb|list_| or
+\verb|lnode_| are reserved by the Kazlib library regardless of what header
+files are included.
+
+\subsubsection{The {\tt list_t} type}
+
+\indextype{list_t}
+The type \verb|list_t| is an opaque data type which maintains information about the
+current state of a single list. A list consists of an instance of the
+\verb|list_t| type, plus zero or more instances of the type \verb|lnode_t|. An
+instance of the \verb|list_t| type can be dynamically created using the
+\verb|list_create| function, and destroyed by the \verb|list_destroy| function.
+Alternately, the program can declare an object of type \verb|list_t| and have
+it initialized via the \verb|list_init| function.
+
+\subsubsection{The {\tt listcount_t} type}
+
+\indextype{listcount_t}
+\indexmacro{LISTCOUNT_T_MAX}
+The type \verb|listcount_t| is an unsigned integral type which represents
+the number of nodes in a list. The specific choice of unsigned integral type
+is implementation defined. The \verb|LISTCOUNT_T_MAX| macro expands to a
+constant expression of type \verb|listcount_t| which specifies the maximum
+value of that type.\footnote{For example, if the implementation defines
+{\tt listcount_t} as an alias for the type unsigned long, then
+{\tt LISTCOUNT_T_MAX} must have the same value as {\tt ULONG_MAX}.}
+
+\subsubsection{The {\tt lnode_t} type}
+
+\indextype{lnode_t}
+The type \verb|lnode_t| is an opaque type that represents a single node of a
+list. A node contains a a reference to satellite data provided by the user,
+and also stores the key that is associated with the node when it is inserted.
+Nodes may be dynamically created by the \verb|lnode_create| function.
+Alternately, the program may supply an \verb|lnode_t| object that can be
+initialized by the \verb|lnode_init| function.
+
+\subsubsection{The {\tt lnodepool_t} type}
+
+\indextype{lnodepool_t}
+The \verb|lnodepool_t| type provides an alternate method for supplying list
+nodes to the application. A user-supplied or dynamically allocated fixed size
+array of nodes is converted into a a {\it pool\/} of nodes from which free
+nodes may be obtained and to which they may be returned. A user-supplied node
+pool is created by the function \verb|lnode_pool_init| which requires a pointer
+to an object of type \verb|lnode_pool_t|, a pointer to the first element of an
+array of \verb|lnode_t| objects, as well as an integer representing the size of
+the array. Alternately, the function \verb|lnode_pool_create| will dynamically
+allocate an object of type \verb|lnode_pool_t| containing the specified number
+of list nodes.
+
+\subsubsection{The {\tt list_append} function}
+
+ \indexfunc{list_append}
+ \index{List!appending a node}
+ \index{append node to list}
+ \synopsis
+ \begin{verbatim}
+ void list_append(list_t *, lnode_t *);\end{verbatim}
+
+ \constraints
+ The second argument shall not refer to a node that is already in a list
+ or in a list node pool. The first argument shall not refer to a list
+ that is full.
+
+ \description
+ The append operation causes the node pointed at by the second
+ argument to become the last node in the list pointed at by the first
+ argument.\footnote{That is to say, after the operation, the
+ {\tt list_last} function, when applied to the list, shall return a pointer
+ to that node.}
+
+ If the first argument is an expression with side effects, the behavior
+ is undefined.\footnote{Thus, the implementation may provide a macro
+ version of {\tt list_append} which evaluates the first argument
+ more than once.}
+ \index{macros!and side effects}
+
+\subsubsection{The {\tt list_contains} function}
+
+ \indexfunc{list_contains}
+ \index{List!testing for presence of node}
+ \nobreak
+ \synopsis
+ \begin{verbatim}
+ int list_contains(list_t *, lnode_t *node);\end{verbatim}
+ \nobreak
+ \description
+ \nobreak
+ The \verb|list_contains| function shall return 1 if the node
+ pointed at by the second argument is in the list pointed at by the first
+ argument. Otherwise, it shall return 0.
+
+\subsubsection{The {\tt list_count} function}
+
+ \indexfunc{list_count}
+ \index{List!count}
+ \index{List!size}
+ \synopsis
+ \begin{verbatim}
+ listcount_t list_count(list_t *);\end{verbatim}
+
+ \description
+
+ The \verb|list_count| function returns a value which represents the number
+ of nodes currently stored in the list pointed at by the argument.
+
+\subsubsection{The {\tt list_create} function}
+
+ \indexfunc{list_create}
+ \index{List!creation of}
+ \index{create!list object}
+ \synopsis
+ \begin{verbatim}
+ list_t *list_create(listcount_t);\end{verbatim}
+
+ \description
+ The \verb|list_create| function instantiates and initializes an object of
+ type \verb|list_t|, and returns a pointer to it unless insufficient
+ resources exist for the creation of the object, in which case a null
+ pointer is returned.
+
+ The value of the function's argument establishes, for the entire duration
+ of the list object, its capacity.
+
+ The newly created list object is empty.
+
+\subsubsection{The {\tt list_del_first} function}
+
+ \index{List!first node}
+ \indexfunc{list_del_first}
+ \index{List!deletion}
+ \index{delete!first node of a list}
+ \synopsis
+ \begin{verbatim}
+ lnode_t *list_del_first(list_t *);\end{verbatim}
+
+ \constraints
+ The argument shall not point to an empty list.
+
+ \description
+ The \verb|list_del_first| function removes the first node from the
+ list pointed at by the argument and returns a pointer to that
+ node.
+
+ If the argument is an expression with side effects, the behavior is
+ undefined.\index{macros!and side effects}
+
+\subsubsection{The {\tt list_del_last} function}
+
+ \index{List!last node}
+ \indexfunc{list_del_last}
+ \index{List!deletion}
+ \index{delete!last node of a list}
+ \synopsis
+ \begin{verbatim}
+ lnode_t *list_del_last(list_t *);\end{verbatim}
+
+ \constraints
+ The argument shall not point to an empty list.
+
+ \description
+ The \verb|list_del_last| function removes the last node from the list
+ specified by the argument, and returns a pointer to that node. If,
+ prior to the operation, that node had a predecessor, that predecessor
+ shall become the new last node of the list. Otherwise, the list
+ shall become empty.
+
+ The new value of the list count shall be one less than its value
+ prior to the call to this function.
+
+ If the argument is an expression with side effects, the behavior is
+ undefined.\index{macros!and side effects}
+
+\subsubsection{The {\tt list_delete} function}
+
+ \indexfunc{list_delete}
+ \index{List!deletion}
+ \index{delete!arbitrary node of a list}
+ \synopsis
+ \begin{verbatim}
+ lnode_t *list_delete(list_t *, lnode_t *);\end{verbatim}
+
+ \constraints
+ The second argument shall point to a node that is inside the list
+ pointed at by the first argument.
+
+ \description
+ The \verb|list_delete| function removes the node pointed at by its
+ second argument from the list pointed at by its first argument.
+ A pointer to the deleted node is returned.
+
+\subsubsection{The {\tt list_destroy} function}
+
+ \indexfunc{list_destroy}
+ \index{List!destruction of}
+ \synopsis
+ \begin{verbatim}
+ void list_destroy(list_t *);\end{verbatim}
+
+ \constraints
+ The argument shall point to an empty list.
+
+ \description
+ The empty list pointed at by the argument is destroyed. If the list has
+ not been created by a call to the \verb|list_create| function, the
+ behavior is undefined.
+
+ A pointer that previously referred to a list that has been disposed by
+ \verb|list_destroy| has an indeterminate value.
+
+\subsubsection{The {\tt list_destroy_nodes} function}
+
+ \indexfunc{list_destroy_nodes}
+ \synopsis
+ \begin{verbatim}
+ void list_destroy_nodes(list_t *);\end{verbatim}
+
+ \description
+ The nodes, if any, contained in the list pointed at by the argument are
+ disposed of as if by a call to the \verb|lnode_destroy| function. If any
+ node contained in the list was created by means other than the
+ \verb|lnode_create| function, the behavior is undefined.
+
+ After the operation, the list is empty.
+
+ Any pointer that referred to any of the destroyed nodes takes on an
+ indeterminate value.
+
+\subsubsection{The {\tt list_extract} function}
+
+ \index{List!node range extraction}
+ \indexfunc{list_extract}
+ \synopsis
+ \begin{verbatim}
+ void list_extract(list_t *, list_t *, lnode_t *, lnode_t *);\end{verbatim}
+
+ \constraints
+ The second argument points to the {\it source list}. The third
+ argument is either null, or points to a node that is an occupant
+ of the source list. This node is called the {\it starting node}.
+ The fourth argument is either null, or points to a node that is
+ an occupant of the source list. This node is called the {\it ending
+ node}. If the starting node and ending node are both specified, and are
+ distinct nodes, then the starting node shall appear earlier in the source
+ list than the ending node.
+
+ The transfer request shall not call for the capacity of the destination
+ list to be exceeded.
+
+ \description
+ The \verb|list_extract| function moves nodes from the source
+ list to the {\it destination list\/} pointed at by the first
+ argument.\footnote{This right-to-left direction of transfer is consistent
+ with the semantics of standard C library functions such as {\tt memmove} or
+ {\tt strcpy}.}
+
+ If the third and fourth arguments are not null, the entire range of nodes
+ from the starting node and to the ending node, inclusive, is transferred
+ from the source list to the end of the destination list, where they appear
+ in their original order. Other nodes in the source list, if any, are
+ unaffected.
+
+ If the third and fourth arguments both point to the same node, that
+ node alone is transferred to the end of the destination list.
+
+ If either the third argument or the fourth argument is null, or both are null,
+ no transfer of nodes takes place.
+
+ The source and destination list may be the same object.
+
+\subsubsection{The {\tt list_first} function}
+
+ \index{List!first node}
+ \indexfunc{list_first}
+ \synopsis
+ \begin{verbatim}
+ lnode_t *list_first(list_t *);\end{verbatim}
+
+ \description
+ If the list pointed at by the argument is an empty list, a null pointer
+ is returned. Otherwise, a pointer to the first node in that list is
+ returned.
+
+ If the argument is an expression with side effects, the behavior is
+ undefined.\index{macros!and side effects}
+
+\subsubsection{The {\tt list_init} function}
+
+ \indexfunc{list_init}
+ \synopsis
+ \begin{verbatim}
+ list_t *list_init(list_t *, listcount_t);\end{verbatim}
+
+ \constraints
+ The second argument shall not have a zero value.
+
+ \description
+ The \verb|list_init| function initializes the list object pointed at by the
+ first argument, turning it into a valid, empty list. If the object is an
+ already initialized list, the behavior is undefined. A list returned by
+ \verb|list_create| is considered initialized. The second argument
+ specifies the maximum number of nodes that may simultaneously occupy the
+ list.
+
+ The value returned is that of the first argument.
+
+\subsubsection{The {\tt list_ins_after} function}
+
+ \indexfunc{list_ins_after}
+ \index{insert!node into list}
+ \index{List!insertion}
+ \synopsis
+ \begin{verbatim}
+ void list_ins_after(list_t *, lnode_t *, lnode_t *);\end{verbatim}
+
+ \constraints
+ The first argument shall point to a list that is not already full. The
+ second argument shall point to a node, called the {\it new node}, that is not
+ already an occupant of the list pointed at by the first argument, nor
+ of any other list or node pool object. The third
+ argument shall point to a node, called the {\it reference node}, that is an
+ occupant of the list.
+
+ \description
+ The new node becomes an occupant of the list, such that its predecessor
+ is the reference node. If the reference node has a successor, the
+ new node is inserted between the reference node and that successor.
+ Otherwise, the new node becomes the last node of the list.
+
+\subsubsection{The {\tt list_ins_before} function}
+
+ \indexfunc{list_ins_before}
+ \index{insert!node into list}
+ \index{List!insertion}
+ \synopsis
+ \begin{verbatim}
+ void list_ins_before(list_t *, lnode_t *, lnode_t *);\end{verbatim}
+
+ \constraints
+ The first argument shall point to a list that is not already full. The
+ second argument shall point to a node, called the {\it new node}, that is not
+ already an occupant of the list pointed at by the first argument, nor
+ of any other list or node pool object. The third
+ argument shall point to a node, called the {\it reference node}, that is an
+ occupant of the list.
+
+ \description
+ The new node becomes an occupant of the list, such that its successor
+ is the reference node. If the reference node has a predecessor, the
+ new node is inserted between the reference node and that predecessor.
+ Otherwise, the new node becomes the first node of the list.
+
+\subsubsection{The {\tt list_is_sorted} function}
+\label{list:is:sorted}
+ \indexfunc{list_is_sorted}
+
+ \synopsis
+ \begin{verbatim}
+ int list_is_sorted(list_t *,
+ int (const void *, const void *));\end{verbatim}
+
+ \description
+ The first argument points to a list object. The second is assumed to
+ point to a comparison function.
+
+ If the list has exactly one node or is empty, $1$ is returned
+ unconditionally. Otherwise, nodes of the list are examined to
+ determine whether they are in a sorted order according to the comparison
+ function. This is true if the integer ranks of their data items,
+ examined from the first node of the list through to the last node, form a
+ monotonically increasing sequence. If the nodes are in order, the value $1$
+ is returned. Otherwise $0$ is returned.
+
+ If the list has two or more nodes, and the second argument is a pointer to
+ a function that has the correct type, but does not satisfy the semantic
+ properties of a comparison function, the result is unpredictable, but is
+ guaranteed to be one of the values~$0$~or~$1$.
+
+\subsubsection{The {\tt list_isempty} function}
+
+ \indexfunc{list_isempty}
+ \synopsis
+ \begin{verbatim}
+ int list_isempty(list_t *);\end{verbatim}
+
+ \description
+ The \verb|list_isempty| function returns $1$ if the list pointed at by
+ the first argument is empty. Otherwise it returns $0$.
+
+\subsubsection{The {\tt list_isfull} function}
+
+ \indexfunc{list_isfull}
+ \synopsis
+ \begin{verbatim}
+ int list_isfull(list_t *);\end{verbatim}
+
+ \description
+ The \verb|list_isfull| function returns $1$ if the list pointed at by
+ the first argument is full. Otherwise it returns $0$.
+ A list is considered full when it contains the maximum number of nodes
+ that was specified upon its initialization.
+
+ If the argument is an expression with side effects, the behavior is
+ undefined.\index{macros!and side effects}
+
+\subsubsection{The {\tt list_last} function}
+
+ \index{List!last node}
+ \indexfunc{list_last}
+ \synopsis
+ \begin{verbatim}
+ lnode_t *list_last(list_t *);\end{verbatim}
+
+ \description
+ If the list pointed at by its first argument is empty, the \verb|list_last|
+ function returns a null pointer. Otherwise it returns a pointer to the
+ last node.
+
+ If the argument is an expression with side effects, the behavior is
+ undefined.\index{macros!and side effects}
+
+\subsubsection{The {\tt list_merge} function}
+
+ \index{List!merge operation}
+ \indexfunc{list_merge}
+ \synopsis
+ \begin{verbatim}
+ void list_merge(list_t *, list_t *,
+ int (const void *, const void *));\end{verbatim}
+
+ \constraints
+ The list pointed at by the first argument is called the {\it destination
+ list}. The second argument points to the {\it source list}. The third
+ argument points to a comparison function. The sum of the number of nodes
+ occupying the source list and the destination list shall not exceed the
+ maximum number of nodes that are permitted to occupy the destination list.
+ Furthermore, both the source and destination list shall be sorted such that
+ a call to \verb|list_is_sorted| given a pointer to either list as a first
+ argument, and the pointer to the comparison function as its second
+ argument, shall yield the value $1$.
+
+ \description
+ Nodes from the sorted source list are merged into the sorted destination
+ list. After the operation, the source list is empty and the destination
+ list contains all of the nodes it contained prior to the operation, as well
+ as all of the nodes that the source list contained. The nodes are in sorted
+ order according to the comparison function.
+
+ If the third argument is a pointer to a function that has the correct type,
+ but does not fulfill the semantic properties of a comparison function, the
+ order of the nodes in the destination list is unpredictable.
+
+ If the source and destination list are the same object, the
+ \verb|list_merge| operation has no effect.
+
+\subsubsection{The {\tt list_next} function}
+
+ \indexfunc{list_next}
+ \synopsis
+ \begin{verbatim}
+ lnode_t *list_next(list_t *, lnode_t *);\end{verbatim}
+
+ \constraints
+ The node pointed at by the second argument is an occupant of the list pointed
+ at by the first argument.
+
+ \description
+ If the node pointed at by the second argument has a successor, a pointer to
+ that successor is returned. Otherwise, a null pointer is returned.
+
+ If the second argument is an expression which has side effects, the behavior
+ is undefined.\index{macros!and side effects}
+
+\subsubsection{The {\tt list_prepend} function}
+
+ \indexfunc{list_prepend}
+ \index{List!prepending a node}
+ \index{prepend node to list}
+ \synopsis
+ \begin{verbatim}
+ void list_prepend(list_t *, lnode_t *);\end{verbatim}
+
+ \constraints
+ The second argument shall not refer to a node that is already in a list
+ or in a list node pool. The first argument shall not refer to a list
+ that is full.
+
+ \description
+ The prepend operation causes the node pointed at by the second
+ argument to become the first node in the list pointed at by the first
+ argument. After the operation, the \verb|list_first| function, when
+ applied to the list, shall return a pointer to that node.
+ If, prior to to the operation, the list is empty, then the prepended node
+ shall become the first node in that list, otherwise, the prepended node
+ becomes the predecessor of what was previously the first node.
+
+ If the first argument is an expression with side effects, the behavior
+ is undefined.\index{macros!and side effects}
+
+\subsubsection{The {\tt list_prev} function}
+
+ \indexfunc{list_prev}
+ \synopsis
+ \begin{verbatim}
+ lnode_t *list_prev(list_t *, lnode_t *);\end{verbatim}
+
+ \constraints
+ The node pointed at by the second argument is an occupant of the list pointed
+ at by the first argument.
+
+ \description
+ If the node pointed at by the second argument has a predecessor, a pointer to
+ that predecessor is returned. Otherwise, a null pointer is returned.
+
+ If the second argument is an expression which has side effects, the behavior
+ \index{macros!and side effects}
+ is undefined.
+
+\subsubsection{The {\tt list_process} function}
+
+ \indexfunc{list_process}
+ \synopsis
+ \begin{verbatim}
+ void list_process(list_t *, void *,
+ void (*)(list_t *, lnode_t *, void *));\end{verbatim}
+ \nobreak
+ \description
+ The \verb|list_process| function iterates over the nodes of a list,
+ and for each node invokes a callback function.\footnote{In most cases,
+ it is more convenient and preferable to
+ iterate over the list using explicit calls to {\tt list_first}
+ and {\tt list_next}.}
+ The second argument is a {\it context pointer\/} which can have any value.
+ The third argument of
+ \verb|list_process| shall be a pointer to a function which is compatible
+ with the specified type. If the list contains one or more nodes,
+ then the function is invoked once for each node, in order from first
+ to last. On each invocation, the first argument of the callback is a
+ pointer to the list; the second argument is a pointer to a node, called
+ the {\it subject node}; and the third argument repeats the context pointer
+ value that was originally passed to \verb|list_process|.
+
+ The callback function may delete the subject node by, for instance, calling
+ \verb|list_delete|. It may insert new nodes to any place in the list;
+ however, if such an insertion causes the subject node to acquire
+ a new successor, it is implementation-defined whether upon returning
+ from the callback function, the traversal shall continue with the
+ new successor, or with the original successor.
+
+ The callback function, and any function invoked from the callback
+ function, shall not destroy the list or make any modifications
+ other than the insertion of new nodes, or the deletion of the
+ subject node.
+
+ The callback function may recursively invoke \verb|list_process| for the
+ same list or for a different list; the callback invocations arising out of
+ the nested call inherit all of the restrictions of the outer callback in
+ addition to being subject to the usual restrictions.\footnote{This means,
+ for instance, that if two callbacks are in progress for different
+ subject nodes from the same list, the inner callback may not delete
+ its subject node, because it inherits the restriction that the only
+ permitted deletion is the outer callback's subject node.}
+
+ The callback function may freely operate on a different list,
+ subject to any inherited restrictions.
+
+\subsubsection{The {\tt list_return_nodes} function}
+
+ \indexfunc{list_return_nodes}
+ \synopsis
+ \begin{verbatim}
+ void list_return_nodes(list_t *, lnodepool_t *);\end{verbatim}
+
+ \description
+
+ Every node in the list specified by the first argument
+ is returned to the node pool specified by the second argument
+ If the list contains a node that has not been allocated
+ from that node pool, the behavior is undefined.
+
+\subsubsection{The {\tt list_sort} function}
+
+ \index{List!sort operation}
+ \indexfunc{list_sort}
+ \synopsis
+ \begin{verbatim}
+ void list_sort(list_t *, int (const void *, const void *));\end{verbatim}
+
+ \description
+
+ The \verb|list_sort| function changes the order of the nodes of the list
+ specified by the first argument according to the comparison function
+ pointed at by the second argument.
+
+ If the list is empty, or contains only one node, the comparison function is
+ not called.
+
+ Whenever the comparison function is invoked, its arguments are are the data
+ pointers stored in two distinct nodes of the list.
+
+\subsubsection{The {\tt list_find} function}
+
+ \index{List!find operation}
+ \indexfunc{list_find}
+ \synopsis
+ \begin{verbatim}
+ lnode_t *list_find(list_t *,
+ const void *, int (const void *, const void *));\end{verbatim}
+
+ \description
+
+ The \verb|list_find| function exhaustively searches the key for a node
+ whose satellite data matches a search key according to the comparison
+ function. The first argument is the list to be searched, the second
+ argument specifies the search key and the third argument is a pointer
+ to the comparison function.
+
+ The comparison function is invoked to compare the key against the
+ satellite data of successive nodes of the list, starting with the first
+ node. A pointer to the first node for which the comparison function returns
+ zero is returned.
+
+ If the list is empty, or the comparison function returns non-zero for
+ each item, a null pointer is returned.
+
+\subsubsection{The {\tt list_transfer} function}
+
+ \index{List!node transfer}
+ \indexfunc{list_transfer}
+ \synopsis
+ \begin{verbatim}
+ void list_transfer(list_t *, list_t *, lnode_t *);\end{verbatim}
+
+ \constraints
+ The third argument is either null, or it points at a node which is an
+ occupant of the list pointed at by the second argument.
+
+ The transfer request shall not call for the capacity of the destination
+ list to be exceeded.
+
+ \description
+ The \verb|list_transfer| function moves nodes from the list
+ pointed at by the second argument to the list pointed at by
+ the first argument.
+
+ If the third argument is not null, it specifies the node in the source list
+ at which the transfer begins. That node, its successor, and all
+ subsequent nodes, are transferred to the end of the destination list where
+ they appear in their original order. Other nodes in the source list are
+ unaffected.
+
+ If the third argument is null, no transfer of nodes takes place.
+
+ The source and destination list may be the same object.
+
+ If \verb|DL|, \verb|SL| and \verb|SN| are appropriately typed expressions,
+ the function call
+
+\begin{verbatim}
+ void list_transfer(DL, SL, SN);
+\end{verbatim}
+ is equivalent to
+\begin{verbatim}
+ list_extract(DL, SL, SN, list_last(SL));
+\end{verbatim}
+ except that \verb|SL| is evaluated only once.
+
+\subsubsection{The {\tt list_verify} function}
+
+ \indexfunc{list_verify}
+ \synopsis
+ \begin{verbatim}
+ int list_verify(list_t *list);\end{verbatim}
+
+ \description
+ The intent of the \verb|list_verify| function is to perform a verification
+ on the list object, regardless of whether the Kazlib implementation is
+ operated in verification or production mode. If the list objects
+ and its constituent nodes have been correctly manipulated, and the
+ program has not caused any undefined behaviors, the value $1$ is returned.
+ Otherwise, the function may be able to, but is not guaranteed to, detect
+ corruption, and return the value zero.
+
+\subsubsection{The {\tt lnode_borrow} function}
+
+ \indexfunc{lnode_borrow}
+ \synopsis
+ \begin{verbatim}
+ lnode_t *lnode_borrow(lnodepool_t *, void *);\end{verbatim}
+
+ \description
+
+ The \verb|lnode_borrow| function allocates a node from
+ the pool managed by the given \verb|lnodepool_t| object.
+ If the request succeeds, a pointer to the node is returned. If the object
+ has run out of nodes, the return value is a null pointer.
+
+\subsubsection{The {\tt lnode_create} function}
+
+ \indexfunc{lnode_create}
+ \synopsis
+ \begin{verbatim}
+ lnode_t *lnode_create(void *);\end{verbatim}
+
+ \description
+
+ The \verb|lnode_create| function dynamically allocates a list node,
+ stores in it the data value specified in the argument and
+ returns a pointer to it. The allocation is performed by a call to the
+ standard \verb|malloc| function. If the allocation fails, a null
+ pointer is returned.
+
+\subsubsection{The {\tt lnode_destroy} function}
+
+ \indexfunc{lnode_destroy}
+ \synopsis
+ \begin{verbatim}
+ void lnode_destroy(lnode_t *);\end{verbatim}
+
+ \description
+
+ The \verb|lnode_destroy| function destroys a list node that has been
+ allocated with the \verb|lnode_create| function. The value of any pointer
+ that referred to the node that was thus freed is indeterminate.
+
+ If the node is currently the occupant of a list, the behavior is undefined
+ if the list is subsequently used.
+
+\subsubsection{The {\tt lnode_get} function}
+
+ \indexfunc{lnode_get}
+ \synopsis
+ \begin{verbatim}
+ void *lnode_get(lnode_t *);\end{verbatim}
+
+ \description
+
+ The \verb|lnode_get| function retrieves the \verb|void *| data value
+ associated with a node.\footnote{This is the {\bf only} interface for
+ retrieving the data element.}
+
+\subsubsection{The {\tt lnode_init} function}
+
+ \indexfunc{lnode_init}
+ \synopsis
+ \begin{verbatim}
+ lnode_t *lnode_init(lnode_t *, void *);\end{verbatim}
+
+ The \verb|lnode_init| function initializes the contents
+ of the specified list node object, assigning it the
+ data value specified as the second argument.
+ The first argument is a pointer which refers to
+ a data object that has a suitable size and alignment
+ for the representation of an \verb|lnode_t| type.
+ After initialization with \verb|lnode_init|, the object is subsequently
+ eligible as an operand to the functions of the List component.
+
+\subsubsection{The {\tt lnode_is_in_a_list} function}
+
+ \indexfunc{lnode_is_in_a_list}
+ \synopsis
+ \begin{verbatim}
+ int lnode_is_in_a_list(lnode_t *);\end{verbatim}
+
+ \description
+
+ The \verb|lnode_is_in_a_list| function determines whether the given node is
+ an occupant of some list. If the node is in a list, the function returns
+ the value $1$. If the node is not in any list, the return value is zero.
+
+\subsubsection{The {\tt lnode_pool_create} function}
+
+ \indexfunc{lnode_pool_create}
+ \synopsis
+ \begin{verbatim}
+ lnodepool_t *lnode_pool_create(listcount_t);\end{verbatim}
+
+ \constraints
+
+ The value of the argument shall not be zero.
+
+ \description
+
+ The \verb|lnode_pool_create| function dynamically allocates,
+ by means of the standard library function \verb|malloc|
+ a node pool object containing the number of nodes specified
+ as the first argument. If not enough resources are available,
+ a null pointer is returned, otherwise a pointer to the
+ \verb|lnodepool_t| object is returned.
+
+\subsubsection{The {\tt lnode_pool_destroy} function}
+
+ \indexfunc{lnode_pool_destroy}
+ \synopsis
+ \begin{verbatim}
+ void lnode_pool_destroy(lnodepool_t *);\end{verbatim}
+
+ \description
+
+ The \verb|lnode_pool_destroy| function deallocates a
+ node pool that was allocated by \verb|lnode_pool_create|.
+ The value of any pointer which referred to the
+ node pool object becomes indeterminate.
+
+\subsubsection{The {\tt lnode_pool_init} function}
+
+ \indexfunc{lnode_pool_init}
+ \synopsis
+ \begin{verbatim}
+ lnodepool_t *lnode_pool_init(lnodepool_t *,
+ lnode_t *, listcount_t);\end{verbatim}
+
+ \constraints
+
+ The third argument, which specifies the node count, shall not be zero.
+
+ \description
+
+ The \verb|lnode_pool_init| function initializes a data object
+ that has a suitable size and alignment to represent an
+ \verb|lnodepool_t| type. A pointer to this object is passed
+ as the first argument. The node pool thus created draws nodes
+ from an array specified by the second argument, which shall be a pointer to
+ an object that can behave like an array of \verb|lnode_t| objects.
+ The third argument specifies the number of elements in this array.
+
+ After this function, the object pointed at by the \verb|lnodepool_t *|
+ argument is eligible for use with the node pool management functions
+ of the List component. Nodes may be drawn from the pool and returned to it.
+
+ As long as the pool continues to be used, the program should not directly
+ manipulate the node array. In particular, if the program modifies any
+ part of the array, then the behavior is undefined if the
+ \verb|lnodepool_t| object or any nodes drawn from it are subsequently
+ passed to a List function. The program shall not directly use the array
+ elements as independent \verb|lnode_t| objects while the array is
+ associated with the pool; in particular, it shall not pass these elements
+ to Kazlib functions that operate on \verb|lnode_t|.
+
+ The behavior is undefined if the same array is associated with more than
+ one node pool object, or if two node pool objects are given overlapping
+ arrays.
+
+ The node array is managed in an manner that is specific to the
+ implementation; the intent is that each element of the array represents a
+ distinct node object, a pointer to which can be returned in response to an
+ allocation request.
+
+ The \verb|lnode_pool_init| function returns a copy of the first argument.
+
+\subsubsection{The {\tt lnode_pool_isempty} function}
+
+ \indexfunc{lnode_pool_isempty}
+ \synopsis
+ \begin{verbatim}
+ int lnode_pool_isempty(lnodepool_t *);\end{verbatim}
+
+ \description
+
+ The \verb|lnode_pool_isempty| function tests the
+ specified \verb|lnodepool_t| object for ability to supply nodes.
+ If the object has been
+ subject to so many requests that it is no longer capable of
+ of supplying additional list nodes, the value $1$ is returned.
+ Otherwise the return value returned is zero.
+
+\subsubsection{The {\tt lnode_pool_isfrom} function}
+
+ \indexfunc{lnode_pool_isfrom}
+ \synopsis
+ \begin{verbatim}
+ int lnode_pool_isfrom(lnodepool_t *, lnode_t *);\end{verbatim}
+
+ \description
+
+ The function \verb|lnode_pool_isfrom|, intended to serve as a software
+ verification aid, determines whether a list node originates from
+ a particular node pool. The return value is $1$ if this relationship is
+ true, otherwise zero.
+
+\subsubsection{The {\tt lnode_put} function}
+
+ \indexfunc{lnode_put}
+ \synopsis
+ \begin{verbatim}
+ void lnode_put(lnode_t *, void *);\end{verbatim}
+
+ \description
+
+ The function \verb|lnode_put| replaces the data element
+ associated with the list node.
+
+\subsubsection{The {\tt lnode_return} function}
+
+ \indexfunc{lnode_return}
+ \synopsis
+ \begin{verbatim}
+ void lnode_return(lnodepool_t *, lnode_t *);\end{verbatim}
+
+ \constraints
+
+ The node pointed at by the second argument was derived by an allocation
+ request from the pool pointed at by the first argument.\footnote{In
+ other words, the {\tt lnode_pool_isfrom} function, were it called with
+ the same two arguments, would return $1$ if this constraint is met.}
+
+ Furthermore, the node must not be the occupant of a list.
+
+ \description
+
+ The \verb|lnode_return| function returns a node back to the node pool from
+ which it came. The node must not be subsequently used as an argument to any
+ List functions, until it happens to be allocated again. The pointer to
+ the node object remains valid, and may be returned by a subsequent
+ allocation request from the same node pool.
+
+\subsection{Implementation}
+\index{List!reference implementation}
+
+This section describes the elements of the reference implementation of the
+List component. No requirement is imposed that an implementation should
+follow the reference implementation. The same is true of the
+implementation notes for the other components.
+
+\subsubsection{Types}
+\index{implementation!List types}
+\index{typedefs!implementation of List}
+
+The reference List implementation is a doubly-linked circular list
+\index{sentinel node!of linked list}
+with a {\it sentinel node}. The node structure type is defined like this:
+\begin{verbatim}
+ typedef struct lnode_t {
+ struct lnode_t *list_next;
+ struct lnode_t *list_prev;
+ void *list_data;
+ } lnode_t;
+\end{verbatim}
+and the list structure is defined like this:
+\begin{verbatim}
+ typedef struct list_t {
+ lnode_t list_nilnode;
+ listcount_t list_nodecount;
+ listcount_t list_maxcount;
+ } list_t;
+\end{verbatim}
+The \verb|list_nilnode| member of the list object is the sentinel. It is
+always present in the list, never deleted. When the list is empty, the sentinel
+node's \verb|list_next| and \verb|list_prev| pointers simply point back at the sentinel
+node. The \verb|list_maxcount| member of the list tells how many nodes may be
+inserted and \verb|list_nodecount| keeps track of the actual count.
+
+The reason the sentinel node is called \verb|list_nilnode| is that it
+acts as the successor of a list's tail node, if there is one,
+and as the predecessor of the first node. In a linked list implementation
+that does not use a sentinel node, the \verb|list_next| pointer of
+the the tail node and the \verb|list_prev| pointer of the first node would
+be null.
+
+Note that prefixed names are used for all of the structure members. This is so
+that the header file conforms to the documented namespace. If, for example, the
+\verb|list_nilnode| member were simply called \verb|nilnode|, then
+if the program contained somewhere a macro called \verb|nilnode|, there would
+be a potential clash. If the program defined \verb|nilnode| prior to including
+the \verb|list.h| header, the declaration of \verb|struct list_t| would
+be confounded. If the program defined \verb|nilnode| after
+including \verb|list.h|, the definition would interfere with \verb|list.h|
+macros whose replacement text refers to the \verb|nilnode| member.
+
+For programming convenience, the list implementation source file defines short
+macro names for the structure members:
+\begin{verbatim}
+ #define next list_next
+ #define prev list_prev
+ #define data list_data
+\end{verbatim}
+... and so forth. These names are private to the translation unit, which
+includes only standard ANSI C headers. Some of the examples in this section
+make use of the short names; it is assumed that these macros are in effect.
+
+\subsubsection{Selected operations}
+\index{implementation!List operations}
+
+\paragraph{Retrieving the first node}
+\index{List!first node}
+
+Given a pointer \verb|P| to a \verb|list_t| type, the \verb|list_first|
+function examines the value of \verb|P->nilnode.next| which points
+at the head node if the list is not empty. If the list is empty,
+then this expression points back at the sentinel node. In
+other words, the comparison
+\begin{verbatim}
+ P->nilnode.next == &P->nilnode
+\end{verbatim}
+yields true when the list is empty. In this case, the interface requires that
+a null pointer be returned by \verb|list_first|. The implementation actually
+uses the above test, through a test for \verb|P->nodecount| being equal to
+zero is also possible.
+
+In general, any operation which produces a pointer to the nilnode that must be
+returned back to the calling program must test for that case and return a null
+pointer instead to satisfy the interface requirements.
+
+\paragraph{Node deletion}
+\index{List!deletion}
+
+Thanks to the use of the sentinel node, the list deletion operation doesn't
+have to test for special cases. A node in the middle of the list is
+deleted in exactly the same way as the first or the last node:
+\begin{verbatim}
+ 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;
+ }
+\end{verbatim}
+Quite simply, the successor and predecessor of the deleted node are connected
+together so that the deleted node is spliced out from the list. If the node is
+the last remaining one, then the sentinel node serves as both the successor and
+the predecessor. The effect of the deletion then is to set the sentinel's next
+and previous links to point to itself, as they did initially when the list was
+previously empty.
+
+The next and prev pointers are set to null not only for enhanced error checking
+in language implementations that trap dereferences of null pointers,
+but also to indicate that the node is not on any list. The interface
+function \verb|lnode_is_in_a_list| makes use of this.
+
+It's worth discussing in some detail why the values of expressions
+\verb|del->next| and \verb|del->prev| are cached in local variables. The
+actual statements that splice the node out of the list could instead have been
+written:
+\begin{verbatim}
+ del->prev->next = del->next;
+ del->next->prev = del->prev;
+\end{verbatim}
+However, this causes some compilers to generate less than optimal code because
+they fail to apply common subexpression elimination to the double
+occurrence of \verb|del->next|. Caching this expression in a local variable
+helps to get better code by making the semantics more obvious. In any case,
+modern compilers tend to do a good job of caching locals in high speed storage,
+particularly on architectures generously endowed with registers, so using a few
+extra locals is unlikely to lead to worse target code. The principle of using
+local variables to perform ``manual CSE'' is applied throughout the Kazlib
+reference implementation.
+
+\paragraph{Node insertion}
+Node insertion is also simple, thanks to the sentinel node which makes
+the doubly linked list circular. All insertions are done using
+the functions \verb|list_ins_before| and \verb|list_ins_after|.
+These are very similar, so it suffices to show \verb|list_ins_before|:
+\begin{verbatim}
+ 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);
+ }
+\end{verbatim}
+The node \verb|this| is the one before which the new node is being
+inserted. Internally, the pointer \verb|that| points to the
+node after which the insertion takes place. In other words, the function
+inserts the node \verb|new| in between \verb|this| and \verb|that|.
+
+Note the copious assertions which verify all of the documented constraints:
+that the node is not already on the list, or any other list, that the reference
+node \verb|this| is in the list, and that the list capacity won't be exceeded,
+and that the node count doesn't overflow its type.
+
+\index{List!insertion}
+
+\section{Hash component}
+
+The Hash component provides a means to manage collections of elements, called
+hashes, that are not ordered. Each element in the collection has a unique key,
+which is used for searching and inserting. The intent is that the
+implementation is based on extendible hashing, and the interface allows for
+user-defined hashing functions. The number of elements that can be stored
+in a hash is limited; maximum number of entries in a hash is known as its
+{\it capacity}.
+
+\subsection{Interface}
+
+\subsubsection{The {\tt hash.h} header}
+
+Each C or C++ translation unit that is to use the functionality of the Hash
+component shall include the header \verb|hash.h|. This header shall
+contain declarations of types and external functions, and definitions of
+macros. The following typedef names shall be
+defined:\index{Hash!typedef names}
+\index{typedefs!defined by Hash}
+\begin{verbatim}
+ hash_t hashcount_t
+ hnode_t hash_val_t
+ hash_comp_t hnode_alloc_t
+ hscan_t hnode_free_t
+ hash_fun_t
+\end{verbatim}
+In addition, the following structure tags may be defined:\index{Hash!tag names}
+\index{tags!defined by Hash}
+\begin{verbatim}
+ struct hash_t
+ struct hnode_t
+ struct hscan_t
+\end{verbatim}
+The following external function names shall be declared:
+\index{Hash!function names}\index{functions!defined by Hash}
+\begin{verbatim}
+ hash_create hash_count
+ hash_set_allocator hash_size
+ hash_destroy hash_isfull
+ hash_free_nodes hash_isempty
+ hash_init hash_scan_begin
+ hash_insert hash_scan_next
+ hash_lookup hash_scan_delete
+ hash_delete hash_scan_delfree
+ hash_alloc_insert hash_verify
+ hash_delete_free hnode_create
+ hnode_put hnode_init
+ hnode_get hnode_destroy
+ hnode_getkey hash_free
+\end{verbatim}
+\index{Hash!external objects}
+In addition, the external object name
+\begin{verbatim}
+ hash_val_t_bit
+\end{verbatim}
+shall be declared. The following preprocessor symbols (macros) shall be
+defined: \index{Hash!macro names}\index{macros!defined by Hash}
+\indexmacro{HASHCOUNT_T_MAX}
+\indexmacro{HASH_VAL_T_BIT}
+\indexmacro{HASH_VAL_T_MAX}
+\indexmacro{HASH_H}
+\begin{verbatim}
+ HASHCOUNT_T_MAX
+ HASH_VAL_T_BIT
+ HASH_H\end{verbatim}
+\index{symbols!reserved by Hash}\index{Hash!reserved symbols}
+Macro identifiers which begin with the upper-case prefix \verb|HASH| are
+reserved for future extensions to the \verb|hash.h| header, as are
+names in the ordinary and tag namespaces which begin with \verb|hash_|,
+\verb|hnode_| or \verb|hscan_|. External names which begin with \verb|hash_|,
+\verb|hnode_| or \verb|hscan_| are reserved by the Kazlib library regardless of
+what headers are included.
+
+\subsubsection{The {\tt hash_t} type}
+
+\indextype{hash_t}
+The type \verb|hash_t| is an opaque data type which maintains information about
+the current state of a single hash. From the programmer's viewpoint, a hash
+consists of an instance of the \verb|hash_t| type, plus zero or more instances
+of the type \verb|hnode_t|. An instance of the \verb|hash_t| type can be
+dynamically created using the \verb|hash_create| function, and destroyed by the
+\verb|hash_destroy| function. Alternately, the program can declare an object
+of type \verb|hash_t| and have it initialized via the \verb|hash_init|
+function. When initializing a hash this way, the user must also provide
+a fixed-size array of \verb|hnode_t *| objects which serves as the hash table.
+\footnote{A hash initialized this way does not support extendible hashing,
+because there is no mechanism for growing the user-supplied array.}
+
+\subsubsection{The {\tt hnode_t} type}
+
+\indextype{hnode_t}
+The \verb|hnode_t| type is an opaque type that represents a single element
+that can be inserted into a hash. A hash node contains a a reference to
+satellite data provided by the user. Nodes may be dynamically created by the
+\verb|hnode_create| function. Alternately, the program may supply an
+\verb|hnode_t| object that can be initialized by the \verb|hnode_init|
+function.
+
+\subsubsection{The {\tt hash_comp_t} type}
+
+\indextype{hash_comp_t}
+The \verb|hash_comp_t| type is a typedef name for the pointer-to-function type
+\begin{verbatim}
+ int (*)(const void *, const void *);
+\end{verbatim}
+In the context of the Hash component, this type denotes pointers to
+comparison functions.
+
+\subsubsection{The {\tt hscan_t} type}
+
+\indextype{hscan_t}
+The \verb|hscan_t| typedef stands for an opaque type which represents
+context information for traversing a hash. It is initialized by the
+\verb|hash_scan_begin| function, which specifies a hash to be
+traversed. Successive elements are retrieved using the \verb|hash_scan_next|
+function, which eventually indicates that no more elements
+remain. Inserting to, or deleting from a hash other than using
+the function \verb|hash_scan_delete| causes any \verb|hscan_t|
+objects that refer to it to become indeterminate.
+
+\subsubsection{The {\tt hashcount_t} type}
+
+\indextype{hashcount_t}
+\indexmacro{HASHCOUNT_T_MAX}
+This is an unsigned integral type which is capable of representing the number
+of nodes in a hash.
+The \verb|HASHCOUNT_T_MAX| macro expands to a
+constant expression of type \verb|hashcount_t| which specifies the maximum
+value of that type.
+
+\subsubsection{The {\tt hash_val_t} type}
+
+\indextype{hash_val_t}
+\indexmacro{HASH_VAL_T_MAX}
+The \verb|hash_val_t| type is an unsigned integral type capable of
+holding at least 32 bits. The purpose of this type is to represent the
+output values of hashing functions.
+The \verb|HASH_VAL_T_MAX| macro expands to a
+constant expression of type \verb|hash_val_t| which specifies the maximum
+value of that type.
+
+\subsubsection{The {\tt hnode_alloc_t} type}
+
+\index{Hash!allocator function}
+The \verb|hnode_alloc_t| identifier is a typedef name for the pointer-to-function
+type
+\begin{verbatim}
+ hnode_t *(*)(void *);
+\end{verbatim}
+In other words, a pointer to a function that takes a \verb|void *|
+argument and returns a pointer to \verb|hnode_t|.
+A function of this type which meets certain behavior criteria may be
+registered with a \verb|hash_t| object as node allocator, together
+with a compatible deallocator function. The \verb|void *| argument
+passes user-specified context information through to the
+allocator routines (see section \ref{section:hash_set_allocator}).
+
+\subsubsection{The {\tt hnode_free_t} type}
+
+\index{Hash!deallocator function}
+The \verb|hnode_free_t| identifier is a typedef name for the
+pointer-to-function type
+\begin{verbatim}
+ void (*)(hnode_t *, void *);
+\end{verbatim}
+A function of this type which meets certain behavior criteria may be
+registered with a \verb|hash_t| object as node deallocator
+together with a compatible allocator function.
+
+\subsubsection{The {\tt hash_fun_t} type}
+
+\index{hashing function}
+The \verb|hash_fun_t| identifier is a typedef name for the
+pointer-to-function type
+\begin{verbatim}
+ hash_val_t (*hash_fun_t)(const void *);
+\end{verbatim}
+A function of this type which behaves a certain way is called
+a {\it hashing function}. To be a viable hashing function, such
+a function must take a pointer to a key object, and produce
+an integer value that depends only on the contents of the key,
+and possibly on information that does not change over the lifetime of any hash
+for which that hashing function is used. Additional requirements for hashing
+functions are introduced later.
+
+\subsubsection{The {\tt hash_val_t_bit} object}
+
+ \indexobject{hash_val_t_bit}
+ \synopsis
+ \begin{verbatim}
+ extern int hash_val_t_bit;\end{verbatim}
+
+ \description
+
+ The \verb|hash_val_t_bit| object of type int has a fixed value
+ which counts the number of bits in the \verb|hash_val_t| object.
+ The program shall not store a value into this object.
+
+ The value of \verb|hash_val_t_bit| need not be correct until the
+ first successful call to \verb|hash_create| or to \verb|hash_init|
+ completes.
+
+ The implementation shall provide the macro \verb|HASH_VAL_T_BIT| which
+ expands to a non-lvalue expression that has the same value and type as the
+ object, but which may be a constant expression.\footnote{The intent of
+ providing these values is to ease the implementation of portable hashing
+ functions that take advantage of all of the available bits of a given
+ Kazlib implementation. Alternately, hashing functions may be constructed to
+ only use the lower 32 bits of the type.}
+
+\subsubsection{The {\tt hash_create} function}
+
+ \indexfunc{hash_create}
+ \index{Hash!creation of}
+ \index{create!hash object}
+ \synopsis
+ \begin{verbatim}
+ hash_t *hash_create(hashcount_t, hash_comp_t, hash_fun_t);\end{verbatim}
+
+ \description
+
+ If sufficient resources exist, the \verb|hash_create| function instantiates
+ and initializes an object of type \verb|hash_t| and returns a pointer to
+ it. Otherwise it returns a null pointer.
+
+ The first argument establishes the capacity of the hash, which is
+ initially empty.
+
+ The second argument is a pointer to a comparison function that will be
+ associated with the \verb|hash_t| object for its entire duration.
+
+ \index{hashing function}
+ The third argument is either null or a pointer to a hashing function
+ that is permanently associated with the object. If it is null, a {\it default
+ hashing function\/} is assigned by the implementation.
+
+ The hashing function shall be invoked with an argument that is one
+ of the keys that are being inserted into, or sought after, in the
+ hash. The hashing function must produce the same value each time it
+ is called for a given key. It is up to the hash user to define the
+ representation of keys, to manage their storage, and to provide a matching
+ hashing function. The hash stores only generic \verb|void *| pointers to
+ keys.
+
+ The default hashing function assumes that keys are null terminated
+ strings. That is to say, it behaves as though its \verb|void *|
+ argument points to the first elements of an array of \verb|unsigned|
+ \verb|char|, the last of which is a null character. The use of
+ the default hashing function with keys that do not have this representation
+ results in undefined behavior.
+
+\subsubsection{The {\tt hash_set_allocator} function}
+
+ \indexfunc{hash_set_allocator}
+ \label{section:hash_set_allocator}
+
+ \synopsis
+ \begin{verbatim}
+ void hash_set_allocator(hash_t *, hnode_alloc_t,
+ hnode_free_t, void *);\end{verbatim}
+
+ \constraints
+
+ The second and third arguments---the function pointers---shall either
+ both be null, or both be non-null. The hash pointed at by the first
+ argument shall be empty.
+
+ \description
+
+ When a hash is initialized, it is outfitted with a pair of default
+ node allocation functions. These functions may be replaced with functions
+ supplied by the program by calling the \verb|hash_set_allocator| function
+ and specifying two suitable pointers. If these pointers are null, the
+ default functions are restored.
+
+ These functions are called to allocate and free \verb|hnode_t|
+ objects by the functions \verb|hash_alloc_insert|
+ and \verb|hash_delete_free| (see sections
+ \ref{section:hash_delete_free} and \ref{section:hash_alloc_insert}).
+
+ If sufficient resources exist, the allocation function shall
+ return a pointer to a unique storage object that is large enough
+ and suitably aligned to represent an object of type \verb|dnode_t|.
+ Otherwise, the function shall return a null pointer.
+
+ The deallocation function shall be capable of disposing of the
+ objects created by the matching allocator function.
+
+
+\subsubsection{The {\tt hash_destroy} function}
+
+ \indexfunc{hash_destroy}
+ \synopsis
+ \begin{verbatim}
+ void hash_destroy(hash_t *);\end{verbatim}
+
+ \constraints
+
+ The hash pointed at by the first argument shall be empty.
+
+ \description
+
+ The \verb|hash_destroy| function deinitializes and deallocates a hash
+ that was created with \verb|hash_create|.
+ All pointers and \verb|hscan_t| objects that referred to the hash become
+ indeterminate.
+
+\subsubsection{The {\tt hash_free_nodes} function}
+
+ \indexfunc{hash_free_nodes}
+ \synopsis
+ \begin{verbatim}
+ void hash_free_nodes(hash_t *);\end{verbatim}
+
+ \description
+
+ The \verb|hash_free_nodes| function removes each node from
+ the hash and destroys it as if by calling \verb|hash_delete_free|
+ (Section \ref{section:hash_delete_free}). The order in which
+ the nodes are destroyed is unspecified.
+
+\subsubsection{The {\tt hash_free} function}
+
+ \indexfunc{hash_free}
+ \synopsis
+ \begin{verbatim}
+ void hash_free(hash_t *);\end{verbatim}
+
+ \description
+
+ Every node in the hash is removed from the hash and is then subject to the
+ deallocation function. The overall effect is as if the function
+ \verb|hash_delete_free| (Section \ref{section:hash_delete_free}) were
+ invoked on each node, and then \verb|hash_destroy| invoked on the
+ hash itself.
+
+ This function is obsolescent, and will be removed from some future revision
+ of this document.
+
+\subsubsection{The {\tt hash_init} function}
+
+ \indexfunc{hash_init}
+ \synopsis
+ \begin{verbatim}
+ hash_t *hash_init(hash_t *, hashcount_t, hash_comp_t,
+ hash_fun_t, hnode_t **, hashcount_t);
+ \end{verbatim}
+
+ \constraints
+
+ The last argument, which specifies the size of the program-supplied table,
+ shall be integral power of two that is greater than one---that is to say, an
+ integer of the form $2^k$ where $k$ is a positive integer.
+
+ \description
+
+ The \verb|hash_init| function configures the specified \verb|hash_t| object
+ to use a specified array of \verb|hnode_t *| pointer objects as a table.
+ The user is responsible for providing storage for the \verb|hash_t|
+ object and the array. As in the \verb|hash_create| interface,
+ the second parameter specifies the capacity, and the subsequent
+ arguments specify the comparison and hashing function, respectively.
+ The last two arguments specify the table of pointers. The array object
+ shall have at least as many elements as indicated by the last parameter,
+ otherwise the behavior is undefined. The call to \verb|hash_init| is said
+ to register the array with the hash.
+
+ The program shall not register the same array with more than one hash.
+ More specifically, once the program modifies a registered array, or
+ registers it with another hash, it must discontinue use of the first hash.
+ \footnote{Note that no explicit deinitialization function is provided to
+ dissociate the array. A program disposes of a hash created by
+ {\tt hash_init} by discontinuing its use.}
+
+\subsubsection{The {\tt hash_insert} function}
+
+ \indexfunc{hash_insert}
+ \label{section:hash_insert}
+ \synopsis
+ \begin{verbatim}
+ void hash_insert(hash_t *, hnode_t *, const void *);\end{verbatim}
+
+ \constraints
+ The hash is not full. The key specified by the \verb|void *| parameter
+ does not already exist in the specified hash. The node specified
+ by the second parameter is not already inserted into a hash.
+
+ \description
+ The \verb|hash_insert| function adds a new node to a hash. The user
+ must supply a node object that was initialized with \verb|hnode_init|
+ or dynamically created with \verb|hnode_create|. If the node is
+ already inserted into the same hash or any other hash, the behavior
+ is undefined.
+
+ A program may modify a key or node that has been inserted into a hash, or
+ cause the storage of the key or the node to become invalid. However, any
+ subsequent use of the hash invokes undefined behavior, with the following
+ exception: the data pointer stored within a node may be modified using the
+ \verb|hnode_put| function.
+
+ The \verb|hash_insert| function invokes the hashing function callback with
+ the key pointer as the argument.
+
+ The \verb|hash_insert| function may need to acquire additional storage in
+ order to support hash table growth. If the storage allocation fails, the
+ function shall fully recover, and insert the node without growing the
+ table.
+
+ The Hash implementation shall not modify the storage referenced by a key,
+ and shall not access it other than indirectly through the supplied hashing
+ and comparison functions.
+
+\subsubsection{The {\tt hash_lookup} function}
+
+ \indexfunc{hash_lookup}
+ \synopsis
+ \begin{verbatim}
+ hnode_t *hash_lookup(hash_t *, const void *);\end{verbatim}
+
+ \description
+
+ The \verb|hash_lookup| function searches the given hash for a node
+ matching the given key. Unless the hash is empty, the key shall be
+ compared against one or more keys that are already in the hash,
+ using the comparison function. The key pointer may
+ be identical to one that has already been inserted into the
+ hash.\footnote {In that case, the comparison function must correctly
+ cope with aliased parameters}.
+
+ If the key is found in the hash, a pointer to the corresponding node
+ is returned.\footnote{The corresponding node is the one that was specified
+ in the call to {\tt hash_insert} together with the matching key.}
+
+ If the key is not found, a null pointer is returned.
+
+\subsubsection{The {\tt hash_delete} function}
+
+ \indexfunc{hash_delete}
+ \synopsis
+ \begin{verbatim}
+ hnode_t *hash_delete(hash_t *, hnode_t *);\end{verbatim}
+
+ \constraints
+ The specified node is an occupant of the given hash.
+
+ \description
+ The \verb|hash_delete| function removes from the given hash a
+ node that has previously been inserted into it. The key under
+ which the node was inserted is also removed from the hash.\footnote{Thus
+ the program may arbitrarily manipulate the removed key without destroying
+ the integrity of the hash.}
+
+ Any existing \verb|hscan_t| iterator which is associated with the
+ hash becomes indeterminate.\footnote{To delete the current node during hash
+ table traversal, the {\tt hash_scan_delete} function must be used
+ instead.}
+
+
+\subsubsection{The {\tt hash_alloc_insert} function}
+
+ \label{section:hash_alloc_insert}
+ \indexfunc{hash_alloc_insert}
+
+ \synopsis
+ \begin{verbatim}
+ int hash_alloc_insert(hash_t *, const void *, void *);\end{verbatim}
+
+ \constraints
+
+ The second argument specifies the insertion key. The hash shall not
+ already contain this key.
+
+ \description
+
+ The \verb|hash_alloc_insert| function dynamically allocates and
+ initializes a \verb|hnode_t| object and inserts it into the
+ given hash. The second argument and third arguments are pointers
+ to user data and key objects, either of which may be null.
+
+ The allocation is performed by a call to the default allocation
+ function, or to the function that was configured using
+ \verb|hash_set_allocator| (Section \ref{section:hash_set_allocator}).
+
+ If the allocation succeeds, the insertion is performed and
+ the value 1 is returned. If the allocation fails, no insertion is
+ performed and 0 is returned.
+
+\subsubsection{The {\tt hash_delete_free} function}
+
+ \label{section:hash_delete_free}
+ \indexfunc{hash_delete_free}
+
+ \synopsis
+ \begin{verbatim}
+ void hash_delete_free(hash_t *, hnode_t *)
+ \end{verbatim}
+
+ \constraints
+ The given node can be found within the given hash.
+
+ \description
+ The \verb|hash_delete_free| function is the reverse of
+ \verb|hash_alloc_insert|. It removes the given node form the
+ hash as if by a call to \verb|hash_delete| and then deletes it using the
+ default or user-defined allocator (Section
+ \ref{section:hash_set_allocator}). If the given node had not been created
+ using \verb|hash_alloc_insert|, the behavior is undefined.
+
+\subsubsection{The {\tt hnode_put} function}
+
+ \indexfunc{hnode_put}
+ \synopsis
+ \begin{verbatim}
+ void hnode_put(hnode_t *, void *);\end{verbatim}
+
+ \description
+ The function \verb|hnode_put| replaces the data element
+ associated with the hash node.
+
+\subsubsection{The {\tt hnode_get} function}
+
+ \indexfunc{hnode_get}
+ \synopsis
+ \begin{verbatim}
+ void *hnode_get(hnode_t *);\end{verbatim}
+
+ \description
+ The \verb|hnode_get| function retrieves the \verb|void * | data value
+ associated with the given hash node.
+
+\subsubsection{The {\tt hnode_getkey} function}
+
+ \indexfunc{hnode_getkey}
+ \synopsis
+ \begin{verbatim}
+ const void *hnode_getkey(hnode_t *);\end{verbatim}
+
+ \description
+
+ The \verb|hnode_getkey| function retrieves the \verb|void *| key value
+ associated with the given node. A node acquires an associated key
+ when it is inserted into a hash (see section \ref{section:hash_insert}).
+ Invoking \verb|hnode_getkey| on a node that has not been inserted
+ into a hash results in undefined behavior.
+
+\subsubsection{The {\tt hash_count} function}
+
+ \indexfunc{hash_count}
+ \synopsis
+ \begin{verbatim}
+ hashcount_t hash_count(hash_t *);\end{verbatim}
+
+ \description
+ The \verb|hash_count| function returns a value which represents the number
+ of nodes currently stored in the hash pointed at by the argument.
+
+\subsubsection{The {\tt hash_size} function}
+
+ \indexfunc{hash_size}
+ \synopsis
+ \begin{verbatim}
+ hashcount_t hash_size(hash_t *hash)\end{verbatim}
+
+ \description
+ The \verb|hash_size| function returns an implementation-defined value that
+ depends on the number of entries in the given hash. The intent is that the
+ value represent the size of the internal hash table managed by the given
+ hash.
+
+\subsubsection{The {\tt hash_isfull} function}
+
+ \indexfunc{hash_isfull}
+ \synopsis
+ \begin{verbatim}
+ int hash_isfull(hash_t *);\end{verbatim}
+
+ \description
+ The \verb|hash_isfull| function returns 1 if the hash is full,
+ otherwise it returns 0.
+
+ If the argument is an expression with side effects, the behavior is
+ undefined.\index{macros!and side effects}
+
+
+\subsubsection{The {\tt hash_isempty} function}
+
+ \indexfunc{hash_isempty}
+ \synopsis
+ \begin{verbatim}
+ int hash_isempty(hash_t *);\end{verbatim}
+
+ \description
+ The \verb|hash_isempty| function returns 1 if the given hash is empty,
+ otherwise it returns 0.
+
+\subsubsection{The {\tt hash_scan_begin} function}
+
+ \indexfunc{hash_scan_begin}
+ \synopsis
+ \begin{verbatim}
+ void hash_scan_begin(hscan_t *, hash_t *);\end{verbatim}
+
+ \description
+ The \verb|hash_scan_begin| initializes the \verb|hscan_t| iterator object,
+ preparing it for a traversal of the given hash.
+
+ After this initialization, if the hash is modified in any way by
+ the performance of an insertion or deletion operation, the
+ value of the \verb|hscan_t| object becomes indeterminate,
+ with one exception: the \verb|hash_scan_delete| function or the
+ \verb|hash_scan_delfree| function may be used to delete the current
+ node.
+
+\subsubsection{The {\tt hash_scan_next} function}
+
+ \indexfunc{hash_scan_next}
+ \synopsis
+ \begin{verbatim}
+ hnode_t *hash_scan_next(hscan_t *);\end{verbatim}
+
+ \description
+ If any unvisited nodes remain, the \verb|hash_scan_next| function advances
+ to the next one and returns a pointer to it. Otherwise, it returns a null
+ pointer. Repeated invocations of \verb|hash_scan_next| return a pointer to
+ every node that has been inserted into the table, in no particular order,
+ such that no node is reported twice.
+
+\subsubsection{The {\tt hash_scan_delete} function}
+
+ \indexfunc{hash_scan_delete}
+ \synopsis
+ \begin{verbatim}
+ hnode_t *hash_scan_delete(hash_t *, hnode_t *);
+ \end{verbatim}
+
+ \constraints
+ The specified node is an occupant of the given hash.
+
+ \description
+ This function is almost exactly like \verb|hash_delete| except that it may
+ be used to delete a node that has been most recently obtained from
+ \verb|hash_scan_next| without destroying the validity of the \verb|hscan_t|
+ iterator from which the node was obtained.
+
+\subsubsection{The {\tt hash_scan_delfree} function}
+
+ \label{section:hash_scan_delfree}
+ \indexfunc{hash_scan_delfree}
+
+ \synopsis
+ \begin{verbatim}
+ void hash_scan_delfree(hash_t *, hnode_t *)
+ \end{verbatim}
+
+ \constraints
+ The given node can be found within the given hash.
+
+ \description
+ The \verb|hash_scan_delfree| function is similar to
+ \verb|hash_delete_free|. It removes the given node form the
+ hash and then deletes it using the default or user-defined allocator
+ (Section \ref{section:hash_set_allocator}). If the given node
+ had not been created using \verb|hash_alloc_insert|, the behavior
+ is undefined.
+
+ The deletion from the hash is performed as if by a call to
+ \verb|hash_scan_delete|, thus it is safe to delete a node that
+ was most recently obtained from a \verb|hash_scan_next| without
+ destroying the validity of the \verb|hscan_t| iterator.
+
+\subsubsection{The {\tt hash_verify} function}
+
+ \indexfunc{hash_verify}
+ \synopsis
+ \begin{verbatim}
+ int hash_verify(hash_t *hash);\end{verbatim}
+
+ \description
+ The intent of the \verb|hash_verify| function is to perform a verification
+ on the hash object, regardless of whether the Kazlib implementation is
+ operated in verification or production mode. If the hash object
+ and its constituent nodes have been correctly manipulated, and the
+ program has not caused any undefined behaviors, the value $1$ is returned.
+ Otherwise, the function may be able to, but is not guaranteed to, detect
+ corruption, and return the value zero.
+
+\subsubsection{The {\tt hnode_create} function}
+
+ \indexfunc{hnode_create}
+ \synopsis
+ \begin{verbatim}
+ hnode_t *hnode_create(void *);\end{verbatim}
+
+ \description
+ The \verb|hnode_create| function dynamically allocates a hash node,
+ stores in it the data value specified in the argument and
+ returns a pointer to it. The allocation is performed by a call to the
+ standard \verb|malloc| function. If the allocation fails, a null
+ pointer is returned.
+
+ The node's key pointer remains indeterminate until it is the subject of a
+ \verb|hash_insert| operation.
+
+\subsubsection{The {\tt hnode_init} function}
+
+ \indexfunc{hnode_init}
+ \synopsis
+ \begin{verbatim}
+ hnode_t *hnode_init(hnode_t *, void *);\end{verbatim}
+
+ \description
+ The \verb|hnode_init| function initializes the contents
+ of the specified hash node object, assigning it the
+ data value specified as the second argument.
+ The first argument is a pointer which refers to
+ a data object that has a suitable size and alignment
+ for the representation of an \verb|hnode_t| type.
+ After initialization with \verb|hnode_init|, the object is subsequently
+ eligible as an operand to the functions of the hash component,
+ other than \verb|hnode_getkey|.
+
+ The node's key pointer remains indeterminate until it is the subject of a
+ \verb|hash_insert| operation.
+
+\subsubsection{The {\tt hnode_destroy} function}
+
+ \indexfunc{hnode_destroy}
+ \synopsis
+ \begin{verbatim}
+ void hnode_destroy(hnode_t *);\end{verbatim}
+
+ \description
+ The \verb|hnode_destroy| function destroys a hash node that has been
+ allocated with the \verb|hnode_create| function. The value of any pointer
+ that referred to the node that was thus freed is indeterminate.
+
+ If the node is currently the occupant of a hash, the behavior is undefined
+ if the hash is subsequently used.
+
+\subsection{Implementation}
+
+TODO
+
+\section{Dictionary component}
+
+\index{Dictionary}
+The Dictionary component provides a means to manage ordered sequences of
+elements, having the following properties:
+\begin{enumerate}
+\item If the dictionary is not empty, a first and last element can be identified.
+ In a dictionary having only one element, that one element is both the first and
+ last element.
+\item Each element that is not the last element has another element as its
+ {\it successor}.
+ \index{successor!of a dictionary element}
+ \index{Dictionary!successor of an element}
+\item Each element that is not the first element has a {\it predecessor}.
+ \index{predecessor!of a dictionary element}
+ \index{Dictionary!predecessor of an element}
+\item No element is the predecessor or successor of more than one element.
+\item If one element is the successor of another, the other is necessarily the
+ predecessor of the first.
+\item Each element is associated with a piece of information known as
+ the key. The sequence is ordered according to the relation imposed
+ by the comparison function: the key of an element compares
+ greater than or equal to the key of its predecessor.
+\item If duplicate keys are present, then elements
+ having the same key form a subsequence with no other keys in it, which
+ follows from the previous property. No additional ordering is imposed
+ within such subsequences.
+\item Each element is associated with arbitrary satellite data.
+\end{enumerate}
+
+The Dictionary component supports efficient operations over such ordered
+sequences: such as insertion, deletion, ordered traversal, as well as exact and
+range searches.\footnote{The implicit association of keys and satellite data,
+together with the ability of efficiently search by key to retrieve data, gives
+rise to the term {\it dictionary}. A dictionary need not be ordered; a hash can
+therefore also be considered to be a kind of dictionary; the Kazlib
+nomenclature is somewhat unfortunate in that regard.}
+
+The number of elements that can be stored in a dictionary is limited; maximum
+number of entries in a dictionary is known as its {\it capacity}.
+
+\subsection{Interface}
+
+\subsubsection{The {\tt dict.h} header}
+
+Each C or C++ translation unit that is to use the functionality of the Dict
+component shall include the header \verb|dict.h|. This header shall
+contain declarations of types and external functions, and definitions of
+macros. The following typedef names shall be
+defined:\index{Dict!typedef names}
+\index{typedefs!defined by Dict}
+\begin{verbatim}
+ dict_t dnode_process_t
+ dnode_t dnode_alloc_t
+ dictcount_t dnode_free_t
+ dict_comp_t dict_load_t
+\end{verbatim}
+In addition, the following structure tags may be defined:\index{Dict!tag names}
+\index{tags!defined by Dict}
+\begin{verbatim}
+ struct dict_t
+ struct dnode_t
+\end{verbatim}
+The following external function names shall be declared:
+\index{Dict!function names}\index{functions!defined by Dict}
+\begin{verbatim}
+ dict_create dict_count
+ dict_set_allocator dict_isempty
+ dict_destroy dict_isfull
+ dict_free_nodes dict_contains
+ dict_init dict_allow_dupes
+ dict_verify dnode_is_in_a_dict
+ dict_lookup dnode_create
+ dict_lower_bound dnode_init
+ dict_upper_bound dnode_destroy
+ dict_insert dnode_get
+ dict_delete dnode_getkey
+ dict_alloc_insert dnode_put
+ dict_delete_free dict_process
+ dict_first dict_load_begin
+ dict_last dict_load_next
+ dict_next dict_load_end
+ dict_prev dict_free
+\end{verbatim}
+The following preprocessor symbols shall be
+defined: \index{Dict!macro names}\index{macros!defined by Dict}
+\indexmacro{DICTCOUNT_T_MAX}
+\indexmacro{DICT_H}
+\begin{verbatim}
+ DICTCOUNT_T_MAX
+ DICT_H\end{verbatim}
+\index{symbols!reserved by Dict}\index{Dict!reserved symbols}
+Macro identifiers which begin with the upper-case prefix \verb|DICT| are
+reserved for future extensions to the \verb|dict.h| header, as are
+names in the ordinary and tag namespaces which begin with \verb|dict_|
+or \verb|dnode_|. External names which begin with \verb|dict_|
+or \verb|dnode_| are reserved by the Kazlib library regardless of
+what headers are included.
+
+\subsubsection{The {\tt dict_t} type}
+
+\indextype{dict_t}
+The type \verb|dict_t| is an opaque data type which represents a single
+dictionary. A dictionary consists of an instance of the \verb|dict_t| type,
+plus zero or more instances of the type \verb|dnode_t|. An object of type
+\verb|dict_t| can be initialized by the \verb|dict_init| function. Alternately,
+the \verb|dict_create| function will dynamically allocate and initialize a
+dictionary. An empty dictionary created by \verb|dict_create| may be disposed
+of using \verb|dict_destroy|.
+
+\subsubsection{The {\tt dnode_t} type}
+
+\indextype{dnode_t}
+The \verb|dnode_t| type represents a single entry in a dictionary called a
+dictionary node. The object stores a pointer to user data, and a key pointer
+that is assigned to the dictionary node at the time when it is inserted into
+the dictionary. A \verb|dnode_t| may be dynamically created using
+\verb|dnode_create| and destroyed using \verb|dnode_destroy|. Alternately,
+the program may supply storage for a \verb|dnode_t| object and initialize
+it using the \verb|dnode_init| function.
+
+\subsubsection{The {\tt dictcount_t} type}
+
+\indextype{dictcount_t}
+\indexmacro{DICTCOUNT_T_MAX}
+This is an unsigned integral type which is capable of representing the number
+of nodes in a dictionary. The \verb|DICTCOUNT_T_MAX| macro expands to a
+constant expression of type \verb|dictcount_t| which specifies the maximum
+value of that type.
+
+\subsubsection{The {\tt dict_comp_t} type}
+
+\indextype{dict_comp_t}
+The \verb|dict_comp_t| type is a typedef name for the pointer-to-function type
+\begin{verbatim}
+ int (*)(const void *, const void *);
+\end{verbatim}
+In the context of the Dictionary component, this type denotes pointers to
+comparison functions.
+
+\subsubsection{The {\tt dnode_process_t} type}
+
+\indextype{dnode_process_t}
+The type \verb|dnode_process_t| is a typedef name for the pointer-to-function type
+\begin{verbatim}
+ void (*)(dict_t *, dnode_t *, void *);
+\end{verbatim}
+In the context of the Dictionary component, this is the type of a
+dictionary node processing function (See section \ref{section:dict_process}).
+The first two parameters identify a dictionary and the node within that
+dictionary that is being processed. The third argument is a context pointer.
+
+\subsubsection{The {\tt dnode_alloc_t} type}
+
+\indextype{dnode_alloc_t}
+The type \verb|dnode_alloc_t| is a typedef name for the pointer-to-function type
+\begin{verbatim}
+ dnode_t *(*)(void *);
+\end{verbatim}
+A function compatible with this type which meets certain other criteria may be
+registered with a \verb|dict_t| object as a node allocator function
+(See section \ref{section:dict_set_allocator}).
+
+\subsubsection{The {\tt dnode_free_t} type}
+
+\indextype{dnode_free_t}
+The type \verb|dnode_free_t| is a typedef name for the pointer-to-function type
+\begin{verbatim}
+ void (*)(dnode_t *, void *);
+\end{verbatim}
+A function compatible with this type which meets certain other criteria may be
+registered with a \verb|dict_t| object as a node deallocator function.
+(See section \ref{section:dict_set_allocator}).
+
+\subsubsection{The {\tt dict_load_t} type}
+
+\indextype{dict_load_t}
+
+The \verb|dict_load_t| type is opaque, and represents a context structure
+used during the process of constructing a dictionary from an ordered list
+of nodes. (See sections \ref{section:dict_load_begin} to
+\ref{section:dict_load_end}).
+
+\subsubsection{The {\tt dict_create} function}
+
+ \indexfunc{dict_create}
+ \index{Dictionary!creation of}
+ \index{create!dictionary object}
+
+ \synopsis
+ \begin{verbatim}
+ dict_t *dict_create(dictcount_t, dict_comp_t);\end{verbatim}
+
+ \description
+ The \verb|dict_create| function allocates a new
+ object of type \verb|dict_t| and initializes it to act as
+ a dictionary.
+
+ If insufficient resources exist for the allocation,
+ a null pointer is returned, otherwise a pointer to the dictionary
+ is returned.
+
+ The first argument specifies the capacity of the dictionary,
+ which is initially empty.
+
+ The second argument is a comparison function that is used for comparing
+ keys during insertion and searching operations, and is associated
+ with the dictionary for its entire duration.
+
+\subsubsection{The {\tt dict_set_allocator} function}
+
+ \label{section:dict_set_allocator}
+ \indexfunc{dict_set_allocator}
+
+ \synopsis
+ \begin{verbatim}
+ void dict_set_allocator(dict_t *, dnode_alloc_t,
+ dnode_free_t, void *);\end{verbatim}
+
+ \constraints
+
+ The second and third arguments---the function pointers---shall either
+ both be null, or both be non-null. The dictionary pointed at by the first
+ argument shall be empty.
+
+ \description
+
+ When a dictionary is initialized, it is outfitted with a pair of default
+ node allocation functions. These functions may be replaced with functions
+ supplied by the program by calling the \verb|dict_set_allocator| function
+ and specifying two suitable pointers. If these pointers are null, the
+ default functions are restored.
+
+ These functions are called to allocate and free \verb|dnode_t|
+ objects by the functions \verb|dict_alloc_insert|
+ and \verb|dict_delete_free| (see sections
+ \ref{section:dict_delete_free} and \ref{section:dict_alloc_insert}).
+
+ If sufficient resources exist, the allocation function shall
+ return a pointer to a unique storage object that is large enough
+ and suitably aligned to represent an object of type \verb|dnode_t|.
+ Otherwise, the function shall return a null pointer.
+
+ The deallocation function shall be capable of disposing of the
+ objects created by the matching allocator function.
+
+\subsubsection{The {\tt dict_destroy} function}
+
+ \indexfunc{dict_destroy}
+
+ \synopsis
+ \begin{verbatim}
+ void dict_destroy(dict_t *);\end{verbatim}
+
+ \constraints
+
+ The dictionary pointed at by the first argument shall be empty.
+
+ \description
+
+ The \verb|dict_destroy| function deinitializes and deallocates a dictionary
+ object that was created by \verb|dict_create|. All pointers that
+ referred to the dictionary become indeterminate.
+
+\subsubsection{The {\tt dict_free_nodes} function}
+
+ \indexfunc{dict_free_nodes}
+
+ \synopsis
+ \begin{verbatim}
+ void dict_free_nodes(dict_t *);\end{verbatim}
+
+ \description
+
+ Every node in the dictionary is removed from the dictionary and is then
+ subject to the deallocation function, as if the function
+ \verb|dict_delete_free| (Section \ref{section:dict_delete_free}) were
+ invoked on each node, in some unspecified order.
+
+\subsubsection{The {\tt dict_free} function}
+
+ \indexfunc{dict_free}
+
+ \synopsis
+ \begin{verbatim}
+ void dict_free(dict_t *);\end{verbatim}
+
+ \description
+
+ This function is obsolescent, and will be removed from some future revision
+ of this document. It is equivalent to \verb|dict_free_nodes|.
+
+\subsubsection{The {\tt dict_init} function}
+
+ \indexfunc{dict_init}
+
+ \synopsis
+ \begin{verbatim}
+ dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t);\end{verbatim}
+
+ \description
+
+ The \verb|dict_init| function prepares specified \verb|dict_t| object
+ to behave as a dictionary that may subsequently be used with the other
+ dictionary functions.
+
+ The first argument points to the \verb|dict_t| object to be initialized.
+ The second argument specifies the capacity of the dictionary. The third
+ argument is a pointer to the comparison function which shall be associated
+ with the dictionary for its entire duration.
+
+\subsubsection{The {\tt dict_verify} function}
+
+ \indexfunc{dict_verify}
+
+ \synopsis
+ \begin{verbatim}
+ int dict_verify(dict_t *);\end{verbatim}
+
+ \description
+
+ The intent of the \verb|dict_verify| function is to perform a verification
+ on the dictionary object, regardless of whether the Kazlib implementation
+ is operated in verification or production mode. If the dictionary object
+ and its constituent nodes have been correctly manipulated, and the program
+ has not caused any undefined behaviors, the value $1$ is returned.
+ Otherwise, the function may be able to, but is not guaranteed to, detect
+ corruption, and return the value zero.
+
+\subsubsection{The {\tt dict_lookup} function}
+
+ \indexfunc{dict_lookup}
+
+ \synopsis
+ \begin{verbatim}
+ dnode_t *dict_lookup(dict_t *, const void *);\end{verbatim}
+
+ \description
+ The \verb|dict_lookup| function searches the given dictionary for a node
+ matching the given key. Unless the dictionary is empty, the key shall be
+ compared against one or more keys that are already in the dictionary, using
+ the comparison function. The key pointer may be identical to one that has
+ already been inserted into the dictionary.
+
+ If the key is found in the dictionary, a pointer to the corresponding node
+ is returned.
+
+ If the key is not found, a null pointer is returned.
+
+ If the dictionary contains more than one key which matches the search
+ key, then the first key in the subsequence of duplicate keys is returned.
+
+\subsubsection{The {\tt dict_lower_bound} function}
+
+ \indexfunc{dict_lower_bound}
+
+ \synopsis
+ \begin{verbatim}
+ dnode_t *dict_lower_bound(dict_t *, const void *);\end{verbatim}
+
+ \description
+
+ The \verb|dict_lower_bound| function searches the dictionary in a manner
+ similar to \verb|dict_lookup|.
+
+ If the given key exists in the dictionary, the behavior is exactly the same
+ as \verb|dict_lookup|.
+
+ However, if the key is not found, then the node which has the smallest key
+ that is greater than the search key is returned. If no such key exists
+ (because the search key is higher than any other key in the dictionary
+ or the dictionary is empty) then a null pointer is returned.
+
+ \example
+ Suppose that pointer \verb|d| refers to a dictionary whose registered
+ comparison function performs lexicographic comparisons on ordinary
+ C strings, similar to \verb|strcmp|. To iterate over all keys that
+ begin with the letter \verb|d|, the following idiom can be used:
+ \begin{verbatim}
+ dict_t *d;
+ dnode_t *n, *start, *end;
+ /*...*/
+ start = dict_lower_bound(d, "d");
+ end = dict_lower_bound(d, "e");
+ for (n = start; n != end; n = dict_next(d, n)) {
+ /* n points to each node in turn whose
+ key starts with 'd' */
+ }
+ \end{verbatim}
+ Note that if the dictionary is empty, or has keys which are all lower
+ than \verb|"d"|, then both \verb|start| and \verb|end| shall be null
+ pointers, and the loop body will never execute since the two are equal.
+ Also note that if there are keys that begin with \verb|d| and the
+ dictionary's last node has a key that starts with \verb|d|, then \verb|end|
+ is null, otherwise \verb|end| points to the first key that doesn't begin
+ with \verb|d|. In both cases, the loop will terminate after processing the
+ last \verb|d| key, because \verb|dict_next| shall produce a pointer that is
+ equal to \verb|end|.
+
+\subsubsection{The {\tt dict_upper_bound} function}
+
+ \indexfunc{dict_upper_bound}
+
+ \synopsis
+ \begin{verbatim}
+ dnode_t *dict_upper_bound(dict_t *, const void *);\end{verbatim}
+
+ \description
+
+ The \verb|dict_upper_bound| function searches the dictionary in a manner
+ similar to \verb|dict_lookup|.
+
+ If the given key exists in the dictionary, the behavior is exactly the same
+ as \verb|dict_lookup| with one difference:
+ If the dictionary contains more than one key which matches the search
+ key, then the last key in the sequence of duplicates is returned,
+ rather than the first.
+
+ However, if the key is not found, then the node which has the greatest key
+ that is lower than the search key is returned. If no such key exists
+ (because the search key is lower than any other key in the dictionary
+ or the dictionary is empty) then a null pointer is returned.
+
+ \example
+ The following idiom can be used to iterate over a sequence of duplicate
+ keys without the overhead of performing a full comparison before each
+ iteration to detect the first non-matching key.
+ \begin{verbatim}
+ dict_t *d;
+ void *key;
+ dnode_t *n, *start, *end;
+
+ /* ... Initialize d, and key. ...*/
+ start = dict_lower_bound(d, key);
+ end = dict_upper_bound(d, key);
+
+ /* advance end to first non-matching key */
+ if (end != 0)
+ end = dict_next(d, end);
+ else
+ end = start; /* start == dict_first(d) in this case */
+
+ for (n = start; n != end; n = dict_next(d, n)) {
+ /* n points to duplicate keys in turn */
+ }
+ \end{verbatim}
+ Immediately prior to the execution of the if statement, exactly one of the
+ following conditions is true:
+ \begin{itemize}
+ \item The key was found in the dictionary; \verb|start| points to the
+ first duplicate node and \verb|end| points to the last.
+ \item The dictionary has only higher keys than the search key; \verb|start|
+ points to the first node in the dictionary and \verb|end| is null.
+ \item The dictionary has only lower keys than the search key; \verb|end|
+ points to the last node in the dictionary, and \verb|start| is null.
+ \item The dictionary has both lower and higher keys; \verb|end| and \verb|start|
+ point to two consecutive nodes, respectively, such that the node
+ pointed at by \verb|end| has a lower key than the search key and
+ the node pointed at by \verb|start| has a higher key.
+ \item The dictionary is empty; \verb|start| and \verb|end| are null.
+ \end{itemize}
+ The if statement ensures that if the dictionary contains no matching
+ keys, than \verb|start| and \verb|end| are equal, and if the dictionary
+ contains one or more matching keys, than \verb|end| points to the first
+ non-matching node, or is null if there is no such node. Thus the loop
+ performs correctly in all circumstances.
+
+\subsubsection{The {\tt dict_insert} function}
+
+ \label{section:dict_insert}
+ \indexfunc{dict_insert}
+
+ \synopsis
+ \begin{verbatim}
+ void dict_insert(dict_t *, dnode_t *, const void *);\end{verbatim}
+
+ \constraints
+ The dictionary is not full. If the dictionary has not been configured
+ to allow duplicate keys, the key specified by the \verb|void *| parameter
+ does not already exist in the dictionary.
+
+ \description
+ The \verb|dict_insert| function adds a new node to a dictionary. The user
+ must supply a node object that was initialized with \verb|dnode_init| or
+ dynamically created with \verb|dnode_create|. If the node is already
+ inserted into the same dictionary or any other dictionary, the behavior is
+ undefined.
+
+ Duplicate keys may be inserted into a dictionary only if the dictionary
+ has been configured to permit duplicate keys (see section
+ \ref{section:dict_allow_dupes}). If this is the case, it is also
+ permissible to insert the same key more than once: the implementation shall
+ not distinguish between distinct keys that are declared equal by a
+ correctly designed comparison function, and two key pointers that refer to
+ the same key.
+
+ A program may modify a key or node that has been inserted into a
+ dictionary, or cause the storage of the key or the node to become invalid.
+ However, any subsequent use of the dictionary invokes undefined behavior, with
+ the following exception: the data pointer stored within a node may be
+ modified using the \verb|dnode_put| function.
+
+ The Dictionary implementation shall not modify the storage referenced by a
+ key, and shall not access it other than indirectly through the supplied
+ comparison function.
+
+\subsubsection{The {\tt dict_delete} function}
+
+ \indexfunc{dict_delete}
+
+ \synopsis
+ \begin{verbatim}
+ dnode_t *dict_delete(dict_t *, dnode_t *);\end{verbatim}
+
+ \constraints
+ The specified node is an occupant of the given dictionary.
+
+ \description
+ The \verb|dict_delete| function removes from the given dictionary a
+ node that has previously been inserted into it. The key under
+ which the node was inserted is also removed from the dictionary.
+
+\subsubsection{The {\tt dict_alloc_insert} function}
+
+ \label{section:dict_alloc_insert}
+ \indexfunc{dict_alloc_insert}
+
+ \synopsis
+ \begin{verbatim}
+ int dict_alloc_insert(dict_t *, const void *, void *);\end{verbatim}
+
+ \constraints
+
+ The second argument specifies the insertion key. The dictionary shall not
+ already contain this key unless it has been configured as allowing
+ duplicates.
+
+ \description
+
+ The \verb|dict_alloc_insert| function dynamically allocates and
+ initializes a \verb|dnode_t| object and inserts it into the
+ given dictionary. The second argument and third arguments are pointers
+ to user data and key objects, either of which may be null.
+
+ The allocation is performed by a call to the default allocation
+ function, or to the function that was configured using
+ \verb|dict_set_allocator| (Section \ref{section:dict_set_allocator}).
+
+ If the allocation succeeds, the insertion is performed and
+ the value 1 is returned. If the allocation fails, no insertion is
+ performed and 0 is returned.
+
+\subsubsection{The {\tt dict_delete_free} function}
+
+ \label{section:dict_delete_free}
+ \indexfunc{dict_delete_free}
+
+ \synopsis
+ \begin{verbatim}
+ void dict_delete_free(dict_t *, dnode_t *);\end{verbatim}
+
+ \constraints
+ The given node can be found within the given dictionary.
+
+ \description
+ The \verb|dict_delete_free| function is the reverse of
+ \verb|dict_alloc_insert|. It removes the given node form the
+ dictionary and then deletes it using the default or user-defined allocator
+ (Section \ref{section:dict_set_allocator}). If the given node
+ had not been created using \verb|dict_alloc_insert|, the behavior
+ is undefined.
+
+\subsubsection{The {\tt dict_first} function}
+
+ \indexfunc{dict_first}
+
+ \synopsis
+ \begin{verbatim}
+ dnode_t *dict_first(dict_t *);\end{verbatim}
+
+ \description
+ If the dictionary pointed at by the argument is empty, a null pointer
+ is returned. Otherwise, a pointer to the first node in that dictionary is
+ returned.
+
+\subsubsection{The {\tt dict_last} function}
+
+ \indexfunc{dict_last}
+
+ \synopsis
+ \begin{verbatim}
+ dnode_t *dict_last(dict_t *);\end{verbatim}
+
+ \description
+ If the dictionary pointed at by the argument is empty, a null pointer
+ is returned. Otherwise, a pointer to the last node in that dictionary is
+ returned.
+
+
+\subsubsection{The {\tt dict_next} function}
+
+ \indexfunc{dict_next}
+
+ \synopsis
+ \begin{verbatim}
+ dnode_t *dict_next(dict_t *, dnode_t *);\end{verbatim}
+
+ \constraints
+ The node pointed at by the second argument is an occupant of the dictionary
+ pointed at by the first argument.
+
+ \description
+ If the node pointed at by the second argument has a successor, a pointer to
+ that successor is returned. Otherwise, a null pointer is returned.
+
+ \example
+ The \verb|dict_first| and \verb|dict_next| functions can be used together
+ to iterate over all of the elements of the dictionary, as in the following
+ idiom:
+ \begin{verbatim}
+ dict_t *d;
+ dnode_t *n;
+ /*...*/
+ for (n = dict_first(d); n != 0; n = dict_next(d, n)) {
+ /* n points to each node in turn */
+ }
+ \end{verbatim}
+
+\subsubsection{The {\tt dict_prev} function}
+
+ \indexfunc{dict_prev}
+
+ \synopsis
+ \begin{verbatim}
+ dnode_t *dict_prev(dict_t *, dnode_t *);\end{verbatim}
+
+ \constraints
+ The node pointed at by the second argument is an occupant of the dictionary
+ pointed at by the first argument.
+
+ \description
+ If the node pointed at by the second argument has a predecessor, a pointer
+ to that predecessor is returned. Otherwise, a null pointer is returned.
+
+\subsubsection{The {\tt dict_count} function}
+
+ \indexfunc{dict_count}
+
+ \synopsis
+ \begin{verbatim}
+ dictcount_t dict_count(dict_t *);\end{verbatim}
+
+ \description
+ The \verb|dict_count| function returns a value which represents the number
+ of nodes currently stored in the dictionary pointed at by the argument.
+
+\subsubsection{The {\tt dict_isempty} function}
+
+ \indexfunc{dict_isempty}
+
+ \synopsis
+ \begin{verbatim}
+ int dict_isempty(dict_t *);\end{verbatim}
+
+ \description
+ The \verb|dict_isempty| function returns 1 if the given dictionary is
+ empty, otherwise it returns 0.
+
+\subsubsection{The {\tt dict_isfull} function}
+
+ \indexfunc{dict_isfull}
+
+ \synopsis
+ \begin{verbatim}
+ int dict_isfull(dict_t *);\end{verbatim}
+
+ \description
+ The \verb|dict_isfull| function returns 1 if the dictionary is full,
+ otherwise it returns 0.
+
+ If the argument is an expression with side effects, the behavior is
+ undefined.\index{macros!and side effects}
+
+\subsubsection{The {\tt dict_contains} function}
+
+ \indexfunc{dict_contains}
+
+ \synopsis
+ \begin{verbatim}
+ int dict_contains(dict_t *, dnode_t *);\end{verbatim}
+
+ \description
+ The \verb|dict_contains| function searches the given dictionary to
+ determine whether the given node is an occupant. If the node is found, 1 is
+ returned, otherwise 0 is returned.\footnote{The intent is to support
+ verification. The search may be inefficient compared to {\tt
+ dict_lookup}.}
+
+\subsubsection{The {\tt dict_allow_dupes} function}
+
+ \label{section:dict_allow_dupes}
+ \indexfunc{dict_allow_dupes}
+
+ \synopsis
+ \begin{verbatim}
+ void dict_allow_dupes(dict_t *);\end{verbatim}
+
+ \constraints
+ The dictionary specified by the first argument shall be empty.
+
+ \description
+ The \verb|dict_allow_dupes| function configures the given dictionary to
+ support duplicate keys. This can only be done when the dictionary is empty,
+ and the change cannot be reverted.
+
+\subsubsection{The {\tt dnode_is_in_a_dict} function}
+
+ \indexfunc{dnode_is_in_a_dict}
+
+ \synopsis
+ \begin{verbatim}
+ int dnode_is_in_a_dict(dnode_t *);\end{verbatim}
+
+ \description
+ The \verb|dnode_is_in_a_dict| function reports whether the given node
+ is currently the occupant of some dictionary. If so, 1 is returned.
+ Otherwise 0 is returned.
+
+\subsubsection{The {\tt dnode_create} function}
+
+ \indexfunc{dnode_create}
+
+ \synopsis
+ \begin{verbatim}
+ dnode_t *dnode_create(void *);\end{verbatim}
+
+ \description
+ The \verb|dnode_create| function dynamically allocates a dictionary node,
+ stores in it the data value specified in the argument and
+ returns a pointer to it. The allocation is performed by a call to the
+ standard \verb|malloc| function. If the allocation fails, a null
+ pointer is returned.
+
+ The node's key pointer remains indeterminate until it is the subject of a
+ \verb|dict_insert| operation.
+
+\subsubsection{The {\tt dnode_init} function}
+
+ \indexfunc{dnode_init}
+
+ \synopsis
+ \begin{verbatim}
+ dnode_t *dnode_init(dnode_t *, void *);\end{verbatim}
+
+ \description
+ The \verb|dnode_init| function initializes the contents
+ of the specified dictionary node object, assigning it the
+ data value specified as the second argument.
+ The first argument is a pointer which refers to
+ a data object that has a suitable size and alignment
+ for the representation of an \verb|dnode_t| type.
+ After initialization with \verb|dnode_init|, the object is subsequently
+ eligible as an operand to the functions of the dictionary component,
+ other than \verb|dnode_getkey|.
+
+ The node's key pointer remains indeterminate until it is the subject of a
+ \verb|dict_insert| operation.
+
+\subsubsection{The {\tt dnode_destroy} function}
+
+ \indexfunc{dnode_destroy}
+
+ \synopsis
+ \begin{verbatim}
+ void dnode_destroy(dnode_t *);\end{verbatim}
+
+ \description
+ The \verb|dnode_destroy| function destroys a dictionary node that has been
+ allocated with \verb|dnode_create|. The value of any pointer
+ that referred to the node that was thus freed is indeterminate.
+
+ If the node is currently the occupant of a dictionary, the behavior is
+ undefined if the hash is subsequently used.
+
+\subsubsection{The {\tt dnode_get} function}
+
+ \indexfunc{dnode_get}
+
+ \synopsis
+ \begin{verbatim}
+ void *dnode_get(dnode_t *);\end{verbatim}
+
+ \description
+ The \verb|dnode_get| function retrieves the \verb|void * | data value
+ associated with the given dictionary node.
+
+\subsubsection{The {\tt dnode_getkey} function}
+
+ \indexfunc{dnode_getkey}
+
+ \synopsis
+ \begin{verbatim}
+ const void *dnode_getkey(dnode_t *);\end{verbatim}
+
+ \description
+
+ The \verb|dnode_getkey| function retrieves the \verb|void *| key value
+ associated with the given node. A node acquires an associated key
+ when it is inserted into a dictionary (see section \ref{section:dict_insert}).
+ Invoking \verb|dnode_getkey| on a node that has not been inserted
+ into a dictionary results in undefined behavior.
+
+\subsubsection{The {\tt dnode_put} function}
+
+ \indexfunc{dnode_put}
+
+ \synopsis
+ \begin{verbatim}
+ void dnode_put(dnode_t *, void *);\end{verbatim}
+
+ \description
+ The function \verb|dnode_put| replaces the data element
+ associated with the dictionary node.
+
+\subsubsection{The {\tt dict_process} function}
+
+ \label{section:dict_process}
+ \indexfunc{dict_process}
+
+ \synopsis
+ \begin{verbatim}
+ void dict_process(dict_t *, void *, dnode_process_t);\end{verbatim}
+
+ \description
+ The \verb|dict_process| function iterates over the nodes of a dict,
+ and for each node invokes a callback function.\footnote{In most cases,
+ it is more convenient and preferable to
+ iterate over the dict using explicit calls to {\tt dict_first}
+ and {\tt dict_next}.}
+ The second argument is a {\it context pointer\/} which can have any value.
+ The third argument of
+ \verb|dict_process| shall be a pointer to a function which is compatible
+ with the specified type. If the dict contains one or more nodes,
+ then the function is invoked once for each node, in order from first
+ to last. On each invocation, the first argument of the callback is a
+ pointer to the dict; the second argument is a pointer to a node, called
+ the {\it subject node}; and the third argument repeats the context pointer
+ value that was originally passed to \verb|dict_process|.
+
+ The callback function may delete the subject node by, for instance, calling
+ \verb|dict_delete|. It may insert new nodes into the dictionary;
+ however, if such an insertion causes the subject node to acquire
+ a new successor, it is implementation-defined whether upon returning
+ from the callback function, the traversal shall continue with the
+ new successor, or with the original successor.
+
+ The callback function, and any function invoked from the callback
+ function, shall not destroy the dictionary or make any modifications
+ other than the insertion of new nodes, or the deletion of the
+ subject node.
+
+ The callback function may recursively invoke \verb|dict_process| for the
+ same dictionary or for a different dictionary; the callback invocations arising out of
+ the nested call inherit all of the restrictions of the outer callback in
+ addition to being subject to the usual restrictions.\footnote{This means,
+ for instance, that if two callbacks are in progress for different
+ subject nodes from the same dictionary, the inner callback may not delete
+ its subject node, because it inherits the restriction that the only
+ permitted deletion is the outer callback's subject node.}
+
+ The callback function may freely operate on a different dictionary,
+ subject to any inherited restrictions.
+
+\subsubsection{The {\tt dict_load_begin} function}
+
+ \label{section:dict_load_begin}
+ \indexfunc{dict_load_begin}
+
+ \synopsis
+ \begin{verbatim}
+ void dict_load_begin(dict_load_t *, dict_t *);\end{verbatim}
+
+ \constraints
+ The dictionary specified by the second argument is empty.
+
+ \description
+ The \verb|dict_load_begin| function prepares a context object
+ for the task of constructing the contents of a dictionary out of
+ a sequence of elements which is already sorted according to the
+ sorting function of the dictionary.\footnote{This process is more efficient
+ than inserting all of the elements into a dictionary using {\tt dict_insert}.
+ In the reference implementation, this process runs in linear time, or $O(n)$
+ whereas construction by repeated insertions runs in $O(n\log n)$ time.}
+ The actual construction is performed
+ by zero or more calls to \verb|dict_load_next| and is finalized by
+ \verb|dict_load_end|.
+
+ The \verb|dict_load_begin| function is said to bind the dictionary
+ and context object together; the only way to unbind the two
+ is by calling \verb|dict_load_end| on the context object.
+
+ The program shall not manipulate a dictionary that is bound to
+ a context object, other than by calling \verb|dict_load_next|.
+
+ The program shall not attempt to bind a dictionary to more than one context
+ object simultaneously, or a context object to more than one dictionary
+ simultaneously.
+
+\subsubsection{The {\tt dict_load_next} function}
+
+ \label{section:dict_load_next}
+ \indexfunc{dict_load_next}
+
+ \synopsis
+ \begin{verbatim}
+ void dict_load_next(dict_load_t *, dnode_t *, const void *);\end{verbatim}
+
+ \constraints
+ The node pointed at by the second argument is not an occupant of
+ any dictionary. The key specified by the third argument is greater
+ than or equal to all keys specified in previous calls to
+ \verb|dict_load_next| in the context of the same construction,
+ according to the comparison function of the dictionary that is
+ being constructed. That is to say, successive calls specify monotonically
+ increasing keys.
+ The dictionary is not full.
+
+ \description
+ The \verb|dict_load_next| function continues the construction of a
+ dictionary from an ordered list of elements by specifying the next
+ node in the sequence, along with its key. After this call, the node
+ is considered to be inserted into the dictionary as if by
+ \verb|dict_insert|.
+
+\subsubsection{The {\tt dict_load_end} function}
+
+ \label{section:dict_load_end}
+ \indexfunc{dict_load_end}
+
+ \synopsis
+ \begin{verbatim}
+ void dict_load_end(dict_load_t *);\end{verbatim}
+
+ \description
+ The \verb|dict_load_end| function finalizes the construction of
+ a dictionary from a ordered sequence. It breaks the binding between
+ the \verb|dict_load_t| context object and the dictionary.
+
+\subsection{Implementation}
+
+TODO
+
+\section{Exception component}
+\label{section:exception_component}
+\index{Exception}
+
+The Exception component provides distributed error handling in the form of
+exceptions, behind an interface designed to be implementable using only the
+portable features of standard C. The features of this interface are:
+\begin{itemize}
+\item the ability to set up nested try-catch regions which declare specific
+exceptions that they can handle;
+\item grouped exceptions, allowing handlers to catch specific exceptions,
+or any exception within a group;
+\item the ability to designate a function that is called in the event
+that an exception is thrown that has no handler.
+\item a mechanism for releasing resources acquired by code that is terminated
+by an exception;
+\item the ability to pass dynamically allocated data from the throw site to the
+catch site.
+\end{itemize}
+
+An exception is simply a means of returning to a prior place in the program's
+execution. The ANSI C language provides crude, but portable, exception handling
+consisting of the \verb|jmp_buf| type, the \verb|setjmp| macro and the
+\verb|longjmp| function. The Kazlib Exception component can be implemented in
+terms of these primitives. The constraint to implementability in standard C
+leads to a number of concessions:
+\begin{itemize}
+\item A program can leave cleanup regions and try-catch regions by improper
+means, such as using \verb|goto|, \verb|return| or \verb|break|. This is
+difficult to diagnose, and is simply documented as undefined behavior.
+There is no support in the standard language for designating code that is
+executed whenever a statement block terminates by any means.
+\item For the same reason, the exception handling interface described here
+has an explicit mechanism for deallocation of resources associated with
+statement blocks that are terminated by exceptions. This interface is
+not as convenient as language support for automatic cleanup. Correct
+management of temporary dynamic resources using this interface requires
+programmer discipline.
+\item The requirement to be able to use \verb|setjmp| to save a context
+to be later returned to during exception processing brings in restrictions
+related to non-volatile objects. If non-volatile objects are modified
+between the time an exception handling region is initiated and the time
+an exception is caught in the region, these objects have indeterminate
+values.\footnote{This liberty in ANSI C allows compiler
+or library writers to implement {\tt setjmp} as a simple mechanism that
+takes a snapshot of the machine context. Objects that are optimized into
+special storage---such as registers---and whose values change since the
+context saving operation will be clobbered when the context is restored
+by {\tt longjmp}.}
+\end{itemize}
+
+\subsection{Interface}
+
+\subsubsection{The {\tt except.h} header}
+
+Each C or C++ translation unit that is to use the functionality of the Exception
+component shall include the header \verb|except.h|. This header shall
+contain declarations of types and external functions, and definitions of
+macros. The following typedef names shall be
+defined:\index{Exception!typedef names}
+\begin{verbatim}
+ except_id_t
+ except_t
+\end{verbatim}
+The following external function names shall be declared:
+\index{Exception!function names}\index{functions!defined by Exception}
+\begin{verbatim}
+ except_init except_group
+ except_deinit except_message
+ except_rethrow except_data
+ except_throw except_take_data
+ except_throwd except_set_allocator
+ except_throwf except_alloc
+ except_unhandled_catcher except_free
+ except_code
+\end{verbatim}
+The following preprocessor symbols shall be
+defined: \index{Exception!macro names}\index{macros!defined by Exception}
+\indexmacro{XCEPT_H}
+\begin{verbatim}
+ XCEPT_H except_cleanup_pop
+ XCEPT_GROUP_ANY except_checked_cleanup_pop
+ XCEPT_CODE_ANY except_try_push
+ XCEPT_BAD_ALLOC except_try_pop
+ except_cleanup_push
+\end{verbatim}
+Finally, these two enum constants are defined:
+\begin{verbatim}
+ except_no_call
+ except_call
+\end{verbatim}
+\index{symbols!reserved by Exception}\index{Exception!reserved symbols} Macro
+identifiers which begin with the upper-case prefix \verb|XCEPT|\footnote{The
+prefix {\tt XCEPT} is used rather than {\tt EXCEPT} because ISO 9899 reserves
+preprocessor symbols beginning with {\tt E} followed by a digit or
+capital letter for future extensions to the {\tt <errno.h>} header.}
+are reserved for future extensions to the \verb|except.h|
+header, as are names in the ordinary and tag namespaces which begin with
+\verb|except_|. External names which begin with \verb|except_| are reserved by
+the Kazlib library regardless of what headers are included.
+
+\subsubsection{The {\tt except_id_t} type}
+
+\label{section:except_id_t}
+\indextype{except_id_t}
+\indexmacro{XCEPT_GROUP_ANY}
+\indexmacro{XCEPT_CODE_ANY}
+The type \verb|except_id_t| is an aggregate consisting of two unsigned long
+values which represent an {\it exception group\/} and {\it exception code},
+respectively, in that order.\footnote{Thus, the program may initialize
+an {\tt except_id_t} object using two brace-enclosed initializers which
+specify the group and code.} An exception group is a value which identifies a
+group of related exceptions. An exception code is a value which identifies a
+specific exception uniquely within a group. The codes are assigned by the
+program designer. The Exception component reserves only the group and code
+values of zero, which, when used to specify a catch, match any value.
+
+The preprocessor symbols \verb|XCEPT_GROUP_ANY| and
+\verb|XCEPT_CODE_ANY| each expand to a constant integral expression having the
+value zero. These symbols are intended, in a catch specification, to clearly
+convey that any exception or any group is being caught.
+
+The preprocessor symbol \verb|XCEPT_BAD_ALLOC| expands to an integral constant
+expression having the value 1. This symbol is intended to represent the
+standard exception group for failed memory allocations.
+(See section \ref{section:except_throwf}).
+
+The exception groups from 1 to 15 are reserved for implementation use.
+
+\subsubsection{The {\tt except_t} type}
+
+\indextype{except_t}
+An object of type \verb|except_t| keeps track of all of the information that is
+passed when an exception is thrown, and is known as an {\it exception
+descriptor}. The type is opaque, hence the program shall manipulate this type
+using only the interface functions provided.
+
+\subsubsection{The {\tt except_init} function}
+
+ \indexfunc{except_init}
+
+ \synopsis
+ \begin{verbatim}
+ int except_init(void);\end{verbatim}
+
+ \description
+ The \verb|except_init| function allocates resources needed by the
+ Exception component. Before using any of the other exception interface
+ functions or macros, the program shall perform at least one successful call
+ to \verb|except_init|.
+
+ If the initialization succeeds, \verb|except_init| returns 1. Otherwise
+ it returns 0.
+
+ The \verb|except_init| function may be called more than once. After a
+ successful call, every subsequent call shall be successful up to an
+ implementation-defined maximum number of repetitions, which shall be at least
+ as large as the \verb|INT_MAX| from \verb|limits.h|. \footnote{
+ The intent is to support, but not enforce, a style of global initialization
+ whereby each module which requires the use of another module calls its
+ initialization function from its own initialization function. Only the
+ first such call performs the initialization of the module; subsequent calls
+ merely increment a counter. During deinitialization, the counter is
+ decremented and cleanup takes place when the counter reaches zero.}
+
+\subsubsection{The {\tt except_deinit} function}
+
+ \indexfunc{except_deinit}
+
+ \synopsis
+ \begin{verbatim}
+ void except_deinit(void);\end{verbatim}
+
+ \description
+ The \verb|except_deinit| function releases the resources
+ that were allocated by \verb|except_init|.
+
+ For the resource deallocation to actually take place, the
+ \verb|except_deinit| must be called as many times as the
+ number of times \verb|except_init| was successfully called.
+
+ If \verb|except_deinit| is called more times than \verb|except_init| is
+ successfully called, the behavior is undefined.
+
+\subsubsection{The {\tt except_rethrow} function}
+
+ \indexfunc{except_rethrow}
+
+ \synopsis
+ \begin{verbatim}
+ void except_rethrow(except_t *);\end{verbatim}
+
+ \description
+ The rethrow function is used to rethrow a caught exception. The argument
+ shall not be null. An exception shall not be rethrown from outside of the
+ {\it try-catch region\/} in which it was caught. An exception shall not be
+ rethrown from a try-catch region other than the one in which it was caught.
+ It shall not be rethrown from a try-catch or cleanup region enclosed within
+ the one in which it was caught.
+
+ When an exception is rethrown, the search for a handler does not begin with
+ the region in which the exception was caught. Instead, this region is
+ terminated, and the search continues with the enclosing one, if one
+ exists.
+
+\subsubsection{The {\tt except_throw} function}
+
+ \indexfunc{except_throw}
+
+ \synopsis
+ \begin{verbatim}
+ void except_throw(long, long, const char *);\end{verbatim}
+
+ \constraints
+ The first two arguments specify the exception group and code,
+ respectively. Neither of these arguments shall be zero.
+
+ \description
+ The \verb|except_throw| function causes an exception to be thrown.
+
+ If the throw takes place in a try-catch region where an exception
+ was just caught, this original exception is considered handled. In
+ this case, the new exception is still eligible for handling by the
+ same try-catch region.
+
+ The third argument points to the first character of a string
+ which becomes the {\it exception message}. Because the throwing of
+ the exception may cause the current statement block to terminate,
+ this string data shall be non-local. It may be a string literal, since the
+ implementation shall not modify the message, or it may be an ordinary
+ object of static duration. If it is dynamic data, it becomes the handler's
+ responsibility to extract the message from the caught exception and
+ free the data.\footnote{The programmer should consider using
+ {\tt except_throwd} to pass arbitrary dynamic data from the throw
+ site to the try-catch region.}
+
+ The \verb|except_throw| function does not return. The implementation
+ searches for a suitable try-catch region starting with the one
+ initiated by the most recent \verb|except_try_push|. If there
+ is no enclosing region, the search fails. Otherwise if a match is found,
+ execution continues at the start of the target try-catch region, appearing
+ to be a second return from \verb|except_try_push| distinguished by a non-null
+ value of the \verb|except_t *| object.
+
+ If no match is found during exception processing, the exception is
+ handled internally by the implementation. The implementation then
+ calls the currently registered function for catching unhandled
+ exceptions (see section \ref{section:except_unhandled_catcher}).
+
+ The default catcher for unhandled exceptions shall terminate the program
+ with a diagnostic which identifies the code, group and exception message.
+
+ During the search for an exception handler, cleanup handlers may be
+ encountered. They are removed from the inside out and called with
+ their registered arguments. This process is called {\it unwinding}.
+ \index{unwinding}
+
+\subsubsection{The {\tt except_throwd} function}
+
+ \indexfunc{except_throwd}
+
+ \synopsis
+ \begin{verbatim}
+ void except_throwd(long, long, const char *, void *);\end{verbatim}
+
+ \constraints
+ The first two arguments specify the exception group and code,
+ respectively. Neither of these arguments shall be zero.
+
+ \description
+ The \verb|except_throwd| function is the same as \verb|except_throw| in
+ every respect except that it has an additional \verb|void *| parameter. A
+ null argument may be used for this parameter, or it may be any valid
+ pointer value.
+
+ When the exception is handled, and the handler does not remove this pointer
+ using \verb|except_take_data| then the implementation shall automatically
+ invoke the function \verb|except_free| on this pointer.
+
+\subsubsection{The {\tt except_throwf} function}
+
+ \indexfunc{except_throwf}
+ \label{section:except_throwf}
+
+ \synopsis
+ \begin{verbatim}
+ void except_throwf(long, long, const char *, ...);\end{verbatim}
+
+ \constraints
+ The first two arguments specify the exception group and code,
+ respectively. Neither of these arguments shall be zero.
+
+ \description
+
+ This function is almost exactly the same as \verb|except_throw|
+ except that the exception message is not directly specified.
+ Instead, the \verb|char *| argument specifies a format string which may be
+ followed by trailing arguments. The format string and trailing arguments
+ are interpreted as the format string and arguments of the standard C
+ function \verb|printf| and are subject to the same requirements.
+
+ The format string is interpreted, and the results of formatting are placed into
+ buffer provided by the implementation. The implementation shall provide
+ space for at least 1024 bytes of storage for the result of the formatting,
+ including the null terminator byte. If the formatting requires more space
+ than the implementation provides, the behavior is undefined.
+
+ The results of the formatted print shall become the exception message
+ of the thrown exception.
+
+ If the implementation is unable to allocate resources for the formatted
+ message, it shall throw a code 1 exception having an unspecified code in
+ group \verb|XCEPT_BAD_ALLOC| with an implementation-defined message.
+ (See section \ref{section:except_id_t}).
+
+\subsubsection{The {\tt except_unhandled_catcher} function}
+
+ \label{section:except_unhandled_catcher}
+ \indexfunc{except_unhandled_catcher}
+
+ \synopsis
+ \begin{verbatim}
+ void (*except_unhandled_catcher(void (*)(except_t *)))
+ (except_t *);\end{verbatim}
+
+ \description
+ The \verb|except_unhandled_catcher| function installs a new
+ function for catching unhandled exceptions. The argument is a
+ pointer to a catching function that returns nothing, and accepts a pointer
+ of type \verb|except_t *|. A pointer to the previously installed
+ catching function is returned. If the program did not previously
+ install a catching function, then a pointer to the default catching
+ function is returned. The program may retain this pointer and
+ use it to reinstall the default function.
+
+ A function for catching unhandled exceptions should not return. If it
+ returns, the implementation shall terminate the program with a diagnostic.
+
+\subsubsection{The {\tt except_code} function}
+
+ \indexfunc{except_code}
+
+ \synopsis
+ \begin{verbatim}
+ unsigned long except_code(except_t *);\end{verbatim}
+
+ \description
+ The \verb|except_code| is an accessor function which returns the
+ exception code of the given exception descriptor.
+
+\subsubsection{The {\tt except_group} function}
+
+ \indexfunc{except_group}
+
+ \synopsis
+ \begin{verbatim}
+ unsigned long except_group(except_t *);\end{verbatim}
+
+ \description
+ The \verb|except_group| is an accessor function which returns the
+ exception group of the given exception descriptor.
+
+\subsubsection{The {\tt except_message} function}
+
+ \indexfunc{except_message}
+
+ \synopsis
+ \begin{verbatim}
+ const char *except_message(except_t *);\end{verbatim}
+
+ \description
+ The \verb|except_group| is an accessor function which returns
+ a pointer to the string of text that was specified when the
+ exception was thrown (the exception message).
+
+\subsubsection{The {\tt except_data} function}
+
+ \indexfunc{except_data}
+
+ \synopsis
+ \begin{verbatim}
+ void *except_data(except_t *);\end{verbatim}
+
+ \description
+ The \verb|except_group| returns the data pointer that
+ was specified in the \verb|except_throwd| call.
+ If the exception was not thrown by \verb|except_throwd|
+ the return value is unspecified.
+
+
+\subsubsection{The {\tt except_take_data} function}
+
+ \indexfunc{except_take_data}
+
+ \synopsis
+ \begin{verbatim}
+ void *except_take_data(except_t *);\end{verbatim}
+
+ \description
+ The \verb|except_take_data| returns the data pointer that
+ was specified in the \verb|except_throwd| call, and
+ updates the exception descriptor so that the pointer is
+ set to null.
+
+ If the exception was not thrown by \verb|except_throwd|
+ the result is unspecified.
+
+\subsubsection{The {\tt except_cleanup_push} macro}
+
+ \indexmacro{except_cleanup_push}
+
+ \synopsis
+ \begin{verbatim}
+ void except_cleanup_push(void (*)(void *), void *);\end{verbatim}
+
+ \description
+ The call to \verb|except_cleanup_push| shall be matched with a call to
+ \verb|except_cleanup_pop| which must occur in the same statement block at
+ the same level of nesting.\footnote{This requirement allows an implementation
+ to provide an {\tt except_cleanup_push} macro which opens up a statement
+ block and a {\tt except_cleanup_pop} which closes the statement block.
+ The space for the registered pointers can then be efficiently allocated
+ from automatic storage.}
+
+ The \verb|except_cleanup_push| macro registers a cleanup handler that will
+ be called if an exception subsequently occurs before the matching
+ \verb|except_cleanup_pop| is executed, and is not intercepted and handled by
+ a try-catch region that is nested between the two.
+
+ The first argument to \verb|except_cleanup_push| is a pointer
+ to the cleanup handler, a function that returns nothing and takes
+ a single argument of type \verb|void *|. The second argument
+ is a \verb|void *| value that is registered along with the handler.
+ This value is what is passed to the registered handler, should it
+ be called.
+
+ Cleanup handlers are called in the reverse order of their nesting: inner
+ handlers are called before outer handlers.
+
+ The program shall not leave the cleanup region between the call to the macro
+ \verb|except_cleanup_push| and the matching call to
+ \verb|except_cleanup_pop| by means other than throwing an exception, or
+ calling \verb|except_cleanup_pop|.
+
+ Within the call to the cleanup handler, it is possible that new exceptions
+ may happen. Such exceptions must be handled before the cleanup handler
+ terminates. If the call to the cleanup handler is terminated by an
+ exception, the behavior is undefined.\footnote{The exception which triggered
+ the cleanup is not yet caught; thus the program would be effectively trying
+ to replace an exception with one that isn't in a well-defined state.}
+
+\subsubsection{The {\tt except_cleanup_pop} macro}
+
+ \indexmacro{except_cleanup_pop}
+ \label{section:except_cleanup_pop}
+
+ \synopsis
+ \begin{verbatim}
+ void except_cleanup_pop(int);\end{verbatim}
+
+ \description
+ A call to the \verb|except_cleanup_pop| macro shall match each
+ call to \verb|except_cleanup_push| which shall be in the
+ same statement block at the same nesting level. It shall
+ match the most recent such a call that is not matched
+ by a previous \verb|except_cleanup_pop| at the same level.
+
+ This macro causes the registered cleanup handler to be removed. If, and
+ only if the argument is other than zero, the cleanup handler is called.
+ In that case, the registered context pointer is passed to the cleanup
+ handler.
+
+ \indexenum{except_no_call}
+ \indexenum{except_call}
+ The enumeration constants \verb|except_no_call| and \verb|except_call|
+ may be used as arguments to this function instead of
+ the equivalent constants \verb|0| and \verb|1|.
+
+ The program shall not leave the region between the call to the macro
+ \verb|except_cleanup_push| and the matching call to
+ \verb|except_cleanup_pop| other than by throwing an exception, or
+ by executing the \verb|except_cleanup_pop|.
+
+\subsubsection{The {\tt except_checked_cleanup_pop} macro}
+
+ \indexmacro{except_checked_cleanup_pop}
+
+ \synopsis
+ \begin{verbatim}
+ void except_checked_cleanup_pop(void (*)(void *), int);\end{verbatim}
+
+ \constraints
+ The first pointer-to-function argument shall match the pointer value that
+ was registered by the matching \verb|except_cleanup_push| macro.
+
+ \description
+ The \verb|except_checked_cleanup_pop| macro may be used as an alternative to
+ \verb|except_cleanup_pop|. In verification mode, the constraint serves to
+ provide additional safety by making an explicit declaration regarding which
+ handler is being called (or ignored, as the case may be).
+
+ The program shall not leave the region between the call to the macro
+ \verb|except_cleanup_push| and the call to
+ \verb|except_checked_cleanup_pop| by means other than throwing an
+ exception, or executing the latter macro.
+
+\subsubsection{The {\tt except_try_push} macro}
+
+ \indexmacro{except_try_push}
+ \label{section:except_try_push}
+
+ \synopsis
+ \begin{verbatim}
+ void except_try_push(const except_id_t [],
+ size_t, except_t **);\end{verbatim}
+
+ \description
+ The \verb|except_try_push| marks the beginning of a try-catch region
+ of the program. It must be matched by a \verb|except_try_pop| written in
+ the same statement block at the same level of nesting, which
+ terminates the try-catch region. Regions may be nested.
+
+ The program shall not leave a try-catch region other than by throwing
+ an exception or by executing the \verb|except_try_pop|.\footnote{Thus,
+ leaving the try-catch region using {\tt goto}, {\tt return},
+ {\tt break} or {\tt continue} leads to undefined behavior.}
+
+ The first argument is a pointer to the first element of an array of
+ \verb|except_id_t| objects, the number of elements of which is specified by
+ the second argument. The array specifies which exceptions are caught.
+ The implementation shall treat this array as read-only.\footnote{Thus,
+ the program may allocate the array in static storage.}
+
+ The third argument of \verb|except_try_push| shall point to an object
+ of type \verb|except_t *|. After the call to \verb|except_try_push|,
+ the program shall inspect the value of this object. A null value indicates
+ that no exception has been thrown. A non-null value indicates that an
+ exception was thrown, and is now caught. In other words, when an exception
+ is caught by a try-catch region, then control passes from the throw site
+ back to the first statement after the \verb|except_try_push| statement of
+ the try-catch region. This case is distinguished from an ordinary return by
+ the non-null value of the pointer object that was specified by the third
+ argument of the earlier call to \verb|except_try_push|.
+
+ An exception is considered handled if it is caught in a try-catch region
+ which subsequently terminates by executing its \verb|except_try_pop| or by
+ throwing another exception. When an exception is considered handled, any
+ dynamic data that was associated with that exception is
+ freed.\footnote{Dynamic data may be explicitly associated with an exception
+ using {\tt except_throwd}. Other types of throw may associate unspecified
+ dynamic data.} It's possible for more than one exception to be active
+ at once. During the processing of one exception, a try-catch region
+ which catches the exception may execute a nested try-catch region
+ in which independent exception processing takes place. Provided that
+ no exception escapes from the inner try-catch region, the original
+ exception remains pending. But if an exception escapes from the inner
+ region, it causes the original exception to be handled.\footnote{Thus, a
+ given try-catch region cannot catch multiple exceptions concurrently.}
+
+ The caught exception may be rethrown by calling \verb|except_rethrow|,
+ specifying the the value of the caught exception descriptor as the
+ argument. Rethrowing a caught exception causes the innermost try-catch
+ region to terminate, but the exception is not considered handled. The
+ search for a handler continues with the second most enclosing region.
+
+ Throwing a new exception during the handling of a caught exception may
+ cause the {\it same\/} try-catch region to catch that exception; the
+ try-catch region is not terminated until it is determined that it doesn't
+ catch the new exception.
+
+ Each entry in the array of \verb|except_id_t| objects specifies what
+ exceptions are caught by the try-catch region. When an exception is
+ thrown, the implementation searches for the inner-most try-catch region
+ which has at least one match for the thrown exception in its catch
+ specification array.
+
+ A match occurs when a specification exactly matches the group and code of
+ the thrown exception. If a catch specification is for group 0, then it
+ matches any group. If a catch specification is for code 0, then it matches
+ any exception code. A catch specification of group 0 and code 0 catches all
+ exceptions.
+
+ Non-volatile automatic variables that are local to the function containing
+ the try-catch region, and that are modified after \verb|except_try_push|
+ begins the try-catch region have indeterminate values when an exception is
+ caught.
+
+ Once a caught exception is handled or re-thrown, the value of the
+ \verb|except_t *| pointer which referenced it becomes indeterminate.
+ If a re-thrown exception is caught again, the implementation shall
+ produce a valid \verb|except_t *| pointer.
+
+ \example
+ The following example illustrates the use of \verb|except_try_push| and
+ related macros and functions.
+ \begin{verbatim}
+ #include <stdlib.h>
+ #include <assert.h>
+ #include "except.h"
+
+ #define MY_GROUP 42
+ #define MY_CODE 1
+
+ static void func_that_throws(void)
+ {
+ except_throw(MY_GROUP, MY_CODE, "this is an exception");
+ }
+
+ static void func_that_cleans_up(void)
+ {
+ void *local_data = malloc(10);
+
+ except_cleanup_push(free, local_data);
+ func_that_throws();
+ except_checked_cleanup_pop(free, except_call);
+ }
+
+ void func_that_catches(void)
+ {
+ /* catch specification */
+ static const except_id_t catch_spec[] = {
+ { MY_GROUP, XCEPT_CODE_ANY }
+ };
+ /* exception handle */
+ except_t *exc;
+
+ except_try_push(catch_spec, 1, &exc);
+
+ /*
+ * Start of try-catch region: when exception is
+ * thrown, control returns here.
+ */
+
+ if (exc == 0) {
+ /* try code that may throw an exception */
+
+ func_that_cleans_up();
+ } else {
+ /* handle exception that was thrown */
+
+ assert (except_group(exc) == MY_GROUP);
+ printf("exception caught: %s %ld %ld\n",
+ except_message(exc),
+ except_group(exc), except_code(exc));
+
+ goto terminate; /* ERROR! jumping out of try-catch */
+ }
+
+ /* end of try-catch region */
+
+ except_try_pop();
+ terminate:
+ ;
+ }
+ \end{verbatim}
+ In this example, the function \verb|func_that_catches| is intended to be
+ called first. It sets up a try-catch region which traps exceptions having
+ the group identification \verb|MY_GROUP| (or 42). Any code within that
+ group is caught because the code catch was specified as
+ \verb|XCEPT_CODE_ANY|. When the \verb|except_try_push| macro is executed,
+ it sets the value of \verb|exc| to null. Then \verb|func_that_cleans_up| is
+ called, which throws an exception in the \verb|MY_GROUP| group. This
+ exception is caught, so control resumes at the top of the try-catch region,
+ with \verb|exc| set to a non-null value. Thus the else clause of the if
+ statement is now executed. The handling code simply prints the exception
+ message on standard output, as well as the numeric group and code. The
+ subsequent goto statement demonstrates a serious programming error.
+
+ The \verb|func_that_cleans_up| function illustrates the use of cleanup
+ regions. Dynamic memory is allocated which must not be allowed to leak
+ when an exception is thrown, so a cleanup handler is set up to free the
+ memory in that event. The standard C function \verb|free| happens to have,
+ the right type signature and semantics that it can be used directly as a
+ cleanup handler. Should no exception be thrown, the cleanup pop macro
+ will perform the call to the cleanup handler, because it is invoked with
+ argument \verb|except_call|.
+
+\subsubsection{The {\tt except_try_pop} macro}
+
+ \indexmacro{except_try_pop}
+
+ \synopsis
+ \begin{verbatim}
+ void except_try_pop(void);\end{verbatim}
+
+ \description
+
+ The \verb|except_try_pop| macro terminates a try-catch region. It must
+ match a previous \verb|except_try_push| macro in the same statement
+ block at the same level of nesting which is not already matched by an
+ earlier \verb|except_try_pop|.
+
+\subsubsection{The {\tt except_set_allocator} function}
+
+ \indexfunc{except_set_allocator}
+ \label{section:except_set_allocator}
+
+ \synopsis
+ \begin{verbatim}
+ void except_set_allocator(void *(*)(size_t), void (*)(void *));\end{verbatim}
+
+ \description
+ The \verb|except_set_allocator| function installs a pair of allocator
+ routines that will be used by the Exception component for future allocation
+ and deallocation requests.
+
+ The first argument points to a function that resembles the standard C
+ \verb|malloc| in type and semantics. The second argument points to a
+ function that similarly resembles the standard C function \verb|free|.
+
+ The default allocators are \verb|malloc| and \verb|free|.
+ The call
+ \begin{verbatim}
+ except_set_allocator(malloc, free);
+ \end{verbatim}
+ may be used to restore these default allocator functions.
+
+ The program shall not call \verb|except_set_allocator| if an exception
+ was thrown and has not yet been handled.\footnote{Doing so could, for example,
+ create a mismatch whereby a pointer to data allocated with the previously installed
+ allocator function would be passed to the new deallocator function.}
+
+ The allocator function shall create a unique object consisting of at least
+ as many bytes of storage as indicated by the value of the argument.
+ The pointer returned shall be suitably aligned to represent an object
+ of any type. If insufficient resources exist, the pointer returned shall be
+ null. Requesting an object of zero size may produce a unique pointer
+ that shall be acceptable to the deallocator function, or a null pointer.
+
+ The deallocator function shall be capable of destroying objects created
+ by the corresponding allocator function. Passing a null pointer to the
+ deallocator shall have no effect.
+
+\subsubsection{The {\tt except_alloc} function}
+
+ \indexfunc{except_alloc}
+
+ \synopsis
+ \begin{verbatim}
+ void *except_alloc(size_t);\end{verbatim}
+
+ \description
+ The \verb|except_alloc| function allocates memory using the default
+ memory allocator or one installed by the program.
+ (See section \ref{section:except_set_allocator}).
+
+ If the allocation succeeds, a non-null pointer to the allocated object is
+ returned.
+
+ If the allocator indicates failure by returning a null pointer,
+ then instead of returning, \verb|except_alloc| throws exception code 1
+ in the group \verb|XCEPT_BAD_ALLOC| (See section \ref{section:except_id_t}).
+
+ If a zero size request is specified, then an exception is thrown or
+ a non-null pointer is returned, depending on the treatment of such
+ requests by the underlying allocator.
+
+\subsubsection{The {\tt except_free} function}
+
+ \indexfunc{except_free}
+
+ \synopsis
+ \begin{verbatim}
+ void *except_free(void *);\end{verbatim}
+
+ \description
+
+ The \verb|except_free| function releases memory that was allocated
+ using \verb|except_alloc|. The deallocation is performed using the
+ default allocator or one installed by the program.
+
+ If an object is allocated by \verb|except_alloc|, then a
+ different allocator is installed, and the object is freed using
+ \verb|except_free|, the behavior is undefined.
+
+\subsection{Implementation}
+\index{Exception component!reference implementation}
+
+Described here is a reference implementation of the exception handling
+interface that is covered in section \ref{section:exception_component}
+The reference implementation requires only a conforming ANSI C implementation.
+In particular, the actual mechanism for passing control from an exception throw
+to a catch handler is based on the standard C \verb|setjmp| macro and
+\verb|longjmp| function.
+
+\subsubsection{Overview}
+
+The core structure in the exception handling implementation is a stack that is
+composed of a mixture of two types of nodes: cleanup nodes and catch nodes.
+When an exception is thrown, the stack nodes are popped and processed starting
+with the topmost one.
+
+The nodes are efficiently allocated in automatic storage by the macros
+\verb|except_cleanup_push| and \verb|except_try_push|. These macros
+open up a new statement block and declare the node information in automatic
+storage. These objects are then pushed onto the stack. The corresponding macros
+\verb|except_cleanup_pop| and \verb|except_try_pop| pop the node off the stack
+and close the statement block.
+
+An static variable keeps track of the stack top. In the multi-threaded variant
+of the code which is based on the POSIX threading interface, there is a
+thread-specific stack top created using the thread-specific function
+pthread_key_create. Using global variables is a compromise that simplifies the
+interface; the throw functions simply ``know'' where the thread's exception
+stack is, so the context information doesn't have to be passed around.
+
+\subsubsection{Stack nodes}
+
+A node in the exception handling stack contains a pointer to the next
+node below, followed by a type field and a union which together keep
+track of the appropriate type-specific data:
+\begin{verbatim}
+ enum except_stacktype {
+ XCEPT_CLEANUP, XCEPT_CATCHER
+ };
+
+ struct except_stacknode {
+ struct except_stacknode *except_down;
+ enum except_stacktype except_type;
+ union {
+ struct except_catch *except_catcher;
+ struct except_cleanup *except_cleanup;
+ } except_info;
+ };
+\end{verbatim}
+The union overlaps pointers to structures instead of structures in order to
+save space: there is a disparity in size between a cleanup node and a catch
+node, so making them both use the same amount of space would be wasteful.
+The space saving comes at a price, because the pointers themselves take up
+extra space and time is spent initializing them. Some casting trickery
+could be used to create a stack having two different kinds of structures
+without the use of unions.
+
+\paragraph{Cleanup nodes}
+
+Cleanup nodes act as placeholders for a pointer to a cleanup handler function
+and a context pointer to be passed to that function. The type-dependent
+component of the cleanup node is declared like this:
+\begin{verbatim}
+ struct except_cleanup {
+ void (*except_func)(void *);
+ void *except_context;
+ };
+\end{verbatim}
+The cleanup handler is invoked when the node is popped during exception
+processing. A cleanup handler may also be invoked when the cleanup node is
+removed by executing \verb|except_cleanup_pop| or
+\verb|except_checked_cleanup_pop|. Whether or not this happens depends on the
+integer parameter that is documented in section
+\ref{section:except_cleanup_pop}.
+
+\paragraph{Catch nodes}
+
+The catch node structure is more complicated than the cleanup node.
+Its definition depends on two additional types, \verb|except_id_t|
+and \verb|except_t|, both of which also make play a role in the exception
+component's interface.
+\begin{verbatim}
+ typedef struct {
+ unsigned long except_group;
+ unsigned long except_code;
+ } except_id_t;
+
+ typedef struct {
+ except_id_t except_id;
+ const char *except_message;
+ void *except_dyndata;
+ } except_t;
+
+ struct except_catch {
+ const except_id_t *except_id;
+ size_t except_size;
+ except_t except_obj;
+ jmp_buf except_jmp;
+ };
+\end{verbatim}
+The \verb|except_id| member of the \verb|except_catch| structure is a pointer to the
+array of \verb|except_id_t| objects which specify what exceptions the node
+catches. The \verb|except_size| member specifies the number of elements in the array.
+Both of these values are derived directly from the arguments of the
+\verb|except_try_push| macro (see section \ref{section:except_try_push}). The
+\verb|except_obj| member provides storage for the caught exception. This member is
+the means by which the thrown exception is communicated to the try-catch region
+where it is caught. It contains the group and code identifiers, the exception
+message and, optionally, the pointer to arbitrary exception data. The
+\verb|except_jmp| member is the standard C \verb|jmp_buf|---a place for saving the
+execution context so that it's possible to pass control, via \verb|longjmp|
+from the place where an exception is thrown to the place where it is caught.
+
+If, during the search for an exception handler, a catch node is encountered
+which matches the thrown exception, the node remains the stack. The exception
+information is stored into into the node's \verb|except_obj| member and a
+\verb|longjmp| is executed to return to the try-catch region in which the node
+was allocated and pushed. Because the node is still on the stack, it's possible
+to throw another exception which is caught again by the same node. When an
+exception is thus caught, control resumes just after the \verb|except_throw|
+which placed the node onto the stack. The pointer passed into \verb|except_throw|
+is updated to point to the \verb|except_obj| member of the catch structure.
+The program can then use the portable accessor functions such as
+\verb|except_code| to gain information about the caught exception and handle it
+accordingly.
+
+\index{external names|see {functions}}
+\index{reference implementation|see {implementation}}
+\index{names|see {symbols}}
+\index{identifiers|see {symbols}}
+\index{structure names|see{tags}}
+\index{preprocessor symbols|see{macros}}
+\index{defines|see{macros}}
+\index{reserved symbols|see{symbols}}
+\index{symbols!preprocessor|see{macros}}
+\index{symbols!type names|see{typedefs}}
+\index{symbols!function names|see{functions}}
+\printindex
+
+\end{document}
diff --git a/libutil/kazlib/drivers/dict-main.c b/libutil/kazlib/drivers/dict-main.c
new file mode 100644
index 0000000..08f2e7a
--- /dev/null
+++ b/libutil/kazlib/drivers/dict-main.c
@@ -0,0 +1,300 @@
+#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;
+}
+
+static dnode_t *new_node(void *c)
+{
+ static dnode_t few[5];
+ static int count;
+
+ if (count < 5)
+ return few + count++;
+
+ return NULL;
+}
+
+static void del_node(dnode_t *n, void *c)
+{
+}
+
+static int prompt = 0;
+
+static void construct(dict_t *d)
+{
+ input_t in;
+ int done = 0;
+ dict_load_t dl;
+ dnode_t *dn;
+ char *tok1, *tok2, *val;
+ const char *key;
+ char *help =
+ "p turn prompt on\n"
+ "q finish construction\n"
+ "a <key> <val> add new entry\n";
+
+ if (!dict_isempty(d))
+ puts("warning: dictionary not empty!");
+
+ dict_load_begin(&dl, d);
+
+ while (!done) {
+ if (prompt)
+ putchar('>');
+ fflush(stdout);
+
+ if (!fgets(in, sizeof(input_t), stdin))
+ break;
+
+ switch (in[0]) {
+ case '?':
+ puts(help);
+ break;
+ case 'p':
+ prompt = 1;
+ break;
+ case 'q':
+ done = 1;
+ break;
+ case 'a':
+ if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
+ puts("what?");
+ break;
+ }
+ key = dupstring(tok1);
+ val = dupstring(tok2);
+ dn = dnode_create(val);
+
+ if (!key || !val || !dn) {
+ puts("out of memory");
+ free((void *) key);
+ free(val);
+ if (dn)
+ dnode_destroy(dn);
+ }
+
+ dict_load_next(&dl, dn, key);
+ break;
+ default:
+ putchar('?');
+ putchar('\n');
+ break;
+ }
+ }
+
+ dict_load_end(&dl);
+}
+
+int main(void)
+{
+ input_t in;
+ dict_t darray[10];
+ dict_t *d = &darray[0];
+ dnode_t *dn;
+ int i;
+ char *tok1, *tok2, *val;
+ const char *key;
+
+ char *help =
+ "a <key> <val> add value to dictionary\n"
+ "d <key> delete value from dictionary\n"
+ "l <key> lookup value in dictionary\n"
+ "( <key> lookup lower bound\n"
+ ") <key> lookup upper bound\n"
+ "# <num> switch to alternate dictionary (0-9)\n"
+ "j <num> <num> merge two dictionaries\n"
+ "f free the whole dictionary\n"
+ "k allow duplicate keys\n"
+ "c show number of entries\n"
+ "t dump whole dictionary in sort order\n"
+ "m make dictionary out of sorted items\n"
+ "p turn prompt on\n"
+ "s switch to non-functioning allocator\n"
+ "q quit";
+
+ for (i = 0; i < sizeof darray / sizeof *darray; i++)
+ dict_init(&darray[i], DICTCOUNT_T_MAX, comparef);
+
+ 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, &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 (!dict_alloc_insert(d, key, val)) {
+ puts("dict_alloc_insert failed");
+ free((void *) key);
+ free(val);
+ break;
+ }
+ break;
+ case 'd':
+ if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+ puts("what?");
+ break;
+ }
+ dn = dict_lookup(d, tok1);
+ if (!dn) {
+ puts("dict_lookup failed");
+ break;
+ }
+ val = dnode_get(dn);
+ key = dnode_getkey(dn);
+ dict_delete_free(d, dn);
+
+ free(val);
+ free((void *) key);
+ break;
+ case 'f':
+ dict_free(d);
+ break;
+ case 'l':
+ case '(':
+ case ')':
+ if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+ puts("what?");
+ break;
+ }
+ dn = 0;
+ switch (in[0]) {
+ case 'l':
+ dn = dict_lookup(d, tok1);
+ break;
+ case '(':
+ dn = dict_lower_bound(d, tok1);
+ break;
+ case ')':
+ dn = dict_upper_bound(d, tok1);
+ break;
+ }
+ if (!dn) {
+ puts("lookup failed");
+ break;
+ }
+ val = dnode_get(dn);
+ puts(val);
+ break;
+ case 'm':
+ construct(d);
+ break;
+ case 'k':
+ dict_allow_dupes(d);
+ break;
+ case 'c':
+ printf("%lu\n", (unsigned long) dict_count(d));
+ break;
+ case 't':
+ for (dn = dict_first(d); dn; dn = dict_next(d, dn)) {
+ printf("%s\t%s\n", (char *) dnode_getkey(dn),
+ (char *) dnode_get(dn));
+ }
+ break;
+ case 'q':
+ exit(0);
+ break;
+ case '\0':
+ break;
+ case 'p':
+ prompt = 1;
+ break;
+ case 's':
+ dict_set_allocator(d, new_node, del_node, NULL);
+ break;
+ case '#':
+ if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+ puts("what?");
+ break;
+ } else {
+ int dictnum = atoi(tok1);
+ if (dictnum < 0 || dictnum > 9) {
+ puts("invalid number");
+ break;
+ }
+ d = &darray[dictnum];
+ }
+ break;
+ case 'j':
+ if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
+ puts("what?");
+ break;
+ } else {
+ int dict1 = atoi(tok1), dict2 = atoi(tok2);
+ if (dict1 < 0 || dict1 > 9 || dict2 < 0 || dict2 > 9) {
+ puts("invalid number");
+ break;
+ }
+ dict_merge(&darray[dict1], &darray[dict2]);
+ }
+ break;
+ default:
+ putchar('?');
+ putchar('\n');
+ break;
+ }
+ }
+
+ return 0;
+}
diff --git a/libutil/kazlib/drivers/except-main.c b/libutil/kazlib/drivers/except-main.c
new file mode 100644
index 0000000..fdb64db
--- /dev/null
+++ b/libutil/kazlib/drivers/except-main.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <ctype.h>
+
+static void cleanup(void *arg)
+{
+ printf("cleanup(\"%s\") called\n", (char *) arg);
+}
+
+static void bottom_level(void)
+{
+ char buf[256];
+ printf("throw exception? "); fflush(stdout);
+ fgets(buf, sizeof buf, stdin);
+
+ if (buf[0] >= 0 && toupper(buf[0]) == 'Y')
+ except_throw(1, 1, "nasty exception");
+}
+
+static void top_level(void)
+{
+ except_cleanup_push(cleanup, "argument");
+ bottom_level();
+ except_cleanup_pop(0);
+}
+
+int main(int argc, char **argv)
+{
+ static const except_id_t catch[] = { { 1, 1 }, { 1, 2 } };
+ except_t *ex;
+
+ /*
+ * Nested exception ``try blocks''
+ */
+
+ /* outer */
+ except_try_push(catch, 2, &ex);
+ if (!ex) {
+ /* inner */
+ except_try_push(catch, 2, &ex);
+ if (!ex) {
+ top_level();
+ } else {
+ /* inner catch */
+ printf("caught exception (inner): \"%s\", s=%ld, c=%ld\n",
+ except_message(ex), except_group(ex), except_code(ex));
+ except_rethrow(ex);
+ }
+ except_try_pop();
+ } else {
+ /* outer catch */
+ printf("caught exception (outer): \"%s\", s=%ld, c=%ld\n",
+ except_message(ex), except_group(ex), except_code(ex));
+ }
+ except_try_pop();
+ except_throw(99, 99, "exception in main");
+ return 0;
+}
diff --git a/libutil/kazlib/drivers/hash-main.c b/libutil/kazlib/drivers/hash-main.c
new file mode 100644
index 0000000..0a08542
--- /dev/null
+++ b/libutil/kazlib/drivers/hash-main.c
@@ -0,0 +1,187 @@
+#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;
+}
diff --git a/libutil/kazlib/drivers/list-main.c b/libutil/kazlib/drivers/list-main.c
new file mode 100644
index 0000000..6f462e4
--- /dev/null
+++ b/libutil/kazlib/drivers/list-main.c
@@ -0,0 +1,152 @@
+#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;
+}
diff --git a/libutil/kazlib/drivers/sfx-main.c b/libutil/kazlib/drivers/sfx-main.c
new file mode 100644
index 0000000..fda683b
--- /dev/null
+++ b/libutil/kazlib/drivers/sfx-main.c
@@ -0,0 +1,41 @@
+#include <stdlib.h>
+
+int main(int argc, char **argv)
+{
+ char expr_buf[256];
+ char *expr, *ptr;
+ sfx_rating_t eff;
+
+ for (;;) {
+ if (argc < 2) {
+ expr = expr_buf;
+ if (fgets(expr_buf, sizeof expr_buf, stdin) == 0)
+ break;
+ if ((ptr = strchr(expr_buf, '\n')) != 0)
+ *ptr = 0;
+ } else {
+ expr = (argv++)[1];
+ if (!expr)
+ break;
+ }
+
+ if (!sfx_determine(expr, &eff)) {
+ printf("expression '%s' has a syntax error\n", expr);
+ return EXIT_FAILURE;
+ }
+
+ switch (eff) {
+ case sfx_none:
+ printf("expression '%s' has no side effects\n", expr);
+ break;
+ case sfx_potential:
+ printf("expression '%s' may have side effects\n", expr);
+ break;
+ case sfx_certain:
+ printf("expression '%s' has side effects\n", expr);
+ break;
+ }
+ }
+
+ return 0;
+}
diff --git a/libutil/kazlib/except.c b/libutil/kazlib/except.c
new file mode 100644
index 0000000..c915dda
--- /dev/null
+++ b/libutil/kazlib/except.c
@@ -0,0 +1,347 @@
+/*
+ * Portable Exception Handling for ANSI C.
+ * Copyright (C) 1999 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.
+ *
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <limits.h>
+#include "except.h"
+
+#define XCEPT_BUFFER_SIZE 1024
+
+#define group except_group
+#define code except_code
+#define id except_id
+#define message except_message
+#define dyndata except_dyndata
+#define func except_func
+#define context except_context
+#define id except_id
+#define size except_size
+#define obj except_obj
+#define jmp except_jmp
+#define down except_down
+#define type except_type
+#define catcher except_catcher
+#define cleanup except_cleanup
+#define info except_info
+
+#ifdef KAZLIB_POSIX_THREADS
+
+#include <pthread.h>
+
+static pthread_mutex_t init_mtx = PTHREAD_MUTEX_INITIALIZER;
+static int init_counter;
+static pthread_key_t top_key;
+static pthread_key_t uh_key;
+static pthread_key_t alloc_key;
+static pthread_key_t dealloc_key;
+static void unhandled_catcher(except_t *);
+
+#define get_top() ((struct except_stacknode *) pthread_getspecific(top_key))
+#define set_top(T) (pthread_setspecific(top_key, (T)), (void)((T) == (struct except_stacknode *) 0))
+#define set_catcher(C) (pthread_setspecific(uh_key, (void *) (C)), (void)((C) == (void (*)(except_t *)) 0))
+#define set_alloc(A) (pthread_setspecific(alloc_key, (void *) (A)), (void)((A) == (void *(*)(size_t)) 0))
+#define set_dealloc(D) (pthread_setspecific(dealloc_key, (void *) (D)), (void)((D) == (void (*)(void *)) 0))
+
+static void (*get_catcher(void))(except_t *)
+{
+ void (*catcher)(except_t *) = (void (*)(except_t *)) pthread_getspecific(uh_key);
+ return (catcher == 0) ? unhandled_catcher : catcher;
+}
+
+static void *(*get_alloc(void))(size_t)
+{
+ void *(*alloc)(size_t) = (void *(*)(size_t)) pthread_getspecific(alloc_key);
+ return (alloc == 0) ? malloc : alloc;
+}
+
+static void (*get_dealloc(void))(void *)
+{
+ void (*dealloc)(void *) = (void (*)(void *)) pthread_getspecific(dealloc_key);
+ return (dealloc == 0) ? free : dealloc;
+}
+
+int except_init(void)
+{
+ int retval = 1;
+
+ pthread_mutex_lock(&init_mtx);
+
+ assert (init_counter < INT_MAX);
+
+ if (init_counter++ == 0) {
+ int top_ok = (pthread_key_create(&top_key, 0) == 0);
+ int uh_ok = (pthread_key_create(&uh_key, 0) == 0);
+ int alloc_ok = (pthread_key_create(&alloc_key, 0) == 0);
+ int dealloc_ok = (pthread_key_create(&dealloc_key, 0) == 0);
+
+ if (!top_ok || !uh_ok || !alloc_ok || !dealloc_ok) {
+ retval = 0;
+ init_counter = 0;
+ if (top_ok)
+ pthread_key_delete(top_key);
+ if (uh_ok)
+ pthread_key_delete(uh_key);
+ if (alloc_ok)
+ pthread_key_delete(alloc_key);
+ if (dealloc_ok)
+ pthread_key_delete(dealloc_key);
+ }
+ }
+
+ pthread_mutex_unlock(&init_mtx);
+
+ return retval;
+}
+
+void except_deinit(void)
+{
+ pthread_mutex_lock(&init_mtx);
+
+ assert (init_counter > 0);
+
+ if (--init_counter == 0) {
+ pthread_key_delete(top_key);
+ pthread_key_delete(uh_key);
+ pthread_key_delete(alloc_key);
+ pthread_key_delete(dealloc_key);
+ }
+
+ pthread_mutex_unlock(&init_mtx);
+}
+
+#else /* no thread support */
+
+static int init_counter;
+static void unhandled_catcher(except_t *);
+static void (*uh_catcher_ptr)(except_t *) = unhandled_catcher;
+static void *(*allocator)(size_t) = malloc;
+static void (*deallocator)(void *) = free;
+static struct except_stacknode *stack_top;
+
+#define get_top() (stack_top)
+#define set_top(T) (stack_top = (T))
+#define get_catcher() (uh_catcher_ptr)
+#define set_catcher(C) (uh_catcher_ptr = (C))
+#define get_alloc() (allocator)
+#define set_alloc(A) (allocator = (A))
+#define get_dealloc() (deallocator)
+#define set_dealloc(D) (deallocator = (D))
+
+int except_init(void)
+{
+ assert (init_counter < INT_MAX);
+ init_counter++;
+ return 1;
+}
+
+void except_deinit(void)
+{
+ assert (init_counter > 0);
+ init_counter--;
+}
+
+#endif
+
+
+static int match(const volatile except_id_t *thrown, const except_id_t *caught)
+{
+ int group_match = (caught->group == XCEPT_GROUP_ANY || caught->group == thrown->group);
+ int code_match = (caught->code == XCEPT_CODE_ANY || caught->code == thrown->code);
+
+ return group_match && code_match;
+}
+
+static void do_throw(except_t *except)
+{
+ struct except_stacknode *top;
+
+ assert (except->id.group != 0 && except->id.code != 0);
+
+ for (top = get_top(); top != 0; top = top->down) {
+ if (top->type == XCEPT_CLEANUP) {
+ top->info.cleanup->func(top->info.cleanup->context);
+ } else {
+ struct except_catch *catcher = top->info.catcher;
+ const except_id_t *pi = catcher->id;
+ size_t i;
+
+ assert (top->type == XCEPT_CATCHER);
+ except_free(catcher->obj.dyndata);
+
+ for (i = 0; i < catcher->size; pi++, i++) {
+ if (match(&except->id, pi)) {
+ catcher->obj = *except;
+ set_top(top);
+ longjmp(catcher->jmp, 1);
+ }
+ }
+ }
+ }
+
+ set_top(top);
+ get_catcher()(except); /* unhandled exception */
+ abort();
+}
+
+static void unhandled_catcher(except_t *except)
+{
+ fprintf(stderr, "Unhandled exception (\"%s\", group=%ld, code=%ld)\n",
+ except->message, except->id.group, except->id.code);
+ abort();
+}
+
+static void stack_push(struct except_stacknode *node)
+{
+ node->down = get_top();
+ set_top(node);
+}
+
+void except_setup_clean(struct except_stacknode *esn,
+ struct except_cleanup *ecl, void (*cleanf)(void *), void *context)
+{
+ esn->type = XCEPT_CLEANUP;
+ ecl->func = cleanf;
+ ecl->context = context;
+ esn->info.cleanup = ecl;
+ stack_push(esn);
+}
+
+void except_setup_try(struct except_stacknode *esn,
+ struct except_catch *ech, const except_id_t id[], size_t size)
+{
+ ech->id = id;
+ ech->size = size;
+ ech->obj.dyndata = 0;
+ esn->type = XCEPT_CATCHER;
+ esn->info.catcher = ech;
+ stack_push(esn);
+}
+
+struct except_stacknode *except_pop(void)
+{
+ struct except_stacknode *top = get_top();
+ set_top(top->down);
+ return top;
+}
+
+void except_rethrow(except_t *except)
+{
+ struct except_stacknode *top = get_top();
+ assert (top != 0);
+ assert (top->type == XCEPT_CATCHER);
+ assert (&top->info.catcher->obj == except);
+ set_top(top->down);
+ do_throw(except);
+}
+
+void except_throw(long group, long code, const char *msg)
+{
+ except_t except;
+
+ except.id.group = group;
+ except.id.code = code;
+ except.message = msg;
+ except.dyndata = 0;
+
+ do_throw(&except);
+}
+
+void except_throwd(long group, long code, const char *msg, void *data)
+{
+ except_t except;
+
+ except.id.group = group;
+ except.id.code = code;
+ except.message = msg;
+ except.dyndata = data;
+
+ do_throw(&except);
+}
+
+void except_throwf(long group, long code, const char *fmt, ...)
+{
+ char *buf = except_alloc(XCEPT_BUFFER_SIZE);
+ va_list vl;
+
+ va_start (vl, fmt);
+ vsprintf(buf, fmt, vl);
+ va_end (vl);
+ except_throwd(group, code, buf, buf);
+}
+
+void (*except_unhandled_catcher(void (*new_catcher)(except_t *)))(except_t *)
+{
+ void (*old_catcher)(except_t *) = get_catcher();
+ set_catcher(new_catcher);
+ return old_catcher;
+}
+
+#undef except_code
+#undef except_group
+#undef except_message
+#undef except_data
+
+unsigned long except_code(except_t *ex)
+{
+ return ex->id.code;
+}
+
+unsigned long except_group(except_t *ex)
+{
+ return ex->id.group;
+}
+
+const char *except_message(except_t *ex)
+{
+ return ex->message;
+}
+
+void *except_data(except_t *ex)
+{
+ return ex->dyndata;
+}
+
+void *except_take_data(except_t *ex)
+{
+ void *data = ex->dyndata;
+ ex->dyndata = 0;
+ return data;
+}
+
+void except_set_allocator(void *(*alloc)(size_t), void (*dealloc)(void *))
+{
+ set_alloc(alloc);
+ set_dealloc(dealloc);
+}
+
+void *except_alloc(size_t size)
+{
+ void *ptr = get_alloc()(size);
+
+ if (ptr == 0)
+ except_throw(XCEPT_BAD_ALLOC, 0, "out of memory");
+ return ptr;
+}
+
+void except_free(void *ptr)
+{
+ get_dealloc()(ptr);
+}
diff --git a/libutil/kazlib/except.h b/libutil/kazlib/except.h
new file mode 100644
index 0000000..3131fb9
--- /dev/null
+++ b/libutil/kazlib/except.h
@@ -0,0 +1,147 @@
+/*
+ * Portable Exception Handling for ANSI C.
+ * Copyright (C) 1999 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.
+ *
+ */
+
+#ifndef XCEPT_H
+#define XCEPT_H
+
+#include <setjmp.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#define XCEPT_GROUP_ANY 0
+#define XCEPT_CODE_ANY 0
+#define XCEPT_BAD_ALLOC 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum { except_no_call, except_call };
+
+typedef struct {
+ unsigned long except_group;
+ unsigned long except_code;
+} except_id_t;
+
+typedef struct {
+ except_id_t volatile except_id;
+ const char *volatile except_message;
+ void *volatile except_dyndata;
+} except_t;
+
+struct except_cleanup {
+ void (*except_func)(void *);
+ void *except_context;
+};
+
+struct except_catch {
+ const except_id_t *except_id;
+ size_t except_size;
+ except_t except_obj;
+ jmp_buf except_jmp;
+};
+
+enum except_stacktype {
+ XCEPT_CLEANUP, XCEPT_CATCHER
+};
+
+struct except_stacknode {
+ struct except_stacknode *except_down;
+ enum except_stacktype except_type;
+ union {
+ struct except_catch *except_catcher;
+ struct except_cleanup *except_cleanup;
+ } except_info;
+};
+
+/* private functions made external so they can be used in macros */
+void except_setup_clean(struct except_stacknode *,
+ struct except_cleanup *, void (*)(void *), void *);
+void except_setup_try(struct except_stacknode *,
+ struct except_catch *, const except_id_t [], size_t);
+struct except_stacknode *except_pop(void);
+
+/* public interface functions */
+int except_init(void);
+void except_deinit(void);
+void except_rethrow(except_t *);
+void except_throw(long, long, const char *);
+void except_throwd(long, long, const char *, void *);
+void except_throwf(long, long, const char *, ...);
+void (*except_unhandled_catcher(void (*)(except_t *)))(except_t *);
+unsigned long except_code(except_t *);
+unsigned long except_group(except_t *);
+const char *except_message(except_t *);
+void *except_data(except_t *);
+void *except_take_data(except_t *);
+void except_set_allocator(void *(*)(size_t), void (*)(void *));
+void *except_alloc(size_t);
+void except_free(void *);
+
+#define except_code(E) ((E)->except_id.except_code)
+#define except_group(E) ((E)->except_id.except_group)
+#define except_message(E) ((E)->except_message)
+#define except_data(E) ((E)->except_dyndata)
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+ * void except_cleanup_push(void (*)(void *), void *);
+ * void except_cleanup_pop(int);
+ * void except_checked_cleanup_pop(void (*)(void *), int);
+ * void except_try_push(const except_id_t [], size_t, except_t **);
+ * void except_try_pop(void);
+ */
+
+#define except_cleanup_push(F, C) \
+ { \
+ struct except_stacknode except_sn; \
+ struct except_cleanup except_cl; \
+ except_setup_clean(&except_sn, &except_cl, F, C)
+
+#define except_cleanup_pop(E) \
+ except_pop(); \
+ if (E) \
+ except_cl.except_func(except_cl.except_context); \
+ }
+
+#define except_checked_cleanup_pop(F, E) \
+ except_pop(); \
+ assert (except_cl.except_func == (F)); \
+ if (E) \
+ except_cl.except_func(except_cl.except_context); \
+ }
+
+#define except_try_push(ID, NUM, PPE) \
+ { \
+ struct except_stacknode except_sn; \
+ struct except_catch except_ch; \
+ except_setup_try(&except_sn, &except_ch, ID, NUM); \
+ if (setjmp(except_ch.except_jmp)) \
+ *(PPE) = &except_ch.except_obj; \
+ else \
+ *(PPE) = 0
+
+#define except_try_pop() \
+ except_free(except_ch.except_obj.except_dyndata); \
+ except_pop(); \
+ }
+
+#endif
diff --git a/libutil/kazlib/hash.c b/libutil/kazlib/hash.c
new file mode 100644
index 0000000..2140e66
--- /dev/null
+++ b/libutil/kazlib/hash.c
@@ -0,0 +1,837 @@
+/*
+ * 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.
+ *
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <assert.h>
+#include <string.h>
+#define HASH_IMPLEMENTATION
+#include "hash.h"
+
+#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);
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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;
+}
+
+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;
+}
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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
+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
+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.
+ */
+
+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
+ */
+
+hnode_t *hnode_init(hnode_t *hnode, void *data)
+{
+ hnode->data = data;
+ hnode->next = NULL;
+ return hnode;
+}
+
+/*
+ * Destroy a dynamically allocated node.
+ */
+
+void hnode_destroy(hnode_t *hnode)
+{
+ free(hnode);
+}
+
+#undef hnode_put
+void hnode_put(hnode_t *node, void *data)
+{
+ node->data = data;
+}
+
+#undef hnode_get
+void *hnode_get(hnode_t *node)
+{
+ return node->data;
+}
+
+#undef hnode_getkey
+const void *hnode_getkey(hnode_t *node)
+{
+ return node->key;
+}
+
+#undef hash_count
+hashcount_t hash_count(hash_t *hash)
+{
+ return hash->nodecount;
+}
+
+#undef hash_size
+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);
+}
diff --git a/libutil/kazlib/hash.h b/libutil/kazlib/hash.h
new file mode 100644
index 0000000..e8213f7
--- /dev/null
+++ b/libutil/kazlib/hash.h
@@ -0,0 +1,238 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef HASH_H
+#define HASH_H
+
+#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
+
+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;
+
+extern hash_t *hash_create(hashcount_t, hash_comp_t, hash_fun_t);
+extern void hash_set_allocator(hash_t *, hnode_alloc_t, hnode_free_t, void *);
+extern void hash_destroy(hash_t *);
+extern void hash_free_nodes(hash_t *);
+extern void hash_free(hash_t *);
+extern hash_t *hash_init(hash_t *, hashcount_t, hash_comp_t,
+ hash_fun_t, hnode_t **, hashcount_t);
+extern void hash_insert(hash_t *, hnode_t *, const void *);
+extern hnode_t *hash_lookup(hash_t *, const void *);
+extern hnode_t *hash_delete(hash_t *, hnode_t *);
+extern int hash_alloc_insert(hash_t *, const void *, void *);
+extern void hash_delete_free(hash_t *, hnode_t *);
+
+extern void hnode_put(hnode_t *, void *);
+extern void *hnode_get(hnode_t *);
+extern const void *hnode_getkey(hnode_t *);
+extern hashcount_t hash_count(hash_t *);
+extern hashcount_t hash_size(hash_t *);
+
+extern int hash_isfull(hash_t *);
+extern int hash_isempty(hash_t *);
+
+extern void hash_scan_begin(hscan_t *, hash_t *);
+extern hnode_t *hash_scan_next(hscan_t *);
+extern hnode_t *hash_scan_delete(hash_t *, hnode_t *);
+extern void hash_scan_delfree(hash_t *, hnode_t *);
+
+extern int hash_verify(hash_t *);
+
+extern hnode_t *hnode_create(void *);
+extern hnode_t *hnode_init(hnode_t *, void *);
+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/libutil/kazlib/list.c b/libutil/kazlib/list.c
new file mode 100644
index 0000000..818b427
--- /dev/null
+++ b/libutil/kazlib/list.c
@@ -0,0 +1,766 @@
+/*
+ * 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.
+ *
+ */
+
+
+#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)
+
+/*
+ * 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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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.
+ */
+
+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));
+}
+
+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);
+}
+
+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));
+}
+
+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
+ */
+
+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
+ */
+
+int list_isempty(list_t *list)
+{
+ return list->nodecount == 0;
+}
+
+/*
+ * Return 1 if the list is full, 0 otherwise
+ * Permitted only on bounded lists.
+ */
+
+int list_isfull(list_t *list)
+{
+ return list->nodecount == list->maxcount;
+}
+
+/*
+ * Check if the node pool is empty.
+ */
+
+int lnode_pool_isempty(lnodepool_t *pool)
+{
+ return (pool->fre == NULL);
+}
+
+/*
+ * Add the given node at the end of the list
+ */
+
+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.
+ */
+
+void list_prepend(list_t *list, lnode_t *node)
+{
+ list_ins_after(list, node, &list->nilnode);
+}
+
+/*
+ * Retrieve the first node of the list
+ */
+
+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
+ */
+
+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
+ */
+
+listcount_t list_count(list_t *list)
+{
+ return list->nodecount;
+}
+
+/*
+ * Remove the first node from the list and return it.
+ */
+
+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.
+ */
+
+lnode_t *list_del_last(list_t *list)
+{
+ return list_delete(list, list->nilnode.prev);
+}
+
+
+/*
+ * Associate a data item with the given node.
+ */
+
+void lnode_put(lnode_t *lnode, void *data)
+{
+ lnode->data = data;
+}
+
+/*
+ * Retrieve the data item associated with the node.
+ */
+
+void *lnode_get(lnode_t *lnode)
+{
+ return lnode->data;
+}
+
+/*
+ * Retrieve the node's successor. If there is no successor,
+ * NULL is returned.
+ */
+
+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().
+ */
+
+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.
+ */
+
+int lnode_is_in_a_list(lnode_t *lnode)
+{
+ return (lnode->next != NULL || lnode->prev != NULL);
+}
+
+
+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;
+}
diff --git a/libutil/kazlib/list.h b/libutil/kazlib/list.h
new file mode 100644
index 0000000..97abc2f
--- /dev/null
+++ b/libutil/kazlib/list.h
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef LIST_H
+#define LIST_H
+
+#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;
+
+lnode_t *lnode_create(void *);
+lnode_t *lnode_init(lnode_t *, void *);
+void lnode_destroy(lnode_t *);
+void lnode_put(lnode_t *, void *);
+void *lnode_get(lnode_t *);
+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
+
+lnodepool_t *lnode_pool_init(lnodepool_t *, lnode_t *, listcount_t);
+lnodepool_t *lnode_pool_create(listcount_t);
+void lnode_pool_destroy(lnodepool_t *);
+lnode_t *lnode_borrow(lnodepool_t *, void *);
+void lnode_return(lnodepool_t *, lnode_t *);
+int lnode_pool_isempty(lnodepool_t *);
+int lnode_pool_isfrom(lnodepool_t *, lnode_t *);
+
+list_t *list_init(list_t *, listcount_t);
+list_t *list_create(listcount_t);
+void list_destroy(list_t *);
+void list_destroy_nodes(list_t *);
+void list_return_nodes(list_t *, lnodepool_t *);
+
+listcount_t list_count(list_t *);
+int list_isempty(list_t *);
+int list_isfull(list_t *);
+int list_contains(list_t *, lnode_t *);
+
+void list_append(list_t *, lnode_t *);
+void list_prepend(list_t *, lnode_t *);
+void list_ins_before(list_t *, lnode_t *, lnode_t *);
+void list_ins_after(list_t *, lnode_t *, lnode_t *);
+
+lnode_t *list_first(list_t *);
+lnode_t *list_last(list_t *);
+lnode_t *list_next(list_t *, lnode_t *);
+lnode_t *list_prev(list_t *, lnode_t *);
+
+lnode_t *list_del_first(list_t *);
+lnode_t *list_del_last(list_t *);
+lnode_t *list_delete(list_t *, lnode_t *);
+
+void list_process(list_t *, void *, void (*)(list_t *, lnode_t *, void *));
+
+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 */
+
+void list_extract(list_t *, list_t *, lnode_t *, lnode_t *);
+void list_transfer(list_t *, list_t *, lnode_t *first);
+void list_merge(list_t *, list_t *, int (const void *, const void *));
+void list_sort(list_t *, int (const void *, const void *));
+lnode_t *list_find(list_t *, const void *, int (const void *, const void *));
+int list_is_sorted(list_t *, int (const void *, const void *));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libutil/kazlib/sfx.c b/libutil/kazlib/sfx.c
new file mode 100644
index 0000000..829a53d
--- /dev/null
+++ b/libutil/kazlib/sfx.c
@@ -0,0 +1,1138 @@
+/*
+ * SFX---A utility which tries to determine whether a given C expression
+ * is free of side effects. This can be used for verifying that macros which
+ * expand their arguments more than once are not being accidentally misused.
+ *
+ * Copyright (C) 1999 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.
+ *
+ */
+
+#include <ctype.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include "except.h"
+#include "sfx.h"
+#include "hash.h"
+#ifdef KAZLIB_POSIX_THREADS
+#include <pthread.h>
+#endif
+
+/*
+ * Exceptions
+ */
+
+#define SFX_EX 0x34DB9C4A
+#define SFX_SYNERR 1
+
+/*
+ * Cache entry
+ */
+
+typedef struct {
+ hnode_t node;
+ const char *expr;
+ sfx_rating_t eff;
+} sfx_entry_t;
+
+/*
+ * Parsing context structure
+ */
+
+typedef struct {
+ const unsigned char *start;
+ const unsigned char *input;
+ size_t size;
+ sfx_rating_t eff;
+} context_t;
+
+/*
+ * Declarator type: abstract, concrete or both
+ */
+
+typedef enum {
+ decl_abstract, decl_concrete, decl_both
+} decl_t;
+
+static void init_context(context_t *ctx, const unsigned char *expr)
+{
+ ctx->input = ctx->start = expr;
+ ctx->size = strlen((const char *) expr) + 1;
+ ctx->eff = sfx_none;
+}
+
+static void assign_context(context_t *copy, context_t *orig)
+{
+ *copy = *orig;
+}
+
+static void set_effect(context_t *ctx, sfx_rating_t eff)
+{
+ assert (eff == sfx_none || eff == sfx_potential || eff == sfx_certain);
+
+ if (eff > ctx->eff)
+ ctx->eff = eff;
+}
+
+static void reset_effect(context_t *ctx)
+{
+ ctx->eff = sfx_none;
+}
+
+static sfx_rating_t get_effect(context_t *ctx)
+{
+ return ctx->eff;
+}
+
+static int skip_ws(context_t *expr)
+{
+ while (*expr->input != 0 && isspace(*expr->input))
+ expr->input++;
+
+ return (*expr->input == 0);
+}
+
+static int get_next(context_t *expr)
+{
+ int ret = *expr->input;
+ if (ret)
+ expr->input++;
+ return ret;
+}
+
+static int get_next_skip_ws(context_t *expr)
+{
+ if (!skip_ws(expr))
+ return *expr->input++;
+ return 0;
+}
+
+static const unsigned char *get_ptr(context_t *expr)
+{
+ return expr->input;
+}
+
+static void skip_n(context_t *ctx, size_t n)
+{
+ assert ((size_t) (ctx->input - ctx->start) <= ctx->size - n);
+ ctx->input += n;
+}
+
+static void put_back(context_t *expr, int ch)
+{
+ if (ch)
+ expr->input--;
+}
+
+static int peek_next(context_t *expr)
+{
+ return *expr->input;
+}
+
+static void syntax_error(void)
+{
+ except_throw(SFX_EX, SFX_SYNERR, "syntax_error");
+}
+
+static void match_hard(context_t *expr, int match)
+{
+ int ch = get_next(expr);
+ if (ch != match)
+ syntax_error();
+}
+
+static void chk_comma(context_t *);
+
+static void skip_ident(context_t *expr)
+{
+ int ch = get_next(expr);
+
+ if (!isalpha(ch) && ch != '_')
+ syntax_error();
+
+ do {
+ ch = get_next(expr);
+ } while (isalnum(ch) || ch == '_');
+
+ put_back(expr, ch);
+}
+
+static void skip_constant(context_t *expr)
+{
+ int ch = get_next(expr);
+
+ assert (isdigit(ch) || ch == '.');
+
+ do {
+ ch = get_next(expr);
+ if (ch == 'e' || ch == 'E') {
+ ch = get_next(expr);
+ if (ch == '+' || ch == '-') {
+ ch = get_next(expr);
+ if (!isdigit(ch))
+ syntax_error();
+ }
+ }
+ } while (ch != 0 && (isalnum(ch) || ch == '.'));
+
+ put_back(expr, ch);
+}
+
+static void skip_strlit(context_t *expr)
+{
+ int ch = get_next(expr);
+
+ assert (ch == '"');
+
+ do {
+ ch = get_next(expr);
+ if (ch == '\\') {
+ get_next(expr);
+ continue;
+ }
+ } while (ch != 0 && ch != '"');
+
+ if (ch != '"')
+ syntax_error();
+}
+
+static void skip_charlit(context_t *expr)
+{
+ int ch = get_next(expr);
+
+ assert (ch == '\'');
+
+ do {
+ ch = get_next(expr);
+ if (ch == '\\') {
+ get_next(expr);
+ continue;
+ }
+ } while (ch != 0 && ch != '\'');
+
+ if (ch != '\'')
+ syntax_error();
+}
+
+static void chk_spec_qual_list(context_t *expr)
+{
+ skip_ws(expr);
+ skip_ident(expr);
+
+ for (;;) {
+ int ch;
+
+ skip_ws(expr);
+ ch = peek_next(expr);
+
+ if (!isalpha(ch) && ch != '_')
+ break;
+
+ skip_ident(expr);
+ }
+}
+
+static int speculate(void (*chk_func)(context_t *), context_t *expr, context_t *copy, int nextchar)
+{
+ static const except_id_t catch[] = { { SFX_EX, XCEPT_CODE_ANY } };
+ except_t *ex;
+ volatile int result = 0;
+ assign_context(copy, expr);
+
+ except_try_push(catch, 1, &ex);
+
+ if (ex == 0) {
+ chk_func(copy);
+ if (nextchar) {
+ skip_ws(copy);
+ match_hard(copy, nextchar);
+ }
+ result = 1;
+ }
+
+ except_try_pop();
+
+ return result;
+}
+
+static void chk_pointer_opt(context_t *expr)
+{
+ for (;;) {
+ int ch = get_next_skip_ws(expr);
+
+ if (ch != '*') {
+ put_back(expr, ch);
+ break;
+ }
+
+ skip_ws(expr);
+
+ ch = peek_next(expr);
+
+ if (ch == '*')
+ continue;
+ if (!isalpha(ch) && ch != '_')
+ break;
+
+ skip_ident(expr);
+ }
+}
+
+static void chk_decl(context_t *, decl_t);
+
+static void chk_parm_decl(context_t *expr)
+{
+ chk_spec_qual_list(expr);
+ chk_decl(expr, decl_both);
+}
+
+static void chk_parm_type_list(context_t *expr)
+{
+ for (;;) {
+ int ch;
+
+ chk_parm_decl(expr);
+
+ ch = get_next_skip_ws(expr);
+
+ if (ch != ',') {
+ put_back(expr, ch);
+ break;
+ }
+
+ ch = get_next_skip_ws(expr);
+
+ if (ch == '.') {
+ match_hard(expr, '.');
+ match_hard(expr, '.');
+ break;
+ }
+
+ put_back(expr, ch);
+ }
+}
+
+static void chk_conditional(context_t *);
+
+static void chk_direct_decl(context_t *expr, decl_t type)
+{
+ for (;;) {
+ int ch = get_next_skip_ws(expr);
+
+ if (ch == '(') {
+ skip_ws(expr);
+ ch = peek_next(expr);
+ if (ch == '*' || ch == '(' || ch == '[')
+ chk_decl(expr, type);
+ else if (isalpha(ch) || ch == '_')
+ chk_parm_type_list(expr);
+ match_hard(expr, ')');
+ } else if (ch == '[') {
+ skip_ws(expr);
+ ch = peek_next(expr);
+ if (ch != ']')
+ chk_conditional(expr);
+ match_hard(expr, ']');
+ } else if ((type == decl_concrete || type == decl_both) && (isalpha(ch) || ch == '_')) {
+ put_back(expr, ch);
+ skip_ident(expr);
+ break;
+ } else {
+ put_back(expr, ch);
+ break;
+ }
+ }
+}
+
+static void chk_decl(context_t *expr, decl_t type)
+{
+ int ch;
+ chk_pointer_opt(expr);
+ skip_ws(expr);
+ ch = peek_next(expr);
+ if (ch == '[' || ch == '(' || ((type == decl_concrete || type == decl_both) && (isalpha(ch) || ch == '_'))) {
+ chk_direct_decl(expr, type);
+ }
+}
+
+static void chk_typename(context_t *expr)
+{
+ chk_spec_qual_list(expr);
+ chk_decl(expr, decl_abstract);
+}
+
+static void chk_primary(context_t *expr)
+{
+ int ch = peek_next(expr);
+
+ if (ch == 'L') {
+ get_next(expr);
+ ch = peek_next(expr);
+
+ if (ch == '\'') {
+ skip_charlit(expr);
+ return;
+ }
+
+ if (ch == '"') {
+ skip_strlit(expr);
+ return;
+ }
+
+ put_back(expr, 'L');
+ ch = 'L';
+ }
+
+ if (isalpha(ch) || ch == '_') {
+ skip_ident(expr);
+ return;
+ }
+
+ if (isdigit(ch) || ch == '.') {
+ skip_constant(expr);
+ return;
+ }
+
+ if (ch == '(') {
+ get_next(expr);
+ chk_comma(expr);
+ match_hard(expr, ')');
+ return;
+ }
+
+ if (ch == '\'') {
+ skip_charlit(expr);
+ return;
+ }
+
+ if (ch == '"') {
+ skip_strlit(expr);
+ return;
+ }
+
+ syntax_error();
+}
+
+static void chk_postfix(context_t *expr)
+{
+ chk_primary(expr);
+
+ for (;;) {
+ int ch = get_next_skip_ws(expr);
+
+ switch (ch) {
+ case '[':
+ chk_comma(expr);
+ skip_ws(expr);
+ match_hard(expr, ']');
+ continue;
+ case '(':
+ set_effect(expr, sfx_potential);
+ ch = get_next_skip_ws(expr);
+
+ if (ch != ')') {
+ put_back(expr, ch);
+ /* clever hack: parse non-empty argument list as comma expression */
+ chk_comma(expr);
+ ch = get_next_skip_ws(expr);
+ }
+
+ if (ch != ')')
+ syntax_error();
+
+ continue;
+ case '.':
+ skip_ws(expr);
+ skip_ident(expr);
+ continue;
+ case '-':
+ ch = get_next(expr);
+
+ if (ch != '-' && ch != '>') {
+ put_back(expr, ch);
+ put_back(expr, '-');
+ break;
+ }
+
+ if (ch == '>') {
+ skip_ws(expr);
+ skip_ident(expr);
+ continue;
+ }
+
+ set_effect(expr, sfx_certain);
+ continue;
+ case '+':
+ ch = get_next(expr);
+ if (ch != '+') {
+ put_back(expr, ch);
+ put_back(expr, '+');
+ break;
+ }
+
+ set_effect(expr, sfx_certain);
+ continue;
+ default:
+ put_back(expr, ch);
+ break;
+ }
+ break;
+ }
+}
+
+static void chk_cast(context_t *);
+
+static void chk_unary(context_t *expr)
+{
+ for (;;) {
+ int nscan, ch = get_next_skip_ws(expr);
+
+ switch (ch) {
+ case '+':
+ ch = get_next(expr);
+ if (ch == '+')
+ set_effect(expr, sfx_certain);
+ else
+ put_back(expr, ch);
+ chk_cast(expr);
+ break;
+ case '-':
+ ch = get_next(expr);
+ if (ch == '-')
+ set_effect(expr, sfx_certain);
+ else
+ put_back(expr, ch);
+ chk_cast(expr);
+ break;
+ case '&': case '*': case '~': case '!':
+ chk_cast(expr);
+ break;
+ case 's':
+ put_back(expr, ch);
+ nscan = 0;
+ sscanf((const char *) get_ptr(expr), "sizeof%*1[^a-z0-9_]%n", &nscan);
+
+ if (nscan == 7 || strcmp((const char *) get_ptr(expr), "sizeof") == 0) {
+ sfx_rating_t eff = get_effect(expr);
+
+ skip_n(expr, 6);
+
+ ch = get_next_skip_ws(expr);
+
+ if (ch == '(') {
+ context_t comma, type;
+ int iscomma = speculate(chk_comma, expr, &comma, ')');
+ int istype = speculate(chk_typename, expr, &type, ')');
+
+ if (!iscomma && !istype)
+ syntax_error();
+
+ if (iscomma) {
+ context_t unary;
+ put_back(expr, ch);
+ if (speculate(chk_unary, expr, &unary, 0)) {
+ assign_context(expr, &unary);
+ istype = 0;
+ }
+ }
+
+ if (istype)
+ assign_context(expr, &type);
+ } else {
+ put_back(expr, ch);
+ chk_unary(expr);
+ }
+
+ reset_effect(expr);
+ set_effect(expr, eff);
+ break;
+ }
+ chk_postfix(expr);
+ break;
+ default:
+ put_back(expr, ch);
+ chk_postfix(expr);
+ break;
+ }
+
+ break;
+ }
+}
+
+static void chk_cast(context_t *expr)
+{
+ enum {
+ parexpr, /* parenthesized expression */
+ partype, /* parenthesized type name */
+ parambig, /* ambiguity between paren expr and paren type name */
+ unary, /* unary expression */
+ plunary, /* unary expression with leading plus or minus */
+ other /* none of the above, or even end of input */
+ } curr = partype, old = partype, peek = partype;
+
+ /* history for backtracking: two cast expression elements back */
+ context_t old_expr = { 0 }, cur_expr = { 0 };
+
+ for (;;) {
+ context_t type, comma, unr;
+ int ch = get_next_skip_ws(expr);
+
+ /*
+ * Determine what the next bit of input is: parenthesized type name,
+ * expression, unary expression or what? Speculative parsing is used
+ * to test several hypotheses. For example, something like
+ * (X)(Y) ^ 1 is seen, it will be turned, by subsequent iterations of
+ * this loop, into the codes: parambig, parambig, other.
+ */
+
+ if (ch == '(') {
+ int istype = speculate(chk_typename, expr, &type, ')');
+ int iscomma = speculate(chk_comma, expr, &comma, ')');
+
+ switch (istype << 1 | iscomma) {
+ case 0:
+ ch = get_next_skip_ws(expr);
+ if (ch == ')')
+ peek = other; /* empty parentheses */
+ else
+ syntax_error();
+ break;
+ case 1:
+ peek = parexpr;
+ break;
+ case 2:
+ peek = partype;
+ break;
+ case 3:
+ peek = parambig;
+ break;
+ }
+ put_back(expr, ch);
+ } else if (ch == 0) {
+ peek = other;
+ } else {
+ put_back(expr, ch);
+ if (speculate(chk_unary, expr, &unr, 0)) {
+ peek = (ch == '+' || ch == '-' || ch == '*' || ch == '&') ? plunary : unary;
+ } else {
+ peek = other;
+ }
+ }
+
+ /*
+ * Okay, now we have an idea what is coming in the input. We make some
+ * sensible decision based on this and the thing we parsed previously.
+ * Either the parsing continues to grab more parenthesized things, or
+ * some decision is made to parse out the suffix material sensibly and
+ * terminate. Backtracking is used up to two elements back. For
+ * example in the case of (X)(Y) ^ 1 (parambig, parambig, other) it's
+ * necessary, upon seeing ^ 1 (other) to go back to second to last
+ * ambigous parenthesized element (X) and terminate by parsing the
+ * (X)(Y) as a postfix expression. It cannot be a cast, because ^1
+ * isn't an expression. Unary expressions that start with + or -
+ * create an interesting ambiguity. Is (X)(Y) + 1 the addition of 1 to
+ * the result of the call to function X with parameter Y? Or is it the
+ * unary expression + 1 cast to type Y and X? The safer assumption is
+ * to go with the function call hypothesis, since that's the
+ * interpretation that may have side effects.
+ */
+
+ switch (curr) {
+ case parexpr: /* impossible cases */
+ case other:
+ case unary:
+ case plunary:
+ assert (0);
+ syntax_error();
+ /* notreached */
+ case partype:
+ switch (peek) {
+ case parexpr: /* cast in front of parenthesized expression */
+ chk_postfix(expr);
+ return;
+ case partype: /* compounding cast: keep looping */
+ break;
+ case parambig: /* type or expr: keep looping */
+ break;
+ case unary:
+ case plunary:
+ chk_unary(expr);
+ return;
+ case other: /* cast in front of non-expression! */
+ syntax_error();
+ /* notreached */
+ }
+ break;
+ case parambig:
+ switch (peek) {
+ case parexpr: /* function call */
+ assign_context(expr, &cur_expr);
+ chk_postfix(expr);
+ return;
+ case partype: /* compounding cast: keep looping */
+ break;
+ case parambig: /* type or expr: keep looping */
+ break;
+ case unary:
+ chk_unary(expr);
+ return;
+ case plunary: /* treat unary expr with + or - as additive */
+ case other:
+ if (old == parambig) {
+ /* reparse two expression-like things in a row as call */
+ assign_context(expr, &old_expr);
+ chk_postfix(expr);
+ return;
+ }
+ /* reparse expression followed by non-parenthesized
+ stuff as postfix expression */
+ assign_context(expr, &cur_expr);
+ chk_postfix(expr);
+ return; /* need more context */
+ }
+ break;
+ }
+
+ old = curr;
+ curr = peek;
+ assign_context(&old_expr, &cur_expr);
+ assign_context(&cur_expr, expr);
+ assign_context(expr, &type);
+ }
+}
+
+static void chk_multiplicative(context_t *expr)
+{
+ for (;;) {
+ int ch;
+
+ chk_cast(expr);
+ ch = get_next_skip_ws(expr);
+
+ if ((ch != '*' && ch != '/' && ch != '%') || peek_next(expr) == '=') {
+ put_back(expr, ch);
+ break;
+ }
+ }
+}
+
+static void chk_additive(context_t *expr)
+{
+ for (;;) {
+ int ch;
+
+ chk_multiplicative(expr);
+ ch = get_next_skip_ws(expr);
+
+ if ((ch != '+' && ch != '-') || peek_next(expr) == '=') {
+ put_back(expr, ch);
+ break;
+ }
+ }
+}
+
+static void chk_shift(context_t *expr)
+{
+ for (;;) {
+ int ch;
+
+ chk_additive(expr);
+ ch = get_next_skip_ws(expr);
+
+ if (ch != '<' && ch != '>') {
+ put_back(expr, ch);
+ break;
+ }
+
+ if (ch == '<' && peek_next(expr) != '<') {
+ put_back(expr, ch);
+ break;
+ }
+
+ if (ch == '>' && peek_next(expr) != '>') {
+ put_back(expr, ch);
+ break;
+ }
+
+ get_next(expr);
+
+ if (peek_next(expr) == '=') {
+ put_back(expr, ch);
+ put_back(expr, ch);
+ break;
+ }
+ }
+}
+
+static void chk_relational(context_t *expr)
+{
+ for (;;) {
+ int ch;
+
+ chk_shift(expr);
+ ch = get_next_skip_ws(expr);
+
+
+ if (ch != '<' && ch != '>') {
+ put_back(expr, ch);
+ break;
+ }
+
+ if (ch == '<' && peek_next(expr) == '<') {
+ put_back(expr, ch);
+ break;
+ }
+
+ if (ch == '>' && peek_next(expr) == '>') {
+ put_back(expr, ch);
+ break;
+ }
+
+ if (peek_next(expr) == '=')
+ get_next(expr);
+ }
+}
+
+static void chk_equality(context_t *expr)
+{
+ for (;;) {
+ int ch;
+
+ chk_relational(expr);
+ ch = get_next_skip_ws(expr);
+
+ if ((ch != '!' && ch != '=') || peek_next(expr) != '=') {
+ put_back(expr, ch);
+ break;
+ }
+
+ match_hard(expr, '=');
+ }
+}
+
+static void chk_and(context_t *expr)
+{
+ for (;;) {
+ int ch;
+
+ chk_equality(expr);
+ ch = get_next_skip_ws(expr);
+
+ if (ch != '&' || peek_next(expr) == '&' || peek_next(expr) == '=') {
+ put_back(expr, ch);
+ break;
+ }
+ }
+}
+
+static void chk_exclusive_or(context_t *expr)
+{
+ for (;;) {
+ int ch;
+
+ chk_and(expr);
+ ch = get_next_skip_ws(expr);
+
+ if (ch != '^' || peek_next(expr) == '=') {
+ put_back(expr, ch);
+ break;
+ }
+ }
+}
+
+static void chk_inclusive_or(context_t *expr)
+{
+ for (;;) {
+ int ch;
+
+ chk_exclusive_or(expr);
+ ch = get_next_skip_ws(expr);
+
+ if (ch != '|' || peek_next(expr) == '|' || peek_next(expr) == '=') {
+ put_back(expr, ch);
+ break;
+ }
+ }
+}
+
+static void chk_logical_and(context_t *expr)
+{
+ for (;;) {
+ int ch;
+
+ chk_inclusive_or(expr);
+ ch = get_next_skip_ws(expr);
+
+ if (ch != '&' || peek_next(expr) != '&') {
+ put_back(expr, ch);
+ break;
+ }
+
+ match_hard(expr, '&');
+ }
+}
+
+static void chk_logical_or(context_t *expr)
+{
+ for (;;) {
+ int ch;
+
+ chk_logical_and(expr);
+ ch = get_next_skip_ws(expr);
+
+ if (ch != '|' || peek_next(expr) != '|') {
+ put_back(expr, ch);
+ break;
+ }
+
+ match_hard(expr, '|');
+ }
+}
+
+static void chk_conditional(context_t *expr)
+{
+ for (;;) {
+ int ch;
+
+ chk_logical_or(expr);
+ ch = get_next_skip_ws(expr);
+
+ if (ch != '?') {
+ put_back(expr, ch);
+ break;
+ }
+
+ chk_comma(expr);
+
+ skip_ws(expr);
+ match_hard(expr, ':');
+ }
+}
+
+static void chk_assignment(context_t *expr)
+{
+ for (;;) {
+ int ch;
+
+ chk_conditional(expr);
+ ch = get_next_skip_ws(expr);
+
+ switch (ch) {
+ case '=':
+ break;
+ case '*': case '/': case '%':
+ case '+': case '-': case '&':
+ case '^': case '|':
+ match_hard(expr, '=');
+ break;
+ case '<':
+ match_hard(expr, '<');
+ match_hard(expr, '=');
+ break;
+ case '>':
+ match_hard(expr, '>');
+ match_hard(expr, '=');
+ break;
+ case 0:
+ default:
+ put_back(expr, ch);
+ return;
+ }
+ set_effect(expr, sfx_certain);
+ }
+}
+
+static void chk_comma(context_t *expr)
+{
+ for (;;) {
+ int ch;
+
+ chk_assignment(expr);
+ ch = get_next_skip_ws(expr);
+
+ if (ch != ',') {
+ put_back(expr, ch);
+ break;
+ }
+ }
+}
+
+/*
+ * This function returns 1 if the expression is successfully parsed,
+ * or 0 if there is a syntax error.
+ *
+ * The object pointed to by eff is set to indicate the side effect ranking of
+ * the parsed expression: sfx_none, sfx_potential and sfx_certain. These
+ * rankins mean, respectively, that there are no side effects, that there are
+ * potential side effects, or that there certainly are side effects.
+ */
+
+int sfx_determine(const char *expr, sfx_rating_t *eff)
+{
+ static const except_id_t catch[] = { { SFX_EX, XCEPT_CODE_ANY } };
+ except_t *ex;
+ context_t ctx;
+ volatile int retval = 1;
+
+ if (!except_init())
+ return 0;
+
+ init_context(&ctx, (const unsigned char *) expr);
+
+ except_try_push(catch, 1, &ex);
+
+ if (ex == 0) {
+ chk_comma(&ctx);
+ skip_ws(&ctx);
+ if (peek_next(&ctx) != 0)
+ syntax_error();
+ } else {
+ /* exception caught */
+ retval = 0;
+ }
+
+ except_try_pop();
+
+ *eff = ctx.eff;
+
+ except_deinit();
+
+ return retval;
+}
+
+
+#ifdef KAZLIB_POSIX_THREADS
+
+static pthread_once_t cache_init;
+static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+#define init_once(X, Y) pthread_once(X, Y)
+#define lock_cache() pthread_mutex_lock(&cache_mutex)
+#define unlock_cache() pthread_mutex_unlock(&cache_mutex)
+
+#else
+static int cache_init;
+
+static void init_once(int *once, void (*func)(void))
+{
+ if (*once == 0) {
+ func();
+ *once = 1;
+ }
+}
+
+#define lock_cache()
+#define unlock_cache()
+#endif
+
+static hash_t *cache;
+
+extern hash_t *hash_create(hashcount_t, hash_comp_t, hash_fun_t);
+
+static void init_cache(void)
+{
+ cache = hash_create(HASHCOUNT_T_MAX, 0, 0);
+}
+
+static int lookup_cache(const char *expr, sfx_rating_t *rating)
+{
+ hnode_t *cache_node;
+ init_once(&cache_init, init_cache);
+
+ lock_cache();
+
+ cache_node = hash_lookup(cache, expr);
+
+ unlock_cache();
+
+ if (cache_node != 0) {
+ sfx_entry_t *cache_entry = hnode_get(cache_node);
+ *rating = cache_entry->eff;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int cache_result(const char *expr, sfx_rating_t rating)
+{
+ int result = 0;
+ hnode_t *cache_node;
+
+ init_once(&cache_init, init_cache);
+
+ if (cache == 0)
+ goto bail;
+
+ lock_cache();
+
+ cache_node = hash_lookup(cache, expr);
+
+ if (!cache_node) {
+ sfx_entry_t *cache_entry = malloc(sizeof *cache_entry);
+
+ if (cache_entry == 0)
+ goto bail_unlock;
+
+ hnode_init(&cache_entry->node, cache_entry);
+ cache_entry->expr = expr;
+ cache_entry->eff = rating;
+ hash_insert(cache, &cache_entry->node, expr);
+ } else {
+ sfx_entry_t *cache_entry = hnode_get(cache_node);
+ cache_entry->eff = rating;
+ result = 1;
+ }
+
+ result = 1;
+
+
+bail_unlock:
+ unlock_cache();
+
+bail:
+ return result;
+}
+
+
+void sfx_check(const char *expr, const char *file, unsigned long line)
+{
+ sfx_rating_t eff;
+ int success = lookup_cache(expr, &eff);
+
+ if (!success) {
+ success = sfx_determine(expr, &eff);
+ cache_result(expr, eff);
+ }
+
+ if (!success) {
+ fprintf(stderr, "%s:%ld: syntax error in expression \"%s\"\n",
+ file, line, expr);
+ } else if (eff == sfx_potential) {
+ fprintf(stderr, "%s:%ld: expression \"%s\" may have side effects\n",
+ file, line, expr);
+ } else if (eff == sfx_certain) {
+ fprintf(stderr, "%s:%ld: expression \"%s\" has side effects\n",
+ file, line, expr);
+ } else {
+ return;
+ }
+}
+
+int sfx_declare(const char *expr, sfx_rating_t eff)
+{
+ return cache_result(expr, eff);
+}
+
diff --git a/libutil/kazlib/sfx.h b/libutil/kazlib/sfx.h
new file mode 100644
index 0000000..b2a485c
--- /dev/null
+++ b/libutil/kazlib/sfx.h
@@ -0,0 +1,46 @@
+/*
+ * SideChk---A utility which tries to determine whether a given C expression
+ * is free of side effects. This can be used for verifying that macros which
+ * expand their arguments more than once are not being accidentally misused.
+ *
+ * Copyright (C) 1999 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.
+ *
+ */
+
+#ifndef SFX_H
+#define SFX_H
+
+#include <assert.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ sfx_none, sfx_potential, sfx_certain
+} sfx_rating_t;
+
+int sfx_determine(const char *, sfx_rating_t *);
+int sfx_declare(const char *, sfx_rating_t);
+void sfx_check(const char *, const char *, unsigned long);
+
+#ifdef __cplusplus
+}
+#endif
+
+#define SFX_CHECK(E) (sfx_check(#E, __FILE__, __LINE__), (E))
+#define SFX_STRING(E) #E
+
+#endif
diff --git a/meryl/args.C b/meryl/args.C
index f1df368..a8573a5 100644
--- a/meryl/args.C
+++ b/meryl/args.C
@@ -430,7 +430,7 @@ merylArgs::merylArgs(int argc, char **argv) {
personality = 'h';
} else if (strcmp(argv[arg], "-memory") == 0) {
arg++;
- memoryLimit = strtouint64(argv[arg], 0L);
+ memoryLimit = strtouint64(argv[arg], 0L) * 1024 * 1024;
} else if (strcmp(argv[arg], "-segments") == 0) {
arg++;
segmentLimit = strtouint64(argv[arg], 0L);
diff --git a/meryl/build.C b/meryl/build.C
index 70655b6..8bff0fd 100644
--- a/meryl/build.C
+++ b/meryl/build.C
@@ -214,22 +214,12 @@ prepareBatch(merylArgs *args) {
if (fatalError)
exit(1);
- if (args->numThreads > 0) {
- // If we were given no segment or memory limit, but threads, we
- // really want to create n segments.
- //
- if ((args->segmentLimit == 0) && (args->memoryLimit == 0)) {
- args->segmentLimit = args->numThreads;
- }
+ // If we were given no segment or memory limit, but threads, we
+ // really want to create n segments.
+ //
+ if ((args->numThreads > 0) && (args->segmentLimit == 0) && (args->memoryLimit == 0))
+ args->segmentLimit = args->numThreads;
- // If we are given a memory limit and threads, we want to use that much memory
- // total, not per thread.
- //
- if ((args->memoryLimit > 0) && (args->numThreads > 0)) {
- args->segmentLimit = 0;
- args->memoryLimit /= args->numThreads;
- }
- }
{
seqStream *seqstr = new seqStream(args->inputFile);
@@ -259,53 +249,45 @@ prepareBatch(merylArgs *args) {
#endif
- // If there is a memory limit, ignore the total number of mers and
- // pick a value that fits in memory.
+ // If there is a memory limit, figure out how to divide the work into an integer multiple of
+ // numThreads segments.
//
- // Otherwise, if there is a segment limit, split the total number
- // of mers into n pieces. Remember, there cannot be both a
- // memoryLimit and a segmentLimit.
+ // Otherwise, if there is a segment limit, split the total number of mers into n pieces.
//
// Otherwise, we must be doing it all in one fell swoop.
//
if (args->memoryLimit) {
args->mersPerBatch = estimateNumMersInMemorySize(args->merSize, args->memoryLimit, args->positionsEnabled, args->beVerbose);
+
if (args->mersPerBatch > args->numMersActual)
args->mersPerBatch = args->numMersActual;
+
+ args->mersPerBatch = (uint64)ceil((double)args->mersPerBatch / (double)args->numThreads);
args->segmentLimit = (uint64)ceil((double)args->numMersActual / (double)args->mersPerBatch);
- if (args->beVerbose)
- fprintf(stderr, "Have a memory limit: mersPerBatch="uint64FMT" segmentLimit="uint64FMT"\n", args->mersPerBatch, args->segmentLimit);
+
+ args->segmentLimit = args->numThreads * (uint32)ceil((double)args->segmentLimit / (double)args->numThreads);
+
} else if (args->segmentLimit) {
args->mersPerBatch = (uint64)ceil((double)args->numMersActual / (double)args->segmentLimit);
- if (args->beVerbose)
- fprintf(stderr, "Have a segment limit: mersPerBatch="uint64FMT" segmentLimit="uint64FMT"\n", args->mersPerBatch, args->segmentLimit);
+
} else {
args->mersPerBatch = args->numMersActual;
args->segmentLimit = 1;
- if (args->beVerbose)
- fprintf(stderr, "Have NO LIMITS!: mersPerBatch="uint64FMT" segmentLimit="uint64FMT"\n", args->mersPerBatch, args->segmentLimit);
}
args->basesPerBatch = (uint64)ceil((double)args->numBasesActual / (double)args->segmentLimit);
- if (args->beVerbose)
- fprintf(stderr, "basesPerBatch = "uint64FMT"\n", args->basesPerBatch);
- // Choose the optimal number of buckets to reduce memory usage.
- // Yes, this is already done in estimateNumMersInMemorySize() (but
- // not saved) and we need to do it for the other cases anyway.
+ // Choose the optimal number of buckets to reduce memory usage. Yes, this is already done in
+ // estimateNumMersInMemorySize() (but not saved) and we need to do it for the other cases anyway.
//
- // We use the number of mers per batch + 1 because we need to store
- // the first position after the last mer. That is, if there are
- // two mers, we will store that the first mer is at position 0, the
- // second mer is at position 1, and the end of the second mer is at
- // position 2.
+ // We use the number of mers per batch + 1 because we need to store the first position after the
+ // last mer. That is, if there are two mers, we will store that the first mer is at position 0,
+ // the second mer is at position 1, and the end of the second mer is at position 2.
//
args->bucketPointerWidth = logBaseTwo64(args->basesPerBatch + 1);
args->numBuckets_log2 = optimalNumberOfBuckets(args->merSize, args->basesPerBatch, args->positionsEnabled);
args->numBuckets = (uint64ONE << args->numBuckets_log2);
args->merDataWidth = args->merSize * 2 - args->numBuckets_log2;
- //args->bucketPointerMask = uint64MASK(args->numBuckets_log2);
-
if (args->merDataWidth > SORTED_LIST_WIDTH * 64) {
fprintf(stderr, " numMersActual = "uint64FMT"\n", args->numMersActual);
@@ -320,11 +302,15 @@ prepareBatch(merylArgs *args) {
if (args->beVerbose) {
if (args->memoryLimit)
- fprintf(stderr, "Computing "uint64FMT" segments using "uint64FMT"MB memory each.\n",
- args->segmentLimit, args->memoryLimit);
+ fprintf(stderr, "Computing "uint64FMT" segments using "uint32FMT" threads and "uint64FMT"MB memory ("uint64FMT"MB if in one batch).\n",
+ args->segmentLimit, args->numThreads,
+ estimateMemory(args->merSize, args->mersPerBatch, args->positionsEnabled) * args->numThreads,
+ estimateMemory(args->merSize, args->numMersActual, args->positionsEnabled));
else
- fprintf(stderr, "Computing "uint64FMT" segments using AS MUCH MEMORY AS NEEDED.\n",
- args->segmentLimit);
+ fprintf(stderr, "Computing "uint64FMT" segments using "uint32FMT" threads and "uint64FMT"MB memory ("uint64FMT"MB if in one batch).\n",
+ estimateMemory(args->merSize, args->mersPerBatch, args->positionsEnabled) * args->numThreads,
+ estimateMemory(args->merSize, args->numMersActual, args->positionsEnabled));
+
fprintf(stderr, " numMersActual = "uint64FMT"\n", args->numMersActual);
fprintf(stderr, " mersPerBatch = "uint64FMT"\n", args->mersPerBatch);
fprintf(stderr, " basesPerBatch = "uint64FMT"\n", args->basesPerBatch);
diff --git a/meryl/estimate.C b/meryl/estimate.C
index 951d69c..09f5073 100644
--- a/meryl/estimate.C
+++ b/meryl/estimate.C
@@ -7,99 +7,51 @@
#include "libmeryl.H"
#include "meryl.H"
-// Takes a memory limit in MB, returns the number of mers that we can
-// fit in that memory size, assuming optimalNumberOfBuckets() below
-// uses the same algorithm.
+// Takes a memory limit in MB, returns the number of mers that we can fit in that memory size,
+// assuming optimalNumberOfBuckets() below uses the same algorithm.
+//
+// For each possible number of buckets, try all poissible pointer widths. First we compute the
+// number of mers that fit in a bucket pointer table of size 2^t storing N bits in the mer data
+// table, then we check that the number of mers in the mer data table agrees with the width of the
+// pointer table.
//
uint64
estimateNumMersInMemorySize(uint32 merSize,
- uint32 mem,
+ uint64 mem,
bool positionsEnabled,
bool beVerbose) {
uint64 maxN = 0;
uint64 bestT = 0;
+ uint64 memLimt = mem * 8; // Memory limit, in bits.
+ uint64 posPerMer = (positionsEnabled == false) ? 0 : 32; // Positions consume space, if enabled.
+ uint64 tMax = (merSize > 25) ? 50 : 2 * merSize - 2; // Max width of bucket pointer table.
- // For each possible number of buckets, try all poissible pointer
- // widths. First we compute the number of mers that fit in a
- // bucket pointer table of size 2^t storing N bits in the mer data
- // table, then we check that the number of mers in the mer data
- // table agrees with the width of the pointer table.
-
-
- // This is the memory size we are trying to fill, in bits.
- //
- uint64 memLimt = ((uint64)mem) << 23;
+ // t - prefix stored in the bucket pointer table; number of entries in the table
+ // N - width of a bucket pointer
- // Positions consume space too, but only if enabled.
- //
- uint64 posPerMer = 0;
- if (positionsEnabled)
- posPerMer = 32;
+ for (uint64 t=2; t < tMax; t++) {
+ for (uint64 N=1; N<40; N++) {
+ uint64 Nmin = uint64ONE << (N - 1); // Minimum number of mers we want to fit in the table
+ uint64 Nmax = uint64ONE << (N); // Maximum number of mers that can fit in the table
- // Limit the number of entries in the bucket pointer table to
- // 50 bits -- thus, the prefix of each mer is at most 25.
- //
- uint32 tMax = 2*merSize - 2;
- if (tMax > 50)
- tMax = 50;
+ uint64 bucketsize = (uint64ONE << t) * N; // Size, in bits, of the pointer table
- for (uint64 t=2; t < tMax; t++) {
+ uint64 n = (memLimt - bucketsize) / (2*merSize - t + posPerMer); // Number of mers we can fit into mer data table.
- // We need to try all possibilities of N, the width of the
- // bucket pointer table === log2(numMers).
- //
- // Increased to 40 bits, so we're valid up to 1 trillion mers.
- //
- for (uint64 N=1; N<40; N++) {
- uint64 Nmin = uint64ONE << (N - 1);
- uint64 Nmax = uint64ONE << (N);
-
- // The size in bits of the bucket pointer table.
- //
- uint64 bucketsize = (uint64ONE << t) * N;
-
- // If our bucket pointer table size hasn't already blown our
- // memory limit, compute the number of mers that we can stuff
- // into the list.
- //
- if (memLimt > bucketsize) {
-
- // The number of mers we can then fit into the mer data table
- // is easy to compute.
- //
- // Even though we allocate merDataArray, bucketPointers,
- // bucketSizes, we don't use merDataArray until after we
- // release bucketSizes, and so we only estimate the maximum
- // in core (not allocated) size.
- //
- uint64 n = (memLimt - bucketsize) / (2*merSize - t + posPerMer);
-
- // We can stop now if our computed number of mers is outside the range that
- // the bucket pointer table can address.
- //
- if ((Nmin <= n) && (n <= Nmax)) {
-
- //fprintf(stderr, "prefixSize="uint64FMTW(2)" numMers="uint64FMTW(9)" memory=%.3fMB\n",
- // t, n,
- // (((uint64ONE << t) * logBaseTwo64(n) + n * (2*merSize - t + posPerMer)) >> 3) / 1048576.0);
-
- // Remember the settings with the highest number of mers, if
- // more than zero mers.
- //
- if ((n > 0) &&
- (maxN < n)) {
- maxN = n;
- bestT = t;
- }
-
- }
+ if ((memLimt > bucketsize) && // pointer table small enough to fit in memory
+ (n > 0) && // at least some space to store mers
+ (n <= Nmax) && // enough space for the mers in the data table
+ (Nmin <= n) && // ...but not more than enough space
+ (maxN < n)) { // this value of t fits more mers that any other seen so far
+ maxN = n;
+ bestT = t;
}
}
}
if (beVerbose)
- fprintf(stdout, "Can fit "uint64FMT" mers into table with prefix of "uint64FMT" bits, using %8.3fMB (%8.3fMB for positions)\n",
+ fprintf(stdout, "Can fit "uint64FMT" mers into table with prefix of "uint64FMT" bits, using %.3fMB (%.3fMB for positions)\n",
maxN,
bestT,
(((uint64ONE << bestT) * logBaseTwo64(maxN) + maxN * (2*merSize - bestT + posPerMer)) >> 3) / 1048576.0,
@@ -110,6 +62,37 @@ estimateNumMersInMemorySize(uint32 merSize,
+uint64
+estimateMemory(uint32 merSize,
+ uint64 numMers,
+ bool positionsEnabled) {
+
+ uint64 posPerMer = (positionsEnabled == false) ? 0 : 32;
+ uint64 tMax = (merSize > 25) ? 50 : 2 * merSize - 2;
+
+ uint64 tMin = tMax;
+ uint64 memMin = UINT64_MAX;
+
+ for (uint64 t=2; t < tMax; t++) {
+ uint64 N = logBaseTwo64(numMers); // Width of the bucket pointer table
+ uint64 memUsed = ((uint64ONE << t) * logBaseTwo64(numMers) + numMers * (2 * merSize - t + posPerMer)) >> 3;
+
+ if (memUsed < memMin) {
+ tMin = t;
+ memMin = memUsed;
+ }
+
+ //fprintf(stderr, "t=%2lu N=%2lu memUsed=%16lu -- tMin=%2lu memMin=%16lu\n",
+ // t, N, memUsed, tMin, memMin);
+ }
+
+ return(memMin >> 20);
+}
+
+
+
+
+
uint32
optimalNumberOfBuckets(uint32 merSize,
uint64 numMers,
@@ -174,8 +157,7 @@ estimate(merylArgs *args) {
}
uint32 opth = optimalNumberOfBuckets(args->merSize, args->numMersEstimated, args->positionsEnabled);
- uint64 memu = ((uint64ONE << opth) * logBaseTwo64(args->numMersEstimated+1) +
- args->numMersEstimated * (2 * args->merSize - opth));
+ uint64 memu = ((uint64ONE << opth) * logBaseTwo64(args->numMersEstimated+1) + args->numMersEstimated * (2 * args->merSize - opth));
fprintf(stderr, uint64FMT" "uint32FMT"-mers can be computed using "uint64FMT"MB memory.\n",
args->numMersEstimated, args->merSize, memu >> 23);
diff --git a/meryl/kmer-mask.C b/meryl/kmer-mask.C
index ed4b463..daa05bc 100644
--- a/meryl/kmer-mask.C
+++ b/meryl/kmer-mask.C
@@ -34,7 +34,7 @@ public:
a4[0] = 0;
aLength = 0;
- aRetained = 0.0;
+ aMasked = 0.0;
aLabel = 0;
b2 = alloc + 4 * maxLength;
@@ -50,7 +50,7 @@ public:
b4[0] = 0;
bLength = 0;
- bRetained = 0.0;
+ bMasked = 0.0;
bLabel = 0;
};
@@ -72,7 +72,7 @@ public:
fgets(a4, maxLength, FASTQ1); chomp(a4);
aLength = strlen(a2);
- aRetained = 0.0;
+ aMasked = 0.0;
aLabel = 0;
if (a2[maxLength - 2] != 0)
@@ -86,7 +86,7 @@ public:
fgets(b4, maxLength, FASTQ2); chomp(b4);
bLength = strlen(b2);
- bRetained = 0.0;
+ bMasked = 0.0;
bLabel = 0;
if (b2[maxLength - 2] != 0)
@@ -107,10 +107,10 @@ public:
void write(FILE *FASTQ1, FILE *FASTQ2) {
if (FASTQ1)
- fprintf(FASTQ1, "%s fractionRetained=%.3f\n%s\n%s\n%s\n", a1, aRetained, am, a3, a4);
+ fprintf(FASTQ1, "%s fractionMasked=%.3f\n%s\n%s\n%s\n", a1, aMasked, am, a3, a4);
if (FASTQ2)
- fprintf(FASTQ2, "%s fractionRetained=%.3f\n%s\n%s\n%s\n", b1, bRetained, bm, b3, b4);
+ fprintf(FASTQ2, "%s fractionMasked=%.3f\n%s\n%s\n%s\n", b1, bMasked, bm, b3, b4);
};
@@ -127,7 +127,7 @@ public:
char *a4;
uint32 aLength;
- double aRetained;
+ double aMasked;
uint32 aLabel;
char b1[1024];
@@ -138,7 +138,7 @@ public:
char *b4;
uint32 bLength;
- double bRetained;
+ double bMasked;
uint32 bLabel;
};
@@ -161,15 +161,16 @@ public:
existName = NULL;
minSize = 0;
extend = 0;
- keepNovel = false;
- keepConfirmed = false;
- demote = false;
- promote = false;
+ cleaner = false;
+ dirtier = false;
discard = true;
+ unlink = false;
- lowThreshold = 1. / 3.;
- highThreshold = 2. / 3.;
+ noMasking = false;
+
+ cleanThreshold = 1. / 3.;
+ matchThreshold = 2. / 3.;
for (uint32 ii=0; ii<1001; ii++)
scoreHistogram[ii] = 0;
@@ -215,15 +216,16 @@ public:
char *existName;
uint32 minSize;
uint32 extend;
- bool keepNovel;
- bool keepConfirmed;
- bool demote;
- bool promote;
+ bool cleaner;
+ bool dirtier;
bool discard;
+ bool unlink;
+
+ bool noMasking;
- double lowThreshold;
- double highThreshold;
+ double cleanThreshold;
+ double matchThreshold;
uint32 scoreHistogram[1001];
uint64 thresholdCounts[4][4];
@@ -274,7 +276,7 @@ printBits(char *S, uint32 Slen, char *found, char *display, const char *label) {
// Scan the read for kmers that exist in the DB. Set a bit for each kmer that exists.
void
-buildMask(char *S, uint32 Slen, char *found, bool keepNovel, existDB *exist, uint32 merSize) {
+buildMask(char *S, uint32 Slen, char *found, existDB *exist, uint32 merSize) {
merStream MS(new kMerBuilder(merSize),
new seqStream(S, Slen),
true, true);
@@ -353,23 +355,25 @@ convertToBases(char *S, uint32 Slen, char *found, uint32 merSize, uint32 extend)
// Assumes the found[] array represents base-based masking.
-// Returns the fraction of the sequence that is not masked.
+// Returns the fraction of the sequence that is masked.
double
-maskSequence(char *S, uint32 Slen, char *found, bool keepNovel, char *display) {
- uint32 saved = 0;
+maskSequence(char *S, uint32 Slen, char *found, bool noMasking, char *display) {
+ uint32 masked = 0;
+
+ // Flip the sense of the found[ii] test to mask kmers found in the database (true) or missing (false).
for (uint32 ii=0; ii<Slen; ii++) {
- if (found[ii] == keepNovel) {
- display[ii] = 'n';
+ if (found[ii] == true) {
+ display[ii] = (noMasking == false) ? 'n' : S[ii];
+ masked++;
} else {
display[ii] = S[ii];
- saved++;
}
}
display[Slen] = 0;
- return((double)saved / Slen);
+ return((double)masked / Slen);
}
@@ -399,8 +403,8 @@ maskWorker(void *G, void *T, void *S) {
//maskThread *t = (maskThread *)T;
fastqRecord *s = (fastqRecord *)S;
- buildMask(s->a2, s->aLength, s->af, g->keepNovel, g->exist, g->merSize);
- buildMask(s->b2, s->bLength, s->bf, g->keepNovel, g->exist, g->merSize);
+ buildMask(s->a2, s->aLength, s->af, g->exist, g->merSize);
+ buildMask(s->b2, s->bLength, s->bf, g->exist, g->merSize);
//printBits(S, found, display , "INITIAL");
removeIsolatedMers(s->a2, s->aLength, s->af, g->minSize);
@@ -411,25 +415,29 @@ maskWorker(void *G, void *T, void *S) {
convertToBases(s->b2, s->bLength, s->bf, g->merSize, g->extend);
//printBits(S, found, display, "BASE COVERAGE");
- s->aRetained = maskSequence(s->a2, s->aLength, s->af, g->keepNovel, s->am);
- s->bRetained = maskSequence(s->b2, s->bLength, s->bf, g->keepNovel, s->bm);
+ s->aMasked = maskSequence(s->a2, s->aLength, s->af, g->noMasking, s->am);
+ s->bMasked = maskSequence(s->b2, s->bLength, s->bf, g->noMasking, s->bm);
- s->aLabel = (s->aRetained < g->lowThreshold) ? 0 : ((s->aRetained < g->highThreshold) ? 1 : 2);
- s->bLabel = (s->bRetained < g->lowThreshold) ? 0 : ((s->bRetained < g->highThreshold) ? 1 : 2);
+ s->aLabel = (s->aMasked < g->cleanThreshold) ? 0 : ((s->aMasked < g->matchThreshold) ? 1 : 2);
+ s->bLabel = (s->bMasked < g->cleanThreshold) ? 0 : ((s->bMasked < g->matchThreshold) ? 1 : 2);
- if ((s->aLabel != s->bLabel) && (g->demote)) {
- s->aLabel = MIN(s->aLabel, s->bLabel);
- s->bLabel = MIN(s->aLabel, s->bLabel);
- }
+ // If both reads exist, adjust labels.
- if ((s->aLabel != s->bLabel) && (g->promote)) {
- s->aLabel = MAX(s->aLabel, s->bLabel);
- s->bLabel = MAX(s->aLabel, s->bLabel);
- }
+ if ((s->aLength > 0) && (s->bLength > 0)) {
+ if ((s->aLabel != s->bLabel) && (g->dirtier)) {
+ s->aLabel = MIN(s->aLabel, s->bLabel);
+ s->bLabel = MIN(s->aLabel, s->bLabel);
+ }
- if ((s->aLabel != s->bLabel) && (g->discard)) {
- s->aLabel = 3;
- s->bLabel = 3;
+ if ((s->aLabel != s->bLabel) && (g->cleaner)) {
+ s->aLabel = MAX(s->aLabel, s->bLabel);
+ s->bLabel = MAX(s->aLabel, s->bLabel);
+ }
+
+ if ((s->aLabel != s->bLabel) && (g->discard)) {
+ s->aLabel = 3;
+ s->bLabel = 3;
+ }
}
}
@@ -443,27 +451,24 @@ fastqWriter(void *G, void *S) {
g->thresholdCounts[s->aLabel][s->bLabel]++;
- g->scoreHistogram[(uint32)(1000 * s->aRetained)]++;
- g->scoreHistogram[(uint32)(1000 * s->bRetained)]++;
+ g->scoreHistogram[(uint32)(1000 * s->aMasked)]++;
+ g->scoreHistogram[(uint32)(1000 * s->bMasked)]++;
delete s;
}
-
-
-
-
FILE *
openInput(char *filename, bool &P) {
char C[2 * FILENAME_MAX];
FILE *F = NULL;
- int32 L = strlen(filename);
+ int32 L = (filename == NULL) ? 0 : strlen(filename);
- if ((L > 6) && (strcmp(filename + L - 6, ".fastq") == 0)) {
- F = fopen(filename, "r");
- P = false;
- }
+ if (filename == NULL)
+ return(NULL);
+
+ if (fileExists(filename) == false)
+ fprintf(stderr, "Failed to open Input file '%s' for reading: File not found.\n", filename), exit(1);
if ((L > 3) && (strcmp(filename + L - 3, ".gz") == 0)) {
sprintf(C, "gzip -dc %s", filename);
@@ -471,28 +476,39 @@ openInput(char *filename, bool &P) {
P = true;
}
- if ((L > 4) && (strcmp(filename + L - 4, ".bz2") == 0)) {
+ else if ((L > 4) && (strcmp(filename + L - 4, ".bz2") == 0)) {
sprintf(C, "bzip2 -dc %s", filename);
F = popen(C, "r");
P = true;
}
- if ((L > 3) && (strcmp(filename + L - 3, ".xz") == 0)) {
+ else if ((L > 3) && (strcmp(filename + L - 3, ".xz") == 0)) {
sprintf(C, "xz -dc %s", filename);
F = popen(C, "r");
P = true;
}
+ else {
+ errno = 0;
+ F = fopen(filename, "r");
+ if (errno)
+ fprintf(stderr, "Failed to open input file '%s' for reading: %s\n", filename, strerror(errno)), exit(1);
+ P = false;
+ }
+
return(F);
}
+
void
closeInput(FILE *F, char *filename, bool P) {
- if (F)
- if (P)
- pclose(F);
- else
- fclose(F);
+ if (F == NULL)
+ return;
+
+ if (P)
+ pclose(F);
+ else
+ fclose(F);
}
@@ -502,6 +518,8 @@ openOutput(char *prefix, const char *extension) {
char N[FILENAME_MAX];
FILE *F = NULL;
+ errno = 0;
+
sprintf(N, "%s.%s.fastq", prefix, extension);
F = fopen(N, "w");
if (errno)
@@ -510,6 +528,7 @@ openOutput(char *prefix, const char *extension) {
return(F);
}
+
void
closeOutput(FILE *F, char *prefix, const char *extension) {
if (F)
@@ -559,27 +578,40 @@ main(int argc, char **argv) {
} else if (strcmp(argv[arg], "-v") == 0) {
beVerbose = true;
- } else if (strcmp(argv[arg], "-novel") == 0) {
- g->keepNovel = true;
- } else if (strcmp(argv[arg], "-confirmed") == 0) {
- g->keepConfirmed = true;
+ } else if (strcmp(argv[arg], "-cleaner") == 0) {
+ g->dirtier = false;
+ g->cleaner = true;
+ g->discard = false;
+ g->unlink = false;
+
+ } else if (strcmp(argv[arg], "-dirtier") == 0) {
+ g->dirtier = true;
+ g->cleaner = false;
+ g->discard = false;
+ g->unlink = false;
- } else if (strcmp(argv[arg], "-demote") == 0) {
- g->demote = true;
- } else if (strcmp(argv[arg], "-promote") == 0) {
- g->promote = true;
} else if (strcmp(argv[arg], "-discard") == 0) {
+ g->dirtier = false;
+ g->cleaner = false;
g->discard = true;
+ g->unlink = false;
+
+ } else if (strcmp(argv[arg], "-unlink") == 0) {
+ g->dirtier = false;
+ g->cleaner = false;
+ g->discard = false;
+ g->unlink = true;
- } else if (strncmp(argv[arg], "-lowthreshold", 3) == 0) {
- g->lowThreshold = atof(argv[++arg]);
- } else if (strncmp(argv[arg], "-highthreshold", 3) == 0) {
- g->highThreshold = atof(argv[++arg]);
+ } else if (strcmp(argv[arg], "-nomasking") == 0) {
+ g->noMasking = true;
+
+ } else if (strncmp(argv[arg], "-clean", 3) == 0) {
+ g->cleanThreshold = atof(argv[++arg]);
+ } else if (strncmp(argv[arg], "-match", 3) == 0) {
+ g->matchThreshold = atof(argv[++arg]);
} else if (strcmp(argv[arg], "-h") == 0) {
g->outputHistogram = argv[++arg];
- //} else if (strcmp(argv[arg], "-o") == 0) {
- // outputSequence = atoi(argv[++arg]);
} else {
err++;
@@ -587,66 +619,88 @@ main(int argc, char **argv) {
arg++;
}
- if ((g->keepNovel == false) && (g->keepConfirmed == false))
+ if ((g->outPrefix == NULL) && (g->seq1Name != NULL))
+ err++;
+ if ((g->merName == NULL) && (g->existName == NULL))
err++;
if (err) {
- fprintf(stderr, "usage: %s [-novel | -confirmed] ...\n", argv[0]);
+ fprintf(stderr, "usage: %s -mdb mer-database -ms mer-size ...\n", argv[0]);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "INPUTS:\n");
+ fprintf(stderr, "\n");
fprintf(stderr, " -mdb mer-database load masking kmers from meryl 'mer-database'\n");
- fprintf(stderr, " -ms mer-size \n");
- fprintf(stderr, " -edb exist-database save masking kmers to 'exist-database' for faster restarts\n");
+ fprintf(stderr, " -ms mer-size the mer size used to generate the meryl database\n");
+ fprintf(stderr, " -edb exist-database save masking kmers to 'exist-database', and reuse on\n");
+ fprintf(stderr, " future runs (optional)\n");
fprintf(stderr, "\n");
fprintf(stderr, " -1 in.1.fastq input reads - fastq, fastq.gz, fastq.bz2 or fastq.xz\n");
- fprintf(stderr, " -2 in.2.fastq - (optional, but if not present, messes up the output classification)\n");
+ fprintf(stderr, " -2 in.2.fastq - optional, for mated reads\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " -l length maximum length of input read (%u)\n", g->maxLength);
+ fprintf(stderr, " If too small, program will fail.\n");
+ fprintf(stderr, " If too large, program will use excessive memory.\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "THRESHOLDS and OPTIONS:\n");
fprintf(stderr, "\n");
- fprintf(stderr, " -o out output reads:\n");
- fprintf(stderr, " out.fullymasked.[12].fastq - reads with below 'lowthreshold' bases retained\n");
- fprintf(stderr, " out.partiallymasked.[12].fastq - reads in between\n");
- fprintf(stderr, " out.retained.[12].fastq - reads with more than 'hightreshold' bases retained\n");
- fprintf(stderr, " out.discarded.[12].fastq - reads with conflicting status\n");
+ fprintf(stderr, " A read with fewer than 'c' bases masked is 'clean', while a read with more than 'd' bases\n");
+ fprintf(stderr, " masked is 'match'ed. Reads in between are 'murky'. See OUTPUTS.\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " -clean c mark reads with less than this fraction masked as 'clean' (%.4f)\n", g->cleanThreshold);
+ fprintf(stderr, " -match d mark reads with more than this fraction masked as 'match' (%.4f)\n", g->matchThreshold);
fprintf(stderr, "\n");
fprintf(stderr, " -m min-size ignore database hits below this many consecutive kmers (%d)\n", g->minSize);
fprintf(stderr, " -e extend-size extend database hits across this many missing kmers (%d)\n", g->extend);
fprintf(stderr, "\n");
- fprintf(stderr, " -novel RETAIN novel sequence not present in the database\n");
- fprintf(stderr, " -confirmed RETAIN confirmed sequence present in the database\n");
+ fprintf(stderr, " For mate pairs, how to handle reads with different classifications:\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " -cleaner use the cleaner classification of the two reads\n");
+ fprintf(stderr, " -dirtier use the dirtier classification of the two reads\n");
+ fprintf(stderr, " -discard discard pairs with conflicting classifications (DEFAULT)\n");
+ fprintf(stderr, " -unlink leave conflicting status alone, output reads to different files\n");
+ fprintf(stderr, " NOTE: mate pairing will be broken\n");
fprintf(stderr, "\n");
- fprintf(stderr, " -promote promote the lesser RETAINED read to the status of the more RETAINED read\n");
- fprintf(stderr, " read1=fullymasked and read2=partiallymasked -> both are partiallymasked\n");
- fprintf(stderr, " -demote demote the more RETAINED read to the status of the lesser RETAINED read\n");
- fprintf(stderr, " read1=fullymasked and read2=partiallymasked -> both are fullymasked\n");
- fprintf(stderr, " -discard discard pairs with conflicting status (DEFAULT)\n");
- fprintf(stderr, " read1=fullymasked and read2=partiallymasked -> both are discarded\n");
+ fprintf(stderr, " -nomasking do not trim masked sequence; output the original read\n");
fprintf(stderr, "\n");
- fprintf(stderr, "stats on stderr, number of sequences with amount RETAINED:\n");
- fprintf(stderr, " -lowthreshold t (%.4f)\n", g->lowThreshold);
- fprintf(stderr, " -highthreshold t (%.4f)\n", g->highThreshold);
+ fprintf(stderr, "OUTPUTS:\n");
fprintf(stderr, "\n");
+ fprintf(stderr, " -o prefix output reads:\n");
+ fprintf(stderr, " prefix.clean.[12].fastq - clean (unmasked) reads\n");
+ fprintf(stderr, " prefix.murky.[12].fastq - reads in between\n");
+ fprintf(stderr, " prefix.match.[12].fastq - matching (masked) reads\n");
+ fprintf(stderr, " prefix.mixed.[12].fastq - reads with conflicting status (for mated reads)\n");
fprintf(stderr, " -h histogram write a histogram of the amount of sequence RETAINED\n");
fprintf(stderr, "\n");
+ fprintf(stderr, "COMPUTE CONFIGURATION and LOGGING\n");
+ fprintf(stderr, "\n");
fprintf(stderr, " -t t use 't' compute threads\n");
fprintf(stderr, " -v show progress\n");
- if ((g->keepNovel == false) && (g->keepConfirmed == false))
- fprintf(stderr, "ERROR: exactly one of -novel and -confirmed must be supplied.\n");
+ if ((g->outPrefix == NULL) && (g->seq1Name != NULL))
+ fprintf(stderr, "ERROR: an output prefix (-o) must be supplied.\n");
+ if ((g->merName == NULL) && (g->existName == NULL))
+ fprintf(stderr, "ERROR: either a mer-database (-mdb) or an exist-database (-edb) must be supplied.\n");
exit(1);
}
- // Open inputs
+ // Open inputs and outputs
g->FASTQ1 = openInput(g->seq1Name, g->FASTQ1pipe);
g->FASTQ2 = openInput(g->seq2Name, g->FASTQ2pipe);
- g->OUTPUT1[0] = openOutput(g->outPrefix, "fullymasked.1");
- g->OUTPUT1[1] = openOutput(g->outPrefix, "partiallymasked.1");
- g->OUTPUT1[2] = openOutput(g->outPrefix, "retained.1");
- g->OUTPUT1[3] = openOutput(g->outPrefix, "discarded.1");
-
- g->OUTPUT2[0] = openOutput(g->outPrefix, "fullymasked.2");
- g->OUTPUT2[1] = openOutput(g->outPrefix, "partiallymasked.2");
- g->OUTPUT2[2] = openOutput(g->outPrefix, "retained.2");
- g->OUTPUT2[3] = openOutput(g->outPrefix, "discarded.2");
+ if (g->FASTQ1) {
+ g->OUTPUT1[0] = openOutput(g->outPrefix, "clean.1"); // Label == 0
+ g->OUTPUT1[1] = openOutput(g->outPrefix, "murky.1"); // Label == 1
+ g->OUTPUT1[2] = openOutput(g->outPrefix, "match.1"); // Label == 2
+ g->OUTPUT1[3] = openOutput(g->outPrefix, "mixed.1"); // Label == 3
+ }
+ if (g->FASTQ2) {
+ g->OUTPUT2[0] = openOutput(g->outPrefix, "clean.2");
+ g->OUTPUT2[1] = openOutput(g->outPrefix, "murky.2");
+ g->OUTPUT2[2] = openOutput(g->outPrefix, "match.2");
+ g->OUTPUT2[3] = openOutput(g->outPrefix, "mixed.2");
+ }
// Load data
@@ -669,47 +723,61 @@ main(int argc, char **argv) {
// Process!
- sweatShop *ss = new sweatShop(fastqLoader, maskWorker, fastqWriter);
+ if (g->FASTQ1) {
+ sweatShop *ss = new sweatShop(fastqLoader, maskWorker, fastqWriter);
- ss->setNumberOfWorkers(numWorkers);
+ ss->setNumberOfWorkers(numWorkers);
- ss->setWorkerBatchSize(1024);
+ ss->setWorkerBatchSize(1024);
- ss->setLoaderQueueSize(numWorkers * 81920);
- ss->setWriterQueueSize(numWorkers * 81920);
+ ss->setLoaderQueueSize(numWorkers * 81920);
+ ss->setWriterQueueSize(numWorkers * 81920);
- ss->run(g, beVerbose);
+ ss->run(g, beVerbose);
- closeInput(g->FASTQ1, g->seq1Name, g->FASTQ1pipe);
- closeInput(g->FASTQ2, g->seq1Name, g->FASTQ2pipe);
+ closeInput(g->FASTQ1, g->seq1Name, g->FASTQ1pipe);
+ closeInput(g->FASTQ2, g->seq1Name, g->FASTQ2pipe);
- closeOutput(g->OUTPUT1[0], g->outPrefix, "fulymasked.1");
- closeOutput(g->OUTPUT1[1], g->outPrefix, "partiallymasked.1");
- closeOutput(g->OUTPUT1[2], g->outPrefix, "retained.1");
- closeOutput(g->OUTPUT1[3], g->outPrefix, "discarded.1");
+ closeOutput(g->OUTPUT1[0], g->outPrefix, "clean.1");
+ closeOutput(g->OUTPUT1[1], g->outPrefix, "murky.1");
+ closeOutput(g->OUTPUT1[2], g->outPrefix, "match.1");
+ closeOutput(g->OUTPUT1[3], g->outPrefix, "mixed.1");
- closeOutput(g->OUTPUT2[0], g->outPrefix, "fulymasked.2");
- closeOutput(g->OUTPUT2[1], g->outPrefix, "partiallymasked.2");
- closeOutput(g->OUTPUT2[2], g->outPrefix, "retained.2");
- closeOutput(g->OUTPUT2[3], g->outPrefix, "discarded.2");
+ closeOutput(g->OUTPUT2[0], g->outPrefix, "clean.2");
+ closeOutput(g->OUTPUT2[1], g->outPrefix, "murky.2");
+ closeOutput(g->OUTPUT2[2], g->outPrefix, "match.2");
+ closeOutput(g->OUTPUT2[3], g->outPrefix, "mixed.2");
- fprintf(stderr, " bBelow bNormal bHigh bDiscarded\n");
- fprintf(stderr, "aBelow %8lu %8lu %8lu %8lu\n", g->thresholdCounts[0][0], g->thresholdCounts[0][1], g->thresholdCounts[0][2], g->thresholdCounts[0][3]);
- fprintf(stderr, "aNormal %8lu %8lu %8lu %8lu\n", g->thresholdCounts[1][0], g->thresholdCounts[1][1], g->thresholdCounts[1][2], g->thresholdCounts[1][3]);
- fprintf(stderr, "aHigh %8lu %8lu %8lu %8lu\n", g->thresholdCounts[2][0], g->thresholdCounts[2][1], g->thresholdCounts[2][2], g->thresholdCounts[2][3]);
- fprintf(stderr, "aDiscarded %8lu %8lu %8lu %8lu\n", g->thresholdCounts[3][0], g->thresholdCounts[3][1], g->thresholdCounts[3][2], g->thresholdCounts[3][3]);
+#define FW uint64FMTW(8)
- if (g->outputHistogram != NULL) {
- FILE *H = fopen(g->outputHistogram, "w");
+ if (g->FASTQ2 == NULL) {
+ fprintf(stderr, "aClean "FW"\n", g->thresholdCounts[0][0]);
+ fprintf(stderr, "aMurky "FW"\n", g->thresholdCounts[1][0]);
+ fprintf(stderr, "aMatch "FW"\n", g->thresholdCounts[2][0]);
+ fprintf(stderr, "aMixed "FW"\n", g->thresholdCounts[3][0]);
+ } else {
+ fprintf(stderr, " bClean bMurky bMatch bMixed\n");
+ fprintf(stderr, "aClean "FW" "FW" "FW" "FW"\n", g->thresholdCounts[0][0], g->thresholdCounts[0][1], g->thresholdCounts[0][2], g->thresholdCounts[0][3]);
+ fprintf(stderr, "aMurky "FW" "FW" "FW" "FW"\n", g->thresholdCounts[1][0], g->thresholdCounts[1][1], g->thresholdCounts[1][2], g->thresholdCounts[1][3]);
+ fprintf(stderr, "aMatch "FW" "FW" "FW" "FW"\n", g->thresholdCounts[2][0], g->thresholdCounts[2][1], g->thresholdCounts[2][2], g->thresholdCounts[2][3]);
+ fprintf(stderr, "aMixed "FW" "FW" "FW" "FW"\n", g->thresholdCounts[3][0], g->thresholdCounts[3][1], g->thresholdCounts[3][2], g->thresholdCounts[3][3]);
+ }
+
+#undef FW
- fprintf(H, "# amount of sequence retained\n");
- for (uint32 i=0; i<1001; i++)
- if (g->scoreHistogram[i] > 0)
- fprintf(H, "%.4f\t%u\n", i / 1000.0, g->scoreHistogram[i]);
+ if (g->outputHistogram != NULL) {
+ FILE *H = fopen(g->outputHistogram, "w");
- fclose(H);
+ fprintf(H, "# fraction-masked number-of-sequences\n");
+ for (uint32 i=0; i<1001; i++)
+ if (g->scoreHistogram[i] > 0)
+ fprintf(H, "%.4f\t%u\n", i / 1000.0, g->scoreHistogram[i]);
+
+ fclose(H);
+ }
}
+ delete g->exist;
delete g;
exit(0);
diff --git a/meryl/mapMers.C b/meryl/mapMers.C
index 98b3edd..f5cf2f0 100644
--- a/meryl/mapMers.C
+++ b/meryl/mapMers.C
@@ -97,20 +97,15 @@ main(int argc, char **argv) {
// with counts, report mean, mode, median, min, max for each frag.
if (operation == OP_STATS) {
- Clen = 0;
- for (uint32 i=0; i<Cmax; i++)
- C[i] = 0;
- while (MS->nextMer()) {
- uint64 cnt = E->count(MS->theFMer()) + E->count(MS->theRMer());
+ Clen = 0;
- if (cnt > 0)
- C[Clen++] = cnt;
- }
+ while (MS->nextMer())
+ C[Clen++] = E->count(MS->theFMer()) + E->count(MS->theRMer());
- uint64 mean = uint64ZERO;
+ uint64 mean = uint64ZERO;
uint64 min = ~uint64ZERO;
- uint64 max = uint64ZERO;
+ uint64 max = uint64ZERO;
uint64 hist[16] = { 0 };
// Histogram values are powers of two, e.g., <=1, <=2, <=4, <=8, <=16, <=32, <=64, <=128, <=256, <=512, <=1024, <=4096, <=8192, <=328768
@@ -118,7 +113,7 @@ main(int argc, char **argv) {
for (uint32 i=0; i<Clen; i++) {
mean += C[i];
- if ((min > C[i]) && (C[i] > 1))
+ if (min > C[i])
min = C[i];
if (max < C[i])
max = C[i];
@@ -126,7 +121,14 @@ main(int argc, char **argv) {
hist[ logBaseTwo64(C[i]) ]++;
}
- mean /= Clen;
+ if (Clen > 0) {
+ mean /= Clen;
+
+ } else {
+ mean = uint64ZERO;
+ min = uint64ZERO;
+ max = uint64ZERO;
+ }
fprintf(stdout,
"%s\t"
diff --git a/meryl/meryl.H b/meryl/meryl.H
index 7527df1..3e8d44e 100644
--- a/meryl/meryl.H
+++ b/meryl/meryl.H
@@ -102,10 +102,15 @@ public:
uint64
estimateNumMersInMemorySize(uint32 merSize,
- uint32 mem,
+ uint64 mem,
bool positionsEnabled,
bool beVerbose);
+uint64
+estimateMemory(uint32 merSize,
+ uint64 numMers,
+ bool positionsEnabled);
+
uint32
optimalNumberOfBuckets(uint32 merSize,
uint64 numMers,
diff --git a/sim4dbutils/reportAlignmentDifferences.C b/sim4dbutils/reportAlignmentDifferences.C
index 946ee3c..3940c24 100644
--- a/sim4dbutils/reportAlignmentDifferences.C
+++ b/sim4dbutils/reportAlignmentDifferences.C
@@ -82,7 +82,7 @@ main(int argc, char **argv) {
// Read matches.
- uint32 lMax = 10240;
+ uint32 lMax = 1048576;
uint32 lLen = 0;
uint32 *nTot = new uint32 [lMax];
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/kmer-tools.git
More information about the debian-med-commit
mailing list