about summary refs log tree commit diff
path: root/lib/util
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2006-08-19 03:12:28 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2006-08-19 03:12:28 +0000
commit1fd361a1ea06e44286c213ca1f814f49306fdc43 (patch)
tree64c8c96cf54d8718847339a403e5e67b922e8c3f /lib/util
downloadnetpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.gz
netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.xz
netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.zip
Create Subversion repository
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'lib/util')
-rw-r--r--lib/util/LICENSE.txt121
-rw-r--r--lib/util/Makefile29
-rw-r--r--lib/util/bitreverse.h46
-rw-r--r--lib/util/filename.c26
-rw-r--r--lib/util/filename.h7
-rw-r--r--lib/util/intcode.h86
-rw-r--r--lib/util/lexheader9
-rw-r--r--lib/util/mallocvar.h107
-rw-r--r--lib/util/nstring.c906
-rw-r--r--lib/util/nstring.h157
-rw-r--r--lib/util/pm_c_util.h62
-rw-r--r--lib/util/shhopt-1.1.6.lsm16
-rw-r--r--lib/util/shhopt.README200
-rw-r--r--lib/util/shhopt.c939
-rw-r--r--lib/util/shhopt.h240
-rw-r--r--lib/util/testnstring.c30
-rw-r--r--lib/util/wordaccess.h65
-rw-r--r--lib/util/wordaccess_64_le.h52
-rw-r--r--lib/util/wordaccess_gcc3_be.h40
-rw-r--r--lib/util/wordaccess_gcc3_le.h54
-rw-r--r--lib/util/wordaccess_generic.h89
21 files changed, 3281 insertions, 0 deletions
diff --git a/lib/util/LICENSE.txt b/lib/util/LICENSE.txt
new file mode 100644
index 00000000..aeb06a7f
--- /dev/null
+++ b/lib/util/LICENSE.txt
@@ -0,0 +1,121 @@
+The Frontier Artistic License Version 1.0
+Derived from the Artistic License at OpenSource.org.
+Submitted to OpenSource.org for Open Source Initiative certification.
+   
+Preamble
+
+The intent of this document is to state the conditions under which a
+Package may be copied, such that the Copyright Holder maintains some
+semblance of artistic control over the development of the package,
+while giving the users of the package the right to use and distribute
+the Package in a more-or-less customary fashion, plus the right to
+make reasonable modifications.
+   
+Definitions
+
+  "Package" refers to the script, suite, file, or collection of
+  scripts, suites, and/or files distributed by the Copyright Holder,
+  and to derivatives of that Package created through textual modification.
+
+  "Standard Version" refers to such a Package if it has not been
+  modified, or has been modified in accordance with the wishes of
+  the Copyright Holder.
+
+  "Copyright Holder" is whoever is named in the copyright statement
+  or statements for the package.
+
+  "You" is you, if you're thinking about copying or distributing
+  this Package.
+
+  "Reasonable copying fee" is whatever you can justify on the basis
+  of media cost, duplication charges, time of people involved, and
+  so on. (You will not be required to justify it to the Copyright
+  Holder, but only to the computing community at large as a market
+  that must bear the fee.)
+
+  "Freely Available" means that no fee is charged for the item
+  itself, though there may be fees involved in handling the item.
+  It also means that recipients of the item may redistribute it under
+  the same conditions they received it.
+       
+
+Terms
+
+1. You may make and give away verbatim copies of the source form of
+the Standard Version of this Package without restriction, provided
+that you duplicate all of the original copyright notices and
+associated disclaimers.
+   
+2. You may apply bug fixes, portability fixes, and other modifications
+derived from the Public Domain or from the Copyright Holder. A Package
+modified in such a way shall still be considered the Standard Version.
+   
+3. You may otherwise modify your copy of this Package in any way,
+provided that you insert a prominent notice in each changed script,
+suite, or file stating how and when you changed that script, suite,
+or file, and provided that you do at least ONE of the following:
+   
+  a) Use the modified Package only within your corporation or
+  organization, or retain the modified Package solely for personal use.
+     
+  b) Place your modifications in the Public Domain or otherwise make
+  them Freely Available, such as by posting said modifications to Usenet
+  or an equivalent medium, or placing the modifications on a major archive
+  site such as ftp.uu.net, or by allowing the Copyright Holder to include
+  your modifications in the Standard Version of the Package.
+     
+  c) Rename any non-standard executables so the names do not conflict
+  with standard executables, which must also be provided, and provide
+  a separate manual page (or equivalent) for each non-standard executable
+  that clearly documents how it differs from the Standard Version.
+     
+  d) Make other distribution arrangements with the Copyright Holder.
+     
+4. You may distribute the programs of this Package in object code or
+executable form, provided that you do at least ONE of the following:
+   
+  a) Distribute a Standard Version of the executables and library
+  files, together with instructions (in the manual page or
+  equivalent) on where to get the Standard Version.
+     
+  b) Accompany the distribution with the machine-readable source of
+  the Package with your modifications.
+     
+  c) Accompany any non-standard executables with their corresponding
+  Standard Version executables, give the non-standard executables
+  non-standard names, and clearly document the differences in manual
+  pages (or equivalent), together with instructions on where to get
+  the Standard Version.
+     
+  d) Make other distribution arrangements with the Copyright Holder.
+     
+5. You may charge a reasonable copying fee for any distribution of
+this Package. You may charge any fee you choose for support of this
+Package. You may not charge a fee for this Package itself. However,
+you may distribute this Package in aggregate with other (possibly
+commercial) programs as part of a larger (possibly commercial)
+software distribution provided that you do not advertise this Package
+as a product of your own.
+   
+6. The scripts and library files supplied as input to or produced as
+output from the programs of this Package do not automatically fall
+under the copyright of this Package, but belong to whomever generated
+them, and may be sold commercially, and may be aggregated with this
+Package.
+   
+7. Scripts, suites, or programs supplied by you that depend on or
+otherwise make use of this Package shall not be considered part of
+this Package.
+   
+8. The name of the Copyright Holder may not be used to endorse or
+promote products derived from this software without specific prior
+written permission.
+   
+9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+   
+                        The End
+
+
+http://www.spinwardstars.com/frontier/fal.html
diff --git a/lib/util/Makefile b/lib/util/Makefile
new file mode 100644
index 00000000..8f461f28
--- /dev/null
+++ b/lib/util/Makefile
@@ -0,0 +1,29 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = lib/util
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+INCLUDES = -I $(BUILDDIR) -I $(SRCDIR)/$(SUBDIR)/..
+
+# nstring is required for asprintf(), etc.  Also some systems don't have
+# snprintf(), e.g. Solaris 2.5.1.  2002.03.29.
+UTILOBJECTS = shhopt.o nstring.o filename.o
+
+MERGE_OBJECTS =
+
+all: $(UTILOBJECTS)
+
+include $(SRCDIR)/Makefile.common
+
+$(UTILOBJECTS):%.o:%.c importinc
+	$(CC) -c $(INCLUDES) -DNDEBUG $(CFLAGS) $(CFLAGS_SHLIB) \
+	  $(CFLAGS_PERSONAL) $(CADD) -o $@ $<
+
+testnstring: test.c nstring.h nstring.o
+	$(CC) $(CFLAGS) $(CADD) -o $@ nstring.o $<
+
+include Makefile.depend
diff --git a/lib/util/bitreverse.h b/lib/util/bitreverse.h
new file mode 100644
index 00000000..b3f0ea13
--- /dev/null
+++ b/lib/util/bitreverse.h
@@ -0,0 +1,46 @@
+/*
+** bitreverse.h
+**
+** This particular array seems to be useful in a lot of bitmap
+** conversion programs.  It's not used with standard libnetpbm PBM
+** processing because bits are stored one per byte (or per word), for
+** easier manipulation.  But if you wanted to write, for example, a
+** program to directly convert Sun raster format into X bitmaps, you
+** could use this.  It's also useful for the "packed PBM" libnetpbm
+** functions.
+**
+** Of course, you could also use this fairly slick chunk of code:
+**
+**     c = ((c >>  1) & 0x55) | ((c <<  1) & 0xaa);
+**     c = ((c >>  2) & 0x33) | ((c <<  2) & 0xcc);
+**     c = ((c >> 4) & 0x0f) | ((c << 4) & 0xf0); 
+*/
+
+#ifndef _BITR_H_
+#define _BITR_H_
+
+static unsigned char bitreverse[256] = {
+    0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0,
+    0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+    0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4,
+    0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+    0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc,
+    0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+    0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca,
+    0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+    0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6,
+    0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+    0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1,
+    0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+    0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9,
+    0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+    0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd,
+    0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+    0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3,
+    0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+    0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7,
+    0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+    0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf,
+    0x3f, 0xbf, 0x7f, 0xff};
+
+#endif /*_BITR_H_*/
diff --git a/lib/util/filename.c b/lib/util/filename.c
new file mode 100644
index 00000000..e3a9a89f
--- /dev/null
+++ b/lib/util/filename.c
@@ -0,0 +1,26 @@
+#include "nstring.h"
+
+#include "filename.h"
+
+const char *
+pm_basename(const char * const fileName) {
+/*----------------------------------------------------------------------------
+   Return the filename portion of a file name, e.g. "foo.ppm" from
+   "/home/bryanh/foo.ppm".
+
+   Return it as a malloc'ed string.
+-----------------------------------------------------------------------------*/
+    unsigned int basenameStart;
+    unsigned int i;
+    const char * retval;
+
+    basenameStart = 0;  /* initial assumption */
+
+    for (i = 0; fileName[i]; ++i) {
+        if (fileName[i] == '/')
+            basenameStart = i+1;
+    }
+    asprintfN(&retval, "%s", &fileName[basenameStart]);
+
+    return retval;
+}
diff --git a/lib/util/filename.h b/lib/util/filename.h
new file mode 100644
index 00000000..f598fec1
--- /dev/null
+++ b/lib/util/filename.h
@@ -0,0 +1,7 @@
+#ifndef FILENAME_H_INCLUDED
+#define FILENAME_H_INCLUDED
+
+const char *
+pm_basename(const char * const fileName);
+
+#endif
diff --git a/lib/util/intcode.h b/lib/util/intcode.h
new file mode 100644
index 00000000..4d9c83aa
--- /dev/null
+++ b/lib/util/intcode.h
@@ -0,0 +1,86 @@
+#ifndef INTCODE_H_INCLUDED
+#define INTCODE_H_INCLUDED
+
+#include "pm_config.h"  /* For uint32_t, BYTE_ORDER */
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   This is a big-endian representation of a 32 bit integer.  I.e.
+   bytes[0] is the most significant 8 bits; bytes[3] is the least
+   significant 8 bits of the number in pure binary.
+
+   On a big-endian machines, this is bit for bit identical to uint32_t.
+   On a little-endian machine, it isn't.
+
+   This is an important data type because decent file formats use
+   big-endian -- they don't care if some CPU happens to use some other
+   code for its own work.
+-----------------------------------------------------------------------------*/
+    unsigned char bytes[4];
+} bigend32;
+
+
+unsigned int const pm_byteOrder = BYTE_ORDER;
+
+
+static __inline__ uint32_t
+pm_uintFromBigend32(bigend32 const arg) {
+
+    uint32_t retval;
+
+    switch (pm_byteOrder) {
+    case BIG_ENDIAN: {
+        union {
+            bigend32 bigend;
+            uint32_t native;
+        } converter;
+        
+        converter.bigend = arg;
+        
+        retval = converter.native;
+    }; break;
+    case LITTLE_ENDIAN: {
+        retval =
+            (arg.bytes[0] << 24) |
+            (arg.bytes[1] << 16) |
+            (arg.bytes[2] <<  8) |
+            (arg.bytes[3] <<  0);
+    } break;
+    }
+    return retval;
+}
+
+
+
+static __inline__ bigend32
+pm_bigendFromUint32(uint32_t const arg) {
+
+    bigend32 retval;
+
+    switch (pm_byteOrder) {
+    case BIG_ENDIAN: {
+        union {
+            bigend32 bigend;
+            uint32_t native;
+        } converter;
+        
+        converter.native = arg;
+
+        retval = converter.bigend;
+    } break;
+    case LITTLE_ENDIAN: {
+        uint32_t shift;
+        shift = arg;
+        retval.bytes[3] = shift;  /* Takes lower 8 bits */
+        shift >>= 8;
+        retval.bytes[2] = shift;  /* Takes lower 8 bits */
+        shift >>= 8;
+        retval.bytes[1] = shift;  /* Takes lower 8 bits */
+        shift >>= 8;
+        retval.bytes[0] = shift;  /* Takes lower 8 bits */
+    } break;
+    }
+    return retval;
+}
+
+#endif
diff --git a/lib/util/lexheader b/lib/util/lexheader
new file mode 100644
index 00000000..c5691660
--- /dev/null
+++ b/lib/util/lexheader
@@ -0,0 +1,9 @@
+/* This is the file 'lexheader'.  It contains extra stuff needed by
+   parsers generated by lex.
+
+   GNU Flex generates a parser that refers to the non-ansi C library 
+   subroutine fileno().  In order to let it compile without warnings,
+   we define _XOPEN_SOURCE to declare that fact.
+*/
+#define _XOPEN_SOURCE
+/* END OF lexheader */
diff --git a/lib/util/mallocvar.h b/lib/util/mallocvar.h
new file mode 100644
index 00000000..a26d007b
--- /dev/null
+++ b/lib/util/mallocvar.h
@@ -0,0 +1,107 @@
+/* These are some dynamic memory allocation facilities.  They are essentially
+   an extension to C, as they do allocations with a cognizance of C 
+   variables.  You can use them to make C read more like a high level
+   language.
+
+   Before including this, you must define an __inline__ macro if your
+   compiler doesn't recognize it as a keyword.
+*/
+
+#ifndef MALLOCVAR_INCLUDED
+#define MALLOCVAR_INCLUDED
+
+#include "pm_config.h"
+
+#include <limits.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#if 0
+} /* to fake out automatic code indenters */
+#endif
+
+static __inline__ void
+mallocProduct(void **      const resultP, 
+              unsigned int const factor1,
+              unsigned int const factor2) {
+/*----------------------------------------------------------------------------
+   malloc a space whose size in bytes is the product of 'factor1' and
+   'factor2'.  But if that size cannot be represented as an unsigned int,
+   return NULL without allocating anything.  Also return NULL if the malloc
+   fails.
+
+   If either factor is zero, malloc a single byte.
+
+   Note that malloc() actually takes a size_t size argument, so the
+   proper test would be whether the size can be represented by size_t,
+   not unsigned int.  But there is no reliable indication available to
+   us, like UINT_MAX, of what the limitations of size_t are.  We
+   assume size_t is at least as expressive as unsigned int and that
+   nobody really needs to allocate more than 4GB of memory.
+-----------------------------------------------------------------------------*/
+    if (factor1 == 0 || factor2 == 0)
+        *resultP = malloc(1);
+    else {
+        if (UINT_MAX / factor2 < factor1) 
+            *resultP = NULL;
+        else 
+            *resultP = malloc(factor1 * factor2); 
+    }
+}
+
+
+
+static __inline__ void
+reallocProduct(void **      const blockP,
+               unsigned int const factor1,
+               unsigned int const factor2) {
+    
+    if (UINT_MAX / factor2 < factor1) 
+        *blockP = NULL;
+    else 
+        *blockP = realloc(*blockP, factor1 * factor2); 
+}
+
+
+
+#define MALLOCARRAY(arrayName, nElements) do { \
+    void * array; \
+    mallocProduct(&array, nElements, sizeof(arrayName[0])); \
+    arrayName = array; \
+} while (0)
+
+#define REALLOCARRAY(arrayName, nElements) { \
+    void * array; \
+    array = arrayName; \
+    reallocProduct(&array, nElements, sizeof(arrayName[0])); \
+    arrayName = array; \
+} while (0)
+
+
+#define MALLOCARRAY_NOFAIL(arrayName, nElements) \
+do { \
+    MALLOCARRAY(arrayName, nElements); \
+    if ((arrayName) == NULL) \
+        abort(); \
+} while(0)
+
+#define REALLOCARRAY_NOFAIL(arrayName, nElements) \
+do { \
+    REALLOCARRAY(arrayName, nElements); \
+    if ((arrayName) == NULL) \
+        abort(); \
+} while(0)
+
+
+#define MALLOCVAR(varName) \
+    varName = malloc(sizeof(*varName))
+
+#define MALLOCVAR_NOFAIL(varName) \
+    do {if ((varName = malloc(sizeof(*varName))) == NULL) abort();} while(0)
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/util/nstring.c b/lib/util/nstring.c
new file mode 100644
index 00000000..702a3c44
--- /dev/null
+++ b/lib/util/nstring.c
@@ -0,0 +1,906 @@
+/*
+ * snprintf.c - a portable implementation of snprintf
+ *
+
+   THIS MODULE WAS ADAPTED FOR NETPBM BY BRYAN HENDERSON ON 2002.03.24.
+   Bryan got the base from 
+   http://www.ijs.si/software/snprintf/snprintf-2.2.tar.gz, but made
+   a lot of changes and additions.
+
+ * AUTHOR
+ *   Mark Martinec <mark.martinec@ijs.si>, April 1999.
+ *
+ *   Copyright 1999, Mark Martinec. All rights reserved.
+ *
+ * TERMS AND CONDITIONS
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the "Frontier Artistic License" which comes
+ *   with this Kit.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty
+ *   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *   See the Frontier Artistic License for more details.
+ *
+ *   You should have received a copy of the Frontier Artistic License
+ *   with this Kit in the file named LICENSE.txt .
+ *   If not, I'll be glad to provide one.
+ *
+ * FEATURES
+ * - careful adherence to specs regarding flags, field width and precision;
+ * - good performance for large string handling (large format, large
+ *   argument or large paddings). Performance is similar to system's sprintf
+ *   and in several cases significantly better (make sure you compile with
+ *   optimizations turned on, tell the compiler the code is strict ANSI
+ *   if necessary to give it more freedom for optimizations);
+ * - return value semantics per ISO/IEC 9899:1999 ("ISO C99");
+ * - written in standard ISO/ANSI C - requires an ANSI C compiler.
+ *
+ * IMPLEMENTED CONVERSION SPECIFIERS AND DATA TYPES
+ *
+ * This snprintf implements only the following conversion specifiers:
+ * s, c, d, u, o, x, X, p  (and synonyms: i, D, U, O - see below)
+ * with flags: '-', '+', ' ', '0' and '#'.
+ * An asterisk is acceptable for field width as well as precision.
+ *
+ * Length modifiers 'h' (short int), 'l' (long int),
+ * and 'll' (long long int) are implemented.
+ *
+ * Conversion of numeric data (conversion specifiers d, u, o, x, X, p)
+ * with length modifiers (none or h, l, ll) is left to the system routine
+ * sprintf, but all handling of flags, field width and precision as well as
+ * c and s conversions is done very carefully by this portable routine.
+ * If a string precision (truncation) is specified (e.g. %.8s) it is
+ * guaranteed the string beyond the specified precision will not be referenced.
+ *
+ * Length modifiers h, l and ll are ignored for c and s conversions (you
+ * can't use data types wint_t and wchar_t).
+ *
+ * The following common synonyms for conversion characters are acceptable:
+ *   - i is a synonym for d
+ *   - D is a synonym for ld, explicit length modifiers are ignored
+ *   - U is a synonym for lu, explicit length modifiers are ignored
+ *   - O is a synonym for lo, explicit length modifiers are ignored
+ * The D, O and U conversion characters are nonstandard, they are accepted
+ * for backward compatibility only, and should not be used for new code.
+ *
+ * The following is specifically NOT implemented:
+ *   - flag ' (thousands' grouping character) is recognized but ignored
+ *   - numeric conversion specifiers: f, e, E, g, G and synonym F,
+ *     as well as the new a and A conversion specifiers
+ *   - length modifier 'L' (long double) and 'q' (quad - use 'll' instead)
+ *   - wide character/string conversions: lc, ls, and nonstandard
+ *     synonyms C and S
+ *   - writeback of converted string length: conversion character n
+ *   - the n$ specification for direct reference to n-th argument
+ *   - locales
+ *
+ * It is permitted for str_m to be zero, and it is permitted to specify NULL
+ * pointer for resulting string argument if str_m is zero (as per ISO C99).
+ *
+ * The return value is the number of characters which would be generated
+ * for the given input, excluding the trailing null. If this value
+ * is greater or equal to str_m, not all characters from the result
+ * have been stored in str, output bytes beyond the (str_m-1) -th character
+ * are discarded. If str_m is greater than zero it is guaranteed
+ * the resulting string will be null-terminated.
+ *
+ * NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1,
+ * but is different from some older and vendor implementations,
+ * and is also different from XPG, XSH5, SUSv2 specifications.
+ * For historical discussion on changes in the semantics and standards
+ * of snprintf see printf(3) man page in the Linux programmers manual.
+ *
+ * Routines asprintf and vasprintf return a pointer (in the ptr argument)
+ * to a buffer sufficiently large to hold the resulting string. This pointer
+ * should be passed to free(3) to release the allocated storage when it is
+ * no longer needed. If sufficient space cannot be allocated, these functions
+ * will return -1 and set ptr to be a NULL pointer. These two routines are a
+ * GNU C library extensions (glibc).
+ *
+ * Routines asnprintf and vasnprintf are similar to asprintf and vasprintf,
+ * yet, like snprintf and vsnprintf counterparts, will write at most str_m-1
+ * characters into the allocated output string, the last character in the
+ * allocated buffer then gets the terminating null. If the formatted string
+ * length (the return value) is greater than or equal to the str_m argument,
+ * the resulting string was truncated and some of the formatted characters
+ * were discarded. These routines present a handy way to limit the amount
+ * of allocated memory to some sane value.
+ *
+ * AVAILABILITY
+ *   http://www.ijs.si/software/snprintf/
+ *
+ */
+
+
+#define PORTABLE_SNPRINTF_VERSION_MAJOR 2
+#define PORTABLE_SNPRINTF_VERSION_MINOR 2
+
+#include <sys/types.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "pm.h"
+#include "pm_c_util.h"
+
+#include "nstring.h"
+
+#ifdef isdigit
+#undef isdigit
+#endif
+#define isdigit(c) ((c) >= '0' && (c) <= '9')
+
+/* For copying strings longer or equal to 'breakeven_point'
+ * it is more efficient to call memcpy() than to do it inline.
+ * The value depends mostly on the processor architecture,
+ * but also on the compiler and its optimization capabilities.
+ * The value is not critical, some small value greater than zero
+ * will be just fine if you don't care to squeeze every drop
+ * of performance out of the code.
+ *
+ * Small values favor memcpy, large values favor inline code.
+ */
+#if defined(__alpha__) || defined(__alpha)
+#  define breakeven_point   2	/* AXP (DEC Alpha)     - gcc or cc or egcs */
+#endif
+#if defined(__i386__)  || defined(__i386)
+#  define breakeven_point  12	/* Intel Pentium/Linux - gcc 2.96 */
+#endif
+#if defined(__hppa)
+#  define breakeven_point  10	/* HP-PA               - gcc */
+#endif
+#if defined(__sparc__) || defined(__sparc)
+#  define breakeven_point  33	/* Sun Sparc 5         - gcc 2.8.1 */
+#endif
+
+/* some other values of possible interest: */
+/* #define breakeven_point  8 */  /* VAX 4000          - vaxc */
+/* #define breakeven_point 19 */  /* VAX 4000          - gcc 2.7.0 */
+
+#ifndef breakeven_point
+#  define breakeven_point   6	/* some reasonable one-size-fits-all value */
+#endif
+
+#define fast_memcpy(d,s,n) \
+  { register size_t nn = (size_t)(n); \
+    if (nn >= breakeven_point) memcpy((d), (s), nn); \
+    else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
+      register char *dd; register const char *ss; \
+      for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } }
+
+#define fast_memset(d,c,n) \
+  { register size_t nn = (size_t)(n); \
+    if (nn >= breakeven_point) memset((d), (int)(c), nn); \
+    else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
+      register char *dd; register const int cc=(int)(c); \
+      for (dd=(d); nn>0; nn--) *dd++ = cc; } }
+
+/* declarations */
+
+static char credits[] = "\n\
+@(#)snprintf.c, v2.2: Mark Martinec, <mark.martinec@ijs.si>\n\
+@(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\
+@(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n";
+
+
+
+void
+vsnprintfN(char *       const str,
+           size_t       const str_m,
+           const char * const fmt,
+           va_list            ap,
+           size_t *     const sizeP) {
+
+    size_t str_l = 0;
+    const char *p = fmt;
+
+    /* In contrast with POSIX, the ISO C99 now says that str can be
+       NULL and str_m can be 0.  This is more useful than the old:
+       if (str_m < 1) return -1;
+    */
+
+    if (!p) p = "";
+    while (*p) {
+        if (*p != '%') {
+            /* if (str_l < str_m) str[str_l++] = *p++; -- this would
+               be sufficient but the following code achieves better
+               performance for cases * where format string is long and
+               contains few conversions
+            */
+            const char *q = strchr(p + 1,'%');
+            size_t n = !q ? strlen(p) : (q - p);
+            if (str_l < str_m) {
+                size_t avail = str_m - str_l;
+                fast_memcpy(str + str_l, p, (n > avail ? avail : n));
+            }
+            p += n; str_l += n;
+        } else {
+            const char *starting_p;
+            size_t min_field_width = 0, precision = 0;
+            int zero_padding = 0, precision_specified = 0, justify_left = 0;
+            int alternate_form = 0, force_sign = 0;
+            int space_for_positive = 1;
+                /* If both the ' ' and '+' flags appear,
+                   the ' ' flag should be ignored.
+                */
+            char length_modifier = '\0';  /* allowed values: \0, h, l, L */
+            char tmp[32];
+                /* temporary buffer for simple numeric->string conversion */
+
+            const char *str_arg;
+                /* string address in case of string argument */
+            size_t str_arg_l;
+                /* natural field width of arg without padding and sign */
+            unsigned char uchar_arg;
+                /* unsigned char argument value - only defined for c
+                   conversion.  N.B. standard explicitly states the char
+                   argument for the c conversion is unsigned.
+                */
+
+            size_t number_of_zeros_to_pad = 0;
+                /* number of zeros to be inserted for numeric
+                   conversions as required by the precision or minimal
+                   field width
+                */
+
+            size_t zero_padding_insertion_ind = 0;
+                /* index into tmp where zero padding is to be inserted */
+
+            char fmt_spec = '\0';
+                /* current conversion specifier character */
+
+            str_arg = credits;
+                /* just to make compiler happy (defined but not used) */
+            str_arg = NULL;
+            starting_p = p;
+            ++p;  /* skip '%' */
+
+            /* parse flags */
+            while (*p == '0' || *p == '-' || *p == '+' ||
+                   *p == ' ' || *p == '#' || *p == '\'') {
+                switch (*p) {
+                case '0': zero_padding = 1; break;
+                case '-': justify_left = 1; break;
+                case '+': force_sign = 1; space_for_positive = 0; break;
+                case ' ': force_sign = 1; break;
+                    /* If both the ' ' and '+' flags appear, the ' '
+                       flag should be ignored
+                    */
+                case '#': alternate_form = 1; break;
+                case '\'': break;
+                }
+                ++p;
+            }
+            /* If the '0' and '-' flags both appear, the '0' flag
+               should be ignored.
+            */
+
+            /* parse field width */
+            if (*p == '*') {
+                int j;
+                p++; j = va_arg(ap, int);
+                if (j >= 0) min_field_width = j;
+                else { min_field_width = -j; justify_left = 1; }
+            } else if (isdigit((int)(*p))) {
+                /* size_t could be wider than unsigned int; make sure
+                   we treat argument like common implementations do
+                */
+                unsigned int uj = *p++ - '0';
+                while (isdigit((int)(*p)))
+                    uj = 10*uj + (unsigned int)(*p++ - '0');
+                min_field_width = uj;
+            }
+            /* parse precision */
+            if (*p == '.') {
+                p++; precision_specified = 1;
+                if (*p == '*') {
+                    int j = va_arg(ap, int);
+                    p++;
+                    if (j >= 0) precision = j;
+                    else {
+                        precision_specified = 0; precision = 0;
+                        /* NOTE: Solaris 2.6 man page claims that in
+                           this case the precision should be set to 0.
+                           Digital Unix 4.0, HPUX 10 and BSD man page
+                           claim that this case should be treated as
+                           unspecified precision, which is what we do
+                           here.
+                        */
+                    }
+                } else if (isdigit((int)(*p))) {
+                    /* size_t could be wider than unsigned int; make
+                       sure we treat argument like common
+                       implementations do
+                    */
+                    unsigned int uj = *p++ - '0';
+                    while (isdigit((int)(*p)))
+                        uj = 10*uj + (unsigned int)(*p++ - '0');
+                    precision = uj;
+                }
+            }
+            /* parse 'h', 'l' and 'll' length modifiers */
+            if (*p == 'h' || *p == 'l') {
+                length_modifier = *p; p++;
+                if (length_modifier == 'l' && *p == 'l') {
+                    /* double l = long long */
+                    length_modifier = 'l';  /* treat it as a single 'l' */
+                    p++;
+                }
+            }
+            fmt_spec = *p;
+
+            /* common synonyms: */
+            switch (fmt_spec) {
+            case 'i': fmt_spec = 'd'; break;
+            case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
+            case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
+            case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
+            default: break;
+            }
+            /* get parameter value, do initial processing */
+            switch (fmt_spec) {
+            case '%':
+                /* % behaves similar to 's' regarding flags and field widths */
+            case 'c':
+                /* c behaves similar to 's' regarding flags and field widths */
+            case 's':
+                /* wint_t and wchar_t not handled */
+                length_modifier = '\0';
+                /* the result of zero padding flag with non-numeric
+                    conversion specifier is undefined. Solaris and
+                    HPUX 10 does zero padding in this case, Digital
+                    Unix and Linux does not.
+                */
+
+                zero_padding = 0;
+                    /* turn zero padding off for string conversions */
+                str_arg_l = 1;
+                switch (fmt_spec) {
+                case '%':
+                    str_arg = p; break;
+                case 'c': {
+                    int j = va_arg(ap, int);
+                    uchar_arg = (unsigned char) j;
+                        /* standard demands unsigned char */
+                    str_arg = (const char *) &uchar_arg;
+                    break;
+                }
+                case 's':
+                    str_arg = va_arg(ap, const char *);
+                    if (!str_arg)
+                        /* make sure not to address string beyond the
+                           specified precision !!!
+                        */
+                        str_arg_l = 0;
+                    else if (!precision_specified)
+                        /* truncate string if necessary as requested by
+                           precision 
+                        */
+                        str_arg_l = strlen(str_arg);
+                    else if (precision == 0)
+                        str_arg_l = 0;
+                    else {
+                        /* memchr on HP does not like n > 2^31  !!! */
+                        const char * q =
+                            memchr(str_arg, '\0',
+                                   precision <= 0x7fffffff ?
+                                   precision : 0x7fffffff);
+                        str_arg_l = !q ? precision : (q-str_arg);
+                    }
+                    break;
+                default: break;
+                }
+                break;
+            case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': {
+                /* NOTE: the u, o, x, X and p conversion specifiers imply
+                   the value is unsigned;  d implies a signed value
+                */
+                int arg_sign = 0;
+                /* 0  if numeric argument is zero (or if pointer is NULL
+                      for 'p'),
+                   +1 if greater than zero (or nonzero for unsigned arguments),
+                   -1 if negative (unsigned argument is never negative)
+                */
+
+                int int_arg = 0;
+                unsigned int uint_arg = 0;
+                   /* defined only for length modifier h, or for no
+                      length modifiers
+                   */
+
+                long int long_arg = 0;  unsigned long int ulong_arg = 0;
+                /* only defined for length modifier l */
+
+                void *ptr_arg = NULL;
+                /* pointer argument value -only defined for p conversion */
+
+                if (fmt_spec == 'p') {
+                    /* HPUX 10: An l, h, ll or L before any other
+                        conversion character (other than d, i, u, o,
+                        x, or X) is ignored.
+
+                      Digital Unix: not specified, but seems to behave
+                      as HPUX does.
+
+                      Solaris: If an h, l, or L appears before any
+                      other conversion specifier (other than d, i, u,
+                      o, x, or X), the behavior is
+                      undefined. (Actually %hp converts only 16-bits
+                      of address and %llp treats address as 64-bit
+                      data which is incompatible with (void *)
+                      argument on a 32-bit system). 
+                    */
+
+                    length_modifier = '\0';
+                    ptr_arg = va_arg(ap, void *);
+                    if (ptr_arg != NULL) arg_sign = 1;
+                } else if (fmt_spec == 'd') {  /* signed */
+                    switch (length_modifier) {
+                    case '\0':
+                    case 'h':
+                        /* It is non-portable to specify a second
+                           argument of char or short to va_arg,
+                           because arguments seen by the called
+                           function are not char or short.  C converts
+                           char and short arguments to int before
+                           passing them to a function.
+                        */
+                        int_arg = va_arg(ap, int);
+                        if      (int_arg > 0) arg_sign =  1;
+                        else if (int_arg < 0) arg_sign = -1;
+                        break;
+                    case 'l':
+                        long_arg = va_arg(ap, long int);
+                        if      (long_arg > 0) arg_sign =  1;
+                        else if (long_arg < 0) arg_sign = -1;
+                        break;
+                    }
+                } else {  /* unsigned */
+                    switch (length_modifier) {
+                    case '\0':
+                    case 'h':
+                        uint_arg = va_arg(ap, unsigned int);
+                        if (uint_arg)
+                            arg_sign = 1;
+                        break;
+                    case 'l':
+                        ulong_arg = va_arg(ap, unsigned long int);
+                        if (ulong_arg)
+                            arg_sign = 1;
+                        break;
+                    }
+                }
+                str_arg = tmp; str_arg_l = 0;
+                /* NOTE: For d, i, u, o, x, and X conversions, if
+                   precision is specified, the '0' flag should be
+                   ignored. This is so with Solaris 2.6, Digital UNIX
+                   4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with
+                   Perl.
+                */
+                if (precision_specified)
+                    zero_padding = 0;
+                if (fmt_spec == 'd') {
+                    if (force_sign && arg_sign >= 0)
+                        tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
+                    /* leave negative numbers for sprintf to handle,
+                       to avoid handling tricky cases like (short
+                       int)(-32768)
+                    */
+                } else if (alternate_form) {
+                    if (arg_sign != 0 && (fmt_spec == 'x' ||
+                                          fmt_spec == 'X')) {
+                        tmp[str_arg_l++] = '0';
+                        tmp[str_arg_l++] = fmt_spec;
+                    }
+                    /* alternate form should have no effect for p
+                       conversion, but ...
+                    */
+                }
+                zero_padding_insertion_ind = str_arg_l;
+                if (!precision_specified)
+                    precision = 1;   /* default precision is 1 */
+                if (precision == 0 && arg_sign == 0) {
+                    /* converted to null string */
+                    /* When zero value is formatted with an explicit
+                       precision 0, the resulting formatted string is
+                       empty (d, i, u, o, x, X, p).
+                    */
+                } else {
+                    char f[5]; int f_l = 0;
+                    f[f_l++] = '%';
+                        /* construct a simple format string for sprintf */
+                    if (!length_modifier) { }
+                    else if (length_modifier=='2') {
+                        f[f_l++] = 'l'; f[f_l++] = 'l';
+                    }
+                    else
+                        f[f_l++] = length_modifier;
+                    f[f_l++] = fmt_spec; f[f_l++] = '\0';
+                    if (fmt_spec == 'p')
+                        str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg);
+                    else if (fmt_spec == 'd') {  /* signed */
+                        switch (length_modifier) {
+                        case '\0':
+                        case 'h':
+                            str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg);
+                            break;
+                        case 'l':
+                            str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg);
+                            break;
+                        }
+                    } else {  /* unsigned */
+                        switch (length_modifier) {
+                        case '\0':
+                        case 'h':
+                            str_arg_l += sprintf(tmp+str_arg_l, f, uint_arg);
+                            break;
+                        case 'l':
+                            str_arg_l += sprintf(tmp+str_arg_l, f, ulong_arg);
+                            break;
+                        }
+                    }
+                    /* include the optional minus sign and possible "0x"
+                       in the region before the zero padding insertion point
+                    */
+                    if (zero_padding_insertion_ind < str_arg_l &&
+                        tmp[zero_padding_insertion_ind] == '-') {
+                        zero_padding_insertion_ind++;
+                    }
+                    if (zero_padding_insertion_ind+1 < str_arg_l &&
+                        tmp[zero_padding_insertion_ind]   == '0' &&
+                        (tmp[zero_padding_insertion_ind+1] == 'x' ||
+                         tmp[zero_padding_insertion_ind+1] == 'X') ) {
+                        zero_padding_insertion_ind += 2;
+                    }
+                }
+                {
+                    size_t num_of_digits =
+                        str_arg_l - zero_padding_insertion_ind;
+                    if (alternate_form && fmt_spec == 'o'
+                        /* unless zero is already the first character */
+                        && !(zero_padding_insertion_ind < str_arg_l
+                             && tmp[zero_padding_insertion_ind] == '0')) {
+                        /* assure leading zero for alternate-form
+                           octal numbers 
+                        */
+                        if (!precision_specified ||
+                            precision < num_of_digits+1) {
+                            /* precision is increased to force the
+                               first character to be zero, except if a
+                               zero value is formatted with an
+                               explicit precision of zero
+                            */
+                            precision = num_of_digits+1;
+                            precision_specified = 1;
+                        }
+                    }
+                    /* zero padding to specified precision? */
+                    if (num_of_digits < precision) 
+                        number_of_zeros_to_pad = precision - num_of_digits;
+                }
+                /* zero padding to specified minimal field width? */
+                if (!justify_left && zero_padding) {
+                    int n =
+                        min_field_width - (str_arg_l+number_of_zeros_to_pad);
+                    if (n > 0) number_of_zeros_to_pad += n;
+                }
+            } break;
+            default:
+                /* unrecognized conversion specifier, keep format
+                   string as-is
+                */
+                zero_padding = 0;
+                    /* turn zero padding off for non-numeric convers. */
+                /* reset flags */
+                justify_left = 1;
+                min_field_width = 0;
+                /* discard the unrecognized conversion, just keep the
+                   unrecognized conversion character
+                */
+                str_arg = p;
+                str_arg_l = 0;
+                if (*p)
+                    /* include invalid conversion specifier unchanged
+                       if not at end-of-string
+                    */
+                    ++str_arg_l;
+                break;
+            }
+            if (*p)
+                p++;  /* step over the just processed conversion specifier */
+            /* insert padding to the left as requested by
+               min_field_width; this does not include the zero padding
+               in case of numerical conversions
+            */
+
+            if (!justify_left) {
+                /* left padding with blank or zero */
+                int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
+                if (n > 0) {
+                    if (str_l < str_m) {
+                        size_t avail = str_m-str_l;
+                        fast_memset(str+str_l, (zero_padding ? '0' : ' '),
+                                    (n > avail ? avail : n));
+                    }
+                    str_l += n;
+                }
+            }
+            /* zero padding as requested by the precision or by the
+               minimal field width for numeric conversions required?
+            */
+            if (number_of_zeros_to_pad <= 0) {
+                /* will not copy first part of numeric right now,
+                   force it to be copied later in its entirety
+                */
+                zero_padding_insertion_ind = 0;
+            } else {
+                /* insert first part of numerics (sign or '0x') before
+                   zero padding
+                */
+                int n = zero_padding_insertion_ind;
+                if (n > 0) {
+                    if (str_l < str_m) {
+                        size_t avail = str_m-str_l;
+                        fast_memcpy(str+str_l, str_arg, (n>avail?avail:n));
+                    }
+                    str_l += n;
+                }
+                /* insert zero padding as requested by the precision
+                   or min field width
+                */
+                n = number_of_zeros_to_pad;
+                if (n > 0) {
+                    if (str_l < str_m) {
+                        size_t avail = str_m - str_l;
+                        fast_memset(str + str_l, '0', (n > avail ? avail : n));
+                    }
+                    str_l += n;
+                }
+            }
+            /* insert formatted string (or as-is conversion specifier
+               for unknown conversions)
+            */
+            {
+                int n = str_arg_l - zero_padding_insertion_ind;
+                if (n > 0) {
+                    if (str_l < str_m) {
+                        size_t avail = str_m-str_l;
+                        fast_memcpy(str + str_l,
+                                    str_arg + zero_padding_insertion_ind,
+                                    (n > avail ? avail : n));
+                    }
+                    str_l += n;
+                }
+            }
+            /* insert right padding */
+            if (justify_left) {
+                /* right blank padding to the field width */
+                int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
+                if (n > 0) {
+                    if (str_l < str_m) {
+                        size_t avail = str_m-str_l;
+                        fast_memset(str+str_l, ' ', (n>avail?avail:n));
+                    }
+                    str_l += n;
+                }
+            }
+        }
+    }
+    if (str_m > 0) {
+        /* make sure the string is null-terminated even at the expense
+           of overwriting the last character (shouldn't happen, but
+           just in case)
+        */
+        str[str_l <= str_m-1 ? str_l : str_m-1] = '\0';
+    }
+    *sizeP = str_l;
+}
+
+
+
+int
+snprintfN(char *       const dest,
+          size_t       const str_m,
+          const char * const fmt,
+          ...) {
+
+    size_t size;
+    va_list ap;
+
+    va_start(ap, fmt);
+    
+    vsnprintfN(dest, str_m, fmt, ap, &size);
+
+    va_end(ap);
+
+    assert(size <= INT_MAX);
+
+    return size;
+}
+
+
+
+/* When a function that is supposed to return a malloc'ed string cannot
+   get the memory for it, it should return 'strsol'.  That has a much
+   better effect on the caller, if the caller doesn't explicitly allow for
+   the out of memory case, than returning NULL.  Note that it is very
+   rare for the system not to have enough memory to return a small string,
+   so it's OK to have somewhat nonsensical behavior when it happens.  We
+   just don't want catastrophic behavior.
+
+   'strsol' is an external symbol, so if Caller wants to detect the
+   out-of-memory failure, he certainly can.
+*/
+const char * const strsol = "NO MEMORY TO CREATE STRING!";
+
+
+
+/* We would like to have vasprintfN(), but it is difficult because you
+   can't run through a va_list twice, which we would want to do: once
+   to measure the length; once actually to build the string.  On some
+   machines, you can simply make two copies of the va_list variable in
+   normal C fashion, but on others you need va_copy, which is a
+   relatively recent invention.  In particular, the simple va_list copy
+   failed on an AMD64 Gcc Linux system in March 2006.
+*/
+
+void PM_GNU_PRINTF_ATTR(2,3)
+asprintfN(const char ** const resultP,
+          const char *  const fmt, 
+          ...) {
+
+    va_list varargs;
+    
+    size_t dryRunLen;
+    
+    va_start(varargs, fmt);
+    
+    vsnprintfN(NULL, 0, fmt, varargs, &dryRunLen);
+
+    va_end(varargs);
+
+    if (dryRunLen + 1 < dryRunLen)
+        /* arithmetic overflow */
+        *resultP = strsol;
+    else {
+        size_t const allocSize = dryRunLen + 1;
+        char * result;
+        result = malloc(allocSize);
+        if (result == NULL)
+            *resultP = strsol;
+        else {
+            va_list varargs;
+            size_t realLen;
+
+            va_start(varargs, fmt);
+
+            vsnprintfN(result, allocSize, fmt, varargs, &realLen);
+                
+            assert(realLen == dryRunLen);
+            va_end(varargs);
+
+            *resultP = result;
+        }
+    }
+}
+
+
+
+void
+strfree(const char * const string) {
+
+    if (string != strsol)
+        free((void *) string);
+}
+
+
+
+const char *
+strsepN(char ** const stringP, const char * const delim) {
+    const char * retval;   
+
+    if (stringP == NULL || *stringP == NULL)
+        retval = NULL;
+    else {
+        char * p;
+
+        retval = *stringP;
+
+        for (p = *stringP; *p && strchr(delim, *p) == NULL; ++p);
+ 
+        if (*p) {
+            /* We hit a delimiter, not end-of-string.  So null out the 
+               delimiter and advance user's pointer to the next token
+            */
+            *p++ = '\0';
+            *stringP = p;
+        } else {
+            /* We ran out of string.  So the end-of-string delimiter is 
+               already there, and we set the user's pointer to NULL to 
+               indicate there are no more tokens.
+            */
+            *stringP = NULL;
+        }
+    }
+    return retval;
+}
+
+
+
+int
+stripeq(const char * const comparand,
+        const char * const comparator) {
+/*----------------------------------------------------------------------------
+  Compare two strings, ignoring leading and trailing white space.
+
+  Return 1 (true) if the strings are identical; 0 (false) otherwise.
+-----------------------------------------------------------------------------*/
+    char *p, *q, *px, *qx;
+    char equal;
+  
+    /* Make p and q point to the first non-blank character in each string.
+     If there are no non-blank characters, make them point to the terminating
+     NULL.
+     */
+
+    p = (char *) comparand;
+    while (ISSPACE(*p)) p++;
+    q = (char *) comparator;
+    while (ISSPACE(*q)) q++;
+
+    /* Make px and qx point to the last non-blank character in each string.
+       If there are no nonblank characters (which implies the string is
+       null), make them point to the terminating NULL.
+    */
+
+    if (*p == '\0') px = p;
+    else {
+        px = p + strlen(p) - 1;
+        while (ISSPACE(*px)) px--;
+    }
+
+    if (*q == '\0') qx = q;
+    else {
+        qx = q + strlen(q) - 1;
+        while (ISSPACE(*qx)) qx--;
+    }
+
+    equal = 1;   /* initial assumption */
+  
+    /* If the stripped strings aren't the same length, 
+       we know they aren't equal 
+     */
+    if (px - p != qx - q) equal = 0;
+
+
+    while (p <= px) {
+        if (*p != *q) equal = 0;
+        p++; q++;
+    }
+    return equal;
+}
+
+
+
+const char *
+memmemN(const char * const haystack,
+        size_t       const haystacklen,
+        const char * const needle,
+        size_t       const needlelen) {
+
+    /* This does the same as the function of the same name in the GNU
+       C library
+    */
+    const char * p;
+
+    for (p = haystack; p <= haystack + haystacklen - needlelen; ++p)
+        if (MEMEQ(p, needle, needlelen))
+            return p;
+
+    return NULL;
+}
diff --git a/lib/util/nstring.h b/lib/util/nstring.h
new file mode 100644
index 00000000..9ed20051
--- /dev/null
+++ b/lib/util/nstring.h
@@ -0,0 +1,157 @@
+#ifndef _NSTRING_H
+#define _NSTRING_H
+
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "pm.h"  /* For PM_GNU_PRINTF_ATTR, __inline__ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#if 0
+} /* to fake out automatic code indenters */
+#endif
+
+/* Here is are string functions that respect the size of the array
+   into which you are copying -- E.g. STRSCPY truncates the source string as
+   required so that it fits, with the terminating null, in the destination
+   array.
+*/
+#define STRSCPY(A,B) \
+	(strncpy((A), (B), sizeof(A)), *((A)+sizeof(A)-1) = '\0')
+#define STRSCMP(A,B) \
+	(strncmp((A), (B), sizeof(A)))
+#define STRSCAT(A,B) \
+    (strncpy(A+strlen(A), B, sizeof(A)-strlen(A)), *((A)+sizeof(A)-1) = '\0')
+
+#define STREQ(A, B) \
+    (strcmp((A), (B)) == 0)
+#define STRNEQ(A, B, C) \
+    (strncmp((A), (B), (C)) == 0)
+#define STRCASEEQ(A, B) \
+    (strcasecmp((A), (B)) == 0)
+#define STRNCASEEQ(A, B, C) \
+    (strncasecmp((A), (B), (C)) == 0)
+#define STRSEQ(A, B) \
+	(strncmp((A), (B), sizeof(A)) == 0)
+
+#define MEMEQ(A, B, C) \
+    (memcmp((A), (B), (C)) == 0)
+#define MEMSZERO(A) \
+    bzero((A), sizeof(A))
+
+
+static __inline__ int
+streq(const char * const comparand,
+      const char * const comparator) {
+
+    return strcmp(comparand, comparator) == 0;
+}
+
+
+
+/* The standard C library routines isdigit(), for some weird 
+   historical reason, does not take a character (type 'char') as its
+   argument.  Instead it takes an integer.  When the integer is a whole
+   number, it represents a character in the obvious way using the local
+   character set encoding.  When the integer is negative, the results
+   are undefined.
+
+   Passing a character to isdigit(), which expects an integer, results in
+   isdigit() sometimes getting a negative number.
+
+   On some systems, when the integer is negative, it represents exactly
+   the character you want it to anyway (e.g. -1 is the character that is
+   encoded 0xFF).  But on others, it does not.
+
+   (The same is true of other routines like isdigit()).
+
+   Therefore, we have the substitutes for isdigit() etc. that take an
+   actual character (type 'char') as an argument.
+*/
+
+#define ISALNUM(C) (isalnum((unsigned char)(C)))
+#define ISALPHA(C) (isalpha((unsigned char)(C)))
+#define ISCNTRL(C) (iscntrl((unsigned char)(C)))
+#define ISDIGIT(C) (isdigit((unsigned char)(C)))
+#define ISGRAPH(C) (isgraph((unsigned char)(C)))
+#define ISLOWER(C) (islower((unsigned char)(C)))
+#define ISPRINT(C) (isprint((unsigned char)(C)))
+#define ISPUNCT(C) (ispunct((unsigned char)(C)))
+#define ISSPACE(C) (isspace((unsigned char)(C)))
+#define ISUPPER(C) (isupper((unsigned char)(C)))
+#define ISXDIGIT(C) (isxdigit((unsigned char)(C)))
+#define TOUPPER(C) ((char)toupper((unsigned char)(C)))
+
+
+/* These are all private versions of commonly available standard C
+   library subroutines whose names are the same except with the N at
+   the end.  Because not all standard C libraries have them all,
+   Netpbm must include them in its own libraries, and because some
+   standard C libraries have some of them, Netpbm must use different
+   names for them.
+   
+   The GNU C library has all of them.  All but the oldest standard C libraries
+   have snprintf().
+
+   There are slight differences between the asprintf() family and that
+   found in other libraries:
+
+     - There is no return value.
+
+     - The returned string is a const char * instead of a char *.  The
+       const is more correct.
+
+     - If the function can't get the memory, it returns 'strsol',
+       which is a string that is in static memory that contains text
+       indicating an out of memory failure has occurred, intead of
+       NULL.  This makes it much easier for programs to ignore this
+       possibility.
+
+   strfree() is strictly a Netpbm invention, to allow proper type checking
+   when freeing storage allocated by the Netpbm asprintfN().
+*/
+
+extern const char * const strsol;
+
+int
+snprintfN(char *       const dest,
+          size_t       const str_m,
+          const char * const fmt,
+          ...) PM_GNU_PRINTF_ATTR(3,4);
+
+void
+vsnprintfN(char *       const str,
+           size_t       const str_m,
+           const char * const fmt,
+           va_list            ap,
+           size_t *     const sizeP);
+
+void
+asprintfN(const char ** const resultP,
+          const char *  const fmt,
+          ...) PM_GNU_PRINTF_ATTR(2,3);
+
+void 
+strfree(const char * const string);
+
+const char *
+strsepN(char ** const stringP, const char * const delim);
+
+int
+stripeq(const char * const comparand,
+        const char * const comparator);
+
+const char *
+memmemN(const char * const haystack,
+        size_t       const haystacklen,
+        const char * const needle,
+        size_t       const needlelen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/util/pm_c_util.h b/lib/util/pm_c_util.h
new file mode 100644
index 00000000..f21a2f82
--- /dev/null
+++ b/lib/util/pm_c_util.h
@@ -0,0 +1,62 @@
+#ifndef PM_C_UTIL_INCLUDED
+#define PM_C_UTIL_INCLUDED
+
+#undef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#undef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#undef ABS
+#define ABS(a) ((a) >= 0 ? (a) : -(a))
+#undef SGN
+#define SGN(a)		(((a)<0) ? -1 : 1)
+#undef ODD
+#define ODD(n) ((n) & 1)
+#undef ROUND
+#define ROUND(X) (((X) >= 0) ? (int)((X)+0.5) : (int)((X)-0.5))
+#undef ROUNDU
+#define ROUNDU(X) ((unsigned int)((X)+0.5))
+#undef SQR
+#define SQR(a) ((a)*(a))
+
+/* NOTE: do not use "bool" as a type in an external interface.  It could
+   have different definitions on either side of the interface.  Even if both
+   sides include this interface header file, the conditional compilation
+   here means one side may use the typedef below and the other side may
+   use some other definition.  For an external interface, be safe and just
+   use "int".
+*/
+
+/* We used to assume that if TRUE was defined, then bool was too.
+   However, we had a report on 2001.09.21 of a Tru64 system that had
+   TRUE but not bool and on 2002.03.21 of an AIX 4.3 system that was
+   likewise.  So now we define bool all the time, unless the macro
+   HAVE_BOOL is defined.  If someone is using the Netpbm libraries and
+   also another library that defines bool, he can either make the
+   other library define/respect HAVE_BOOL or just define HAVE_BOOL in
+   the file that includes pm_config.h or with a compiler option.  Note
+   that C++ always has bool.  
+
+   A preferred way of getting booleans is <stdbool.h>.  But it's not
+   available on all platforms, and it's easy to reproduce what it does
+   here.
+*/
+#ifndef TRUE
+  #define TRUE 1
+  #endif
+#ifndef FALSE
+  #define FALSE 0
+  #endif
+/* C++ has a bool type and false and true constants built in. */
+#ifndef __cplusplus
+  #ifndef HAVE_BOOL
+    #define HAVE_BOOL 1
+    typedef int bool;
+    #endif
+  #ifndef true
+    enum boolvalue {false=0, true=1};
+    #endif
+  #endif
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+#endif
diff --git a/lib/util/shhopt-1.1.6.lsm b/lib/util/shhopt-1.1.6.lsm
new file mode 100644
index 00000000..7fba316e
--- /dev/null
+++ b/lib/util/shhopt-1.1.6.lsm
@@ -0,0 +1,16 @@
+Begin3
+Title:		shhopt - library for parsing command line options.
+Version:	1.1.6
+Entered-date:	10MAR00
+Description:	C-functions for parsing command line options, both
+                traditional one-character options, and GNU'ish
+                --long-options.
+Keywords:	programming, library, lib, commandline, options
+Author: 	sverrehu@online.no (Sverre H. Huseby)
+Primary-site:	http://home.sol.no/~sverrehu/pub-unix/
+Alternate-site:	sunsite.unc.edu /pub/Linux/libs
+		shhopt-1.1.6.tar.gz
+Platforms:	Requires ANSI C-compiler.
+Copying-policy:	Artistic License
+		http://www.opensource.org/licenses/artistic-license.html
+End
diff --git a/lib/util/shhopt.README b/lib/util/shhopt.README
new file mode 100644
index 00000000..2d241edf
--- /dev/null
+++ b/lib/util/shhopt.README
@@ -0,0 +1,200 @@
+Shhopt was originally written by Sverre H. Huseby, and extended by
+Bryan Henderson starting in March 2000 for use in Netpbm.  
+
+Below is the README file from Huseby's package.
+
+The file LICENSE.TXT in this directory contains the license (the
+Artistic License) under which Bryan took and redistributed Shhopt and
+the license under which Bryan offers the modified Shhopt to others.
+
+Bryan made the following changes to shhopt for Netpbm.  It is fully
+backward compatible with the original.
+
+- OPT_FLOAT (floating point number) data type added
+
+- optParseOptions2() added.  Advantages over optParseOptions(): You
+  can have a syntax where there is no such thing as a short option
+  (e.g. -a.  Maybe stacked like -tanp).  Then the long options can
+  have either 1 or 2 dashes (e.g. -width or --width).  Of course, -w
+  could be an abbreviation of -width; that's not the same thing as a
+  short option.
+
+- optParseOptions3() added.  Advantages over optParseOptions2(): 
+  Tells you whether (how many times, actually) an option was
+  specified - no need to play games with defaults.  Also, no need
+  to initialize an option value variable.
+
+- optStruct longName changed from char * to const char * to avoid
+  compiler warnings (with -Wwrite-strings) when you assign a string
+  literal to it (which is the normal case).
+
+- OPTENTRY/OPTENT3 macros added for declaring the option definition
+  array.
+
+- replace isdigit() with ISDIGIT() from Netpbm nstring.h so weird 
+  8-bit characters don't cause incorrect results.
+
+------------------------------------------------------------------------------
+
+
+shhopt - library for parsing command line options.
+==================================================
+
+This is a set of functions for parsing command line options. Both
+traditional one-character options, and GNU-style --long-options are
+supported.
+
+
+What separates this from traditional getopt?
+--------------------------------------------
+
+This library does more of the parsing for you. You set up a special
+structure describing the names and types of the options you want your
+program to support. In the structure you also give addresses of
+variables to update or functions to call for the various
+options. By calling optParseOptions, all options in argv are parsed
+and removed from argv. What is left, are the non-optional arguments to
+your program.
+
+The down-side of this, is that you won't be able to make a program
+where the position of the options between the non-options are
+significant.
+
+shhopt is distributed under the "Artistic license" (aka. the Perl
+license), which IMHO gives more freedom than GPL or LGPL. For a copy
+of the Artistic license, see
+
+    http://www.opensource.org/licenses/artistic-license.html
+
+For more information on Open Source licenses, go to
+
+    http://www.opensource.org/licenses/
+
+
+Usage
+-----
+
+To see how to use this library, take a look at the sample program
+example.c.
+
+A brief explanation:
+
+To parse your command line, you need to create and initialize an array
+of optStruct's. Each element in the array describes a long and short
+version of an option and specifies what type of option it is and how
+to handle it.
+
+The structure fields (see also shhopt.h):
+
+  `shortName' is the short option name without the leading '-'.
+
+  `longName' is the long option name without the leading "--".
+
+  `type' specifies what type of option this is. (Does it expect an
+      argument? Is it a flag? If it takes an argument, what type
+      should it be?)
+
+  `arg' is either a function to be called with the argument from
+      the commandline, or a pointer to a location in which to store
+      the value.
+
+  `flags' indicates whether `arg' points to a function or a storage
+      location.
+
+The different argument types:
+
+  `OPT_END' flags this as the last element in the options array.
+
+  `OPT_FLAG' indicates an option that takes no arguments. If `arg' is
+      not a function pointer, the value of `arg' will be set to 1 if
+      this flag is found on the command line.
+
+  `OPT_STRING' expects a string argument.
+
+  `OPT_INT' expects an int argument.
+
+  `OPT_UINT' expects an unsigned int argument.
+
+  `OPT_LONG' expects a long argument.
+
+  `OPT_ULONG' expects an unsigned long argument.
+
+The different flag types:
+
+  `OPT_CALLFUNC' indicates that `arg' is a function pointer. If this
+      is not given, `arg' is taken as a pointer to a variable.
+
+
+Notes
+-----
+
+* A dash (`-') by itself is not taken as any kind of an option, as
+  several programs use this to indicate the special files stdin and
+  stdout. It is thus left as a normal argument to the program.
+
+* Two dashes (`--') as an argument, is taken to mean that the rest of
+  the arguments should not be scanned for options. This simplifies
+  giving names of files that start with a dash.
+
+* Short (one-character) options accept parameters in two ways, either
+  directly following the option in the same argv-entry, or in the next
+  argv-entry:
+
+	-sPARAMETER
+	-s PARAMETER
+
+* Long options accept parameters in two ways:
+
+	--long-option=PARAMETER
+	--long-option PARAMETER
+
+  To follow the GNU-tradition, your program documentation should use
+  the first form.
+
+* Several one-character options may be combined after a single
+  dash. If any of the options requires a parameter, the rest of the
+  string is taken as this parameter. If there is no "rest of the
+  string", the next argument is taken as the parameter.
+
+* There is no support for floating point (double) arguments to
+  options. This is to avoid unnecessary linking with the math
+  library. See example.c for how to get around this by writing a
+  function converting a string argument to a double (functions
+  strToDouble and doubleFunc).
+
+
+Portability
+-----------
+
+If your libc lacks strtoul, you will need to link with GNU's -liberty,
+that may be found by anonymous ftp to ftp://ftp.gnu.org/pub/gnu/
+
+The library has (more or less recently) been compiled and `tested' on
+the following systems:
+
+	IRIX Release 5.3 IP22
+	GNU/Linux 2.2.11
+	SunOS Release 4.1.3_U1 (-liberty needed)
+	ULTRIX V4.4 (Rev. 69)
+
+All compilations were done using GNU's gcc, and GNU's make.
+
+
+Author
+------
+
+The program is written by
+
+        Sverre H. Huseby        sverrehu@online.no
+        Lofthusvn. 11 B         http://home.sol.no/~sverrehu/
+        N-0587 Oslo
+        Norway
+
+You can use and copy this for _free_, but I would be very happy if you
+send me an E-mail and tell me that you use it. If you insist on paying
+something, please donate some money to an organization that strives to
+make the world a better place for everyone.
+
+I don't like bugs, so please help me removing them by reporting
+whatever you find!
+
diff --git a/lib/util/shhopt.c b/lib/util/shhopt.c
new file mode 100644
index 00000000..7722b5d5
--- /dev/null
+++ b/lib/util/shhopt.c
@@ -0,0 +1,939 @@
+/*------------------------------------------------------------------------
+ |  FILE            shhopt.c
+ |
+ |  DESCRIPTION     Functions for parsing command line arguments. Values
+ |                  of miscellaneous types may be stored in variables,
+ |                  or passed to functions as specified.
+ |
+ |  REQUIREMENTS    Some systems lack the ANSI C -function strtoul. If your
+ |                  system is one of those, you'll ned to write one yourself,
+ |                  or get the GNU liberty-library (from prep.ai.mit.edu).
+ |
+ |  WRITTEN BY      Sverre H. Huseby <sverrehu@online.no>
+ +----------------------------------------------------------------------*/
+
+/*************************************************************************
+  This is based on work by Sverre H. Huseby <sverrehu@online.no>.
+  These functions are backward compatible with the 'shhopt'
+  distributed by Huseby.
+
+  See the file README.shhopt for copy licensing information.
+*************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+
+/*-----------------------------------------------------------------------+
+|  PRIVATE DATA                                                          |
++-----------------------------------------------------------------------*/
+
+static void optFatalFunc(const char *, ...);
+static void (*optFatal)(const char *format, ...) = optFatalFunc;
+
+/*-----------------------------------------------------------------------+
+|  PRIVATE FUNCTIONS                                                     |
++-----------------------------------------------------------------------*/
+
+/*------------------------------------------------------------------------
+ |  NAME          optFatalFunc
+ |
+ |  FUNCTION      Show given message and abort the program.
+ |
+ |  INPUT         format, ...
+ |                        Arguments used as with printf().
+ |
+ |  RETURNS       Never returns. The program is aborted.
+ */
+static void
+optFatalFunc(const char *format, ...)
+{
+    va_list ap;
+
+    fflush(stdout);
+    va_start(ap, format);
+    vfprintf(stderr, format, ap);
+    va_end(ap);
+    fprintf(stderr, "\n");
+    exit(99);
+}
+
+/*------------------------------------------------------------------------
+ |  NAME          optStructCount
+ |
+ |  FUNCTION      Get number of options in a optStruct.
+ |
+ |  INPUT         opt     array of possible options.
+ |
+ |  RETURNS       Number of options in the given array.
+ |
+ |  DESCRIPTION   Count elements in an optStruct-array. The strcture must
+ |                be ended using an element of type OPT_END.
+ */
+static int
+optStructCount(const optEntry opt[])
+{
+    int ret = 0;
+
+    while (opt[ret].type != OPT_END && ret < 500)
+        ++ret;
+    return ret;
+}
+
+/*------------------------------------------------------------------------
+ |  NAME          optMatch
+ |
+ |  FUNCTION      Find a matching option.
+ |
+ |  INPUT         opt     array of possible options.
+ |                s       string to match, without `-' or `--'.
+ |                lng     match long option, otherwise short.
+ |
+ |  RETURNS       Index to the option if found, -1 if not found.
+ |
+ |  DESCRIPTION   Short options are matched from the first character in
+ |                the given string.
+ */
+static int
+optMatch(const optEntry opt[], const char *s, int lng)
+{
+    int        nopt, q, matchlen = 0;
+    const char *p;
+
+    nopt = optStructCount(opt);
+    if (lng) {
+        if ((p = strchr(s, '=')) != NULL)
+            matchlen = p - s;
+        else
+            matchlen = strlen(s);
+    }
+    for (q = 0; q < nopt; q++) {
+        if (lng) {
+            if (!opt[q].longName)
+                continue;
+            if (strncmp(s, opt[q].longName, matchlen) == 0)
+                return q;
+        } else {
+            if (!opt[q].shortName)
+                continue;
+            if (*s == opt[q].shortName)
+                return q;
+        }
+    }
+    return -1;
+}
+
+/*------------------------------------------------------------------------
+ |  NAME          optString
+ |
+ |  FUNCTION      Return a (static) string with the option name.
+ |
+ |  INPUT         opt     the option to stringify.
+ |                lng     is it a long option?
+ |
+ |  RETURNS       Pointer to static string.
+ */
+static char *
+optString(const optEntry opte, int lng)
+{
+    static char ret[31];
+
+    if (lng) {
+        strcpy(ret, "--");
+        strncpy(ret + 2, opte.longName, 28);
+    } else {
+        ret[0] = '-';
+        ret[1] = opte.shortName;
+        ret[2] = '\0';
+    }
+    return ret;
+}
+
+
+    
+static optEntry
+optStructToEntry(const optStruct opt) {
+/*----------------------------------------------------------------------------
+   Return the information in 'opt' (an optStruct type) as an optEntry type.
+   optEntry is newer and has an additional field.
+-----------------------------------------------------------------------------*/
+    optEntry opte;
+
+    opte.shortName = opt.shortName;
+    opte.longName  = opt.longName;
+    opte.type      = opt.type;
+    opte.arg       = opt.arg;
+    opte.specified = NULL;
+    opte.flags     = opt.flags;
+
+    return(opte);
+}
+
+
+
+static optEntry *
+optStructTblToEntryTbl(const optStruct optStructTable[]) {
+/*----------------------------------------------------------------------------
+   Return a table of optEntry types containing the information in the
+   input table of optStruct types.
+
+   Return it in newly malloc'ed storage.
+-----------------------------------------------------------------------------*/
+    int count;
+        /* Number of entries in input table, including OPT_END marker */
+    int i;
+
+    optEntry *optEntryTable;  /* malloc'ed array */
+    
+    /* Count the entries in optStructTable[] */
+    for (i = 0; optStructTable[i].type != OPT_END && i < 500; i++);
+    count = i+1;
+
+    optEntryTable = (optEntry *) malloc(count * sizeof(optEntry));
+    if (optEntryTable) {
+        int i;
+        for (i = 0; i < count; i++) 
+            optEntryTable[i] = optStructToEntry(optStructTable[i]);
+    }
+    return(optEntryTable);
+}
+
+
+
+
+/*------------------------------------------------------------------------
+ |  NAME          optNeedsArgument
+ |
+ |  FUNCTION      Check if an option requires an argument.
+ |
+ |  INPUT         opt     the option to check.
+ |
+ |  RETURNS       Boolean value.
+ */
+static int
+optNeedsArgument(const optEntry opt)
+{
+    return opt.type == OPT_STRING
+	|| opt.type == OPT_INT
+	|| opt.type == OPT_UINT
+	|| opt.type == OPT_LONG
+	|| opt.type == OPT_ULONG
+    || opt.type == OPT_FLOAT
+    || opt.type == OPT_NAMELIST
+        ;
+}
+
+/*------------------------------------------------------------------------
+ |  NAME          argvRemove
+ |
+ |  FUNCTION      Remove an entry from an argv-array.
+ |
+ |  INPUT         argc    pointer to number of options.
+ |                argv    array of option-/argument-strings.
+ |                i       index of option to remove.
+ |
+ |  OUTPUT        argc    new argument count.
+ |                argv    array with given argument removed.
+ */
+static void
+argvRemove(int *argc, char *argv[], int i)
+{
+    if (i >= *argc)
+        return;
+    while (i++ < *argc)
+        argv[i - 1] = argv[i];
+    --*argc;
+}
+
+
+
+static void
+getToken(const char *  const tokenStart,
+         char          const delimiter,
+         const char ** const tokenP,
+         const char ** const nextP) {
+/*----------------------------------------------------------------------------
+   Find the token starting at 'tokenStart' up to but not including
+   the first 'delimiter' character or end of string.  Return it in newly
+   malloced memory as *tokenP, NUL-terminated.
+
+   Make *nextP point just past the token, i.e. to the delimiter or
+   end of string NUL character.
+
+   Note that if the string is empty, or starts with the delimiter,
+   we return an empty string and *nextP == tokenStart, i.e. *nextP
+   doesn't necessarily advance.
+-----------------------------------------------------------------------------*/
+    char * token;
+    const char * cursor;
+    unsigned int charCount;
+
+    /* Run through the token, counting characters */
+
+    charCount = 0;
+    cursor = tokenStart;
+
+    while (*cursor != delimiter && *cursor != '\0') {
+        if (*cursor == '\\') {
+            ++cursor;
+            if (*cursor == '\0')
+                optFatal("string ends with an escape character (\\)");
+        }
+        ++cursor;
+        ++charCount;
+    }
+    
+    token = malloc(charCount + 1);
+    if (token == NULL)
+        optFatal("Could not allocate %u bytes of memory to parse a string",
+                 charCount + 1);
+
+    /* Go back and do it again, this time copying the characters */
+    charCount = 0;
+    cursor = tokenStart;
+
+    while (*cursor != delimiter && *cursor != '\0') {
+        if (*cursor == '\\')
+            ++cursor;
+
+        assert(*cursor != '\0');
+
+        token[charCount++] = *cursor++;
+    }
+    token[charCount] = '\0';
+
+    *tokenP = token;
+    *nextP = cursor;
+}
+
+
+
+static void
+parseNameList(const char *           const listText,
+              struct optNameValue ** const listP) {
+
+    unsigned int const maxOptionCount = 100;
+
+    const char * cursor;
+    unsigned int optionCount;
+    struct optNameValue * list;
+
+    MALLOCARRAY_NOFAIL(list, maxOptionCount+1);
+
+    cursor = &listText[0];  /* initial value */
+
+    optionCount = 0;  /* initial value */
+
+    while (optionCount < maxOptionCount && *cursor != '\0') {
+        const char * next;
+        struct optNameValue pair;
+
+        getToken(cursor, '=', &pair.name, &next);
+
+        cursor = next;
+
+        if (*cursor == '\0')
+            optFatal("name=value option value ends prematurely.  An equal "
+                     "sign was expected following name '%s'", pair.name);
+
+        assert(*cursor == '=');
+        ++cursor;
+
+        getToken(cursor, ',', &pair.value, &next);
+
+        cursor = next;
+
+        list[optionCount++] = pair;
+
+        if (*cursor != '\0') {
+            assert(*cursor == ',');
+            ++cursor;
+        }
+    }
+    list[optionCount].name  = NULL;
+    list[optionCount].value = NULL;
+
+    *listP = list;
+}
+
+
+
+/*------------------------------------------------------------------------
+ |  NAME          optExecute
+ |
+ |  FUNCTION      Perform the action of an option.
+ |
+ |  INPUT         opt     element in array of defined options that 
+ |                        applies to this option
+ |                arg     argument to option, if it applies.
+ |                lng     was the option given as a long option?
+ |
+ |  RETURNS       Nothing. Aborts in case of error.
+ */
+static void
+optExecute(optEntry  const opt, char *arg, int lng)
+{
+    if (opt.specified)
+        (*(opt.specified))++;
+
+    switch (opt.type) {
+    case OPT_FLAG:
+        if (opt.arg)
+            *((int *) opt.arg) = 1;
+        break;
+
+    case OPT_STRING:
+        if (opt.arg)
+            *((char **) opt.arg) = arg;
+        break;
+
+    case OPT_INT:
+    case OPT_LONG: {
+        long tmp;
+        char *e;
+	  
+        if (arg == NULL)
+            optFatal("internal error: optExecute() called with NULL argument "
+                     "'%s'", optString(opt, lng));
+        tmp = strtol(arg, &e, 10);
+        if (*e)
+            optFatal("invalid number `%s'", arg);
+        if (errno == ERANGE
+            || (opt.type == OPT_INT && (tmp > INT_MAX || tmp < INT_MIN)))
+            optFatal("number `%s' to `%s' out of range",
+                     arg, optString(opt, lng));
+        if (opt.type == OPT_INT) {
+            *((int *) opt.arg) = (int) tmp;
+        } else /* OPT_LONG */ {
+            if (opt.arg)
+                *((long *) opt.arg) = tmp;
+        }
+    } break;
+	
+    case OPT_UINT:
+    case OPT_ULONG: {
+        unsigned long tmp;
+        char *e;
+        
+        if (arg == NULL)
+            optFatal("internal error: optExecute() called with NULL argument "
+                     "'%s'", optString(opt, lng));
+        tmp = strtoul(arg, &e, 10);
+        if (*e)
+            optFatal("invalid number `%s'", arg);
+        if (errno == ERANGE
+            || (opt.type == OPT_UINT && tmp > UINT_MAX))
+            optFatal("number `%s' to `%s' out of range",
+                     arg, optString(opt, lng));
+        if (opt.type == OPT_UINT) {
+           if (opt.arg)
+               *((unsigned *) opt.arg) = (unsigned) tmp;
+        } else /* OPT_ULONG */ {
+            if (opt.arg)
+                *((unsigned long *) opt.arg) = tmp;
+        }
+    } break;
+    case OPT_FLOAT: {
+        float tmp;
+        char *e;
+	  
+        if (arg == NULL)
+            optFatal("internal error: optExecute() called with NULL argument "
+                     "'%s'", optString(opt, lng));
+        tmp = strtod(arg, &e);
+        if (*e)
+            optFatal("invalid floating point number `%s'", arg);
+        if (errno == ERANGE)
+            optFatal("floating point number `%s' to `%s' out of range",
+                     arg, optString(opt, lng));
+        if (opt.arg)
+            *((float *) opt.arg) = tmp;
+    } break;
+    case OPT_NAMELIST: {
+        if (arg == NULL)
+            optFatal("internal error: optExecute() called with NULL argument "
+                     "'%s'", optString(opt, lng));
+
+        if (opt.arg)
+            parseNameList(arg, (struct optNameValue **)opt.arg);
+
+    } break;
+    default:
+        break;
+    }
+}
+
+
+
+/*-----------------------------------------------------------------------+
+|  PUBLIC FUNCTIONS                                                      |
++-----------------------------------------------------------------------*/
+
+/*------------------------------------------------------------------------
+ |  NAME          optSetFatalFunc
+ |
+ |  FUNCTION      Set function used to display error message and exit.
+ |
+ |  SYNOPSIS      #include "shhopt.h"
+ |                void optSetFatalFunc(void (*f)(const char *, ...));
+ |
+ |  INPUT         f       function accepting printf()'like parameters,
+ |                        that _must_ abort the program.
+ */
+void
+optSetFatalFunc(void (*f)(const char *, ...))
+{
+    optFatal = f;
+}
+
+
+/*------------------------------------------------------------------------
+ |  NAME          optParseOptions
+ |
+ |  FUNCTION      Parse commandline options.
+ |
+ |  SYNOPSIS      #include "shhopt.h"
+ |                void optParseOptions(int *argc, char *argv[],
+ |                                     optStruct opt[], int allowNegNum);
+ |
+ |  INPUT         argc    Pointer to number of options.
+ |                argv    Array of option-/argument-strings.
+ |                opt     Array of possible options.
+ |                allowNegNum
+ |                        a negative number is not to be taken as
+ |                        an option.
+ |
+ |  OUTPUT        argc    new argument count.
+ |                argv    array with arguments removed.
+ |
+ |  RETURNS       Nothing. Aborts in case of error.
+ |
+ |  DESCRIPTION   This function checks each option in the argv-array
+ |                against strings in the opt-array, and `executes' any
+ |                matching action. Any arguments to the options are
+ |                extracted and stored in the variables or passed to
+ |                functions pointed to by entries in opt.
+ |
+ |                Options and arguments used are removed from the argv-
+ |                array, and argc is decreased accordingly.
+ |
+ |                Any error leads to program abortion.
+ */
+void
+optParseOptions(int *argc, char *argv[], optStruct opt[], int allowNegNum)
+{
+    int  ai,        /* argv index. */
+         optarg,    /* argv index of option argument, or -1 if none. */
+         mi,        /* Match index in opt. */
+         done;
+    char *arg,      /* Pointer to argument to an option. */
+         *o,        /* pointer to an option character */
+         *p;
+
+    optEntry *opt_table;  /* malloc'ed array */
+
+    opt_table = optStructTblToEntryTbl(opt);
+    if (opt_table == NULL)
+        optFatal("Memory allocation failed (trying to allocate space for "
+                 "new-format option table)");
+
+    /*
+     *  Loop through all arguments.
+     */
+    for (ai = 0; ai < *argc; ) {
+        /*
+         *  "--" indicates that the rest of the argv-array does not
+         *  contain options.
+         */
+        if (strcmp(argv[ai], "--") == 0) {
+            argvRemove(argc, argv, ai);
+            break;
+        }
+
+        if (allowNegNum && argv[ai][0] == '-' && ISDIGIT(argv[ai][1])) {
+            ++ai;
+            continue;
+        } else if (strncmp(argv[ai], "--", 2) == 0) {
+            /* long option */
+            /* find matching option */
+            if ((mi = optMatch(opt_table, argv[ai] + 2, 1)) < 0)
+                optFatal("unrecognized option `%s'", argv[ai]);
+
+            /* possibly locate the argument to this option. */
+            arg = NULL;
+            if ((p = strchr(argv[ai], '=')) != NULL)
+                arg = p + 1;
+	    
+            /* does this option take an argument? */
+            optarg = -1;
+            if (optNeedsArgument(opt_table[mi])) {
+                /* option needs an argument. find it. */
+                if (!arg) {
+                    if ((optarg = ai + 1) == *argc)
+                        optFatal("option `%s' requires an argument",
+                                 optString(opt_table[mi], 1));
+                    arg = argv[optarg];
+                }
+            } else {
+                if (arg)
+                    optFatal("option `%s' doesn't allow an argument",
+                             optString(opt_table[mi], 1));
+            }
+            optExecute(opt_table[mi], arg, 1);
+            /* remove option and any argument from the argv-array. */
+            if (optarg >= 0)
+                argvRemove(argc, argv, ai);
+            argvRemove(argc, argv, ai);
+        } else if (*argv[ai] == '-') {
+            /* A dash by itself is not considered an option. */
+            if (argv[ai][1] == '\0') {
+                ++ai;
+                continue;
+            }
+            /* Short option(s) following */
+            o = argv[ai] + 1;
+            done = 0;
+            optarg = -1;
+            while (*o && !done) {
+                /* find matching option */
+                if ((mi = optMatch(opt_table, o, 0)) < 0)
+                    optFatal("unrecognized option `-%c'", *o);
+
+                /* does this option take an argument? */
+                optarg = -1;
+                arg = NULL;
+                if (optNeedsArgument(opt_table[mi])) {
+                    /* option needs an argument. find it. */
+                    arg = o + 1;
+                    if (!*arg) {
+                        if ((optarg = ai + 1) == *argc)
+                            optFatal("option `%s' requires an argument",
+                                     optString(opt_table[mi], 0));
+                        arg = argv[optarg];
+                    }
+                    done = 1;
+                }
+                /* perform the action of this option. */
+                optExecute(opt_table[mi], arg, 0);
+                ++o;
+            }
+            /* remove option and any argument from the argv-array. */
+            if (optarg >= 0)
+                argvRemove(argc, argv, ai);
+            argvRemove(argc, argv, ai);
+        } else {
+            /* a non-option argument */
+            ++ai;
+        }
+    }
+    free(opt_table);
+}
+
+
+static void
+parse_short_option_token(char *argv[], const int argc, const int ai,
+                         const optEntry opt_table[], 
+                         int * const tokens_consumed_p) {
+
+    char *o;  /* A short option character */
+    char *arg;
+    int mi;   /* index into option table */
+    unsigned char processed_arg;  /* boolean */
+        /* We processed an argument to one of the one-character options. 
+           This necessarily means there are no more options in this token
+           to process.
+           */
+
+    *tokens_consumed_p = 1;  /* initial assumption */
+
+    o = argv[ai] + 1;
+    processed_arg = 0;  /* initial value */
+    while (*o && !processed_arg) {
+		/* find matching option */
+		if ((mi = optMatch(opt_table, o, 0)) < 0)
+		    optFatal("unrecognized option `-%c'", *o);
+
+		/* does this option take an argument? */
+		if (optNeedsArgument(opt_table[mi])) {
+		    /* option needs an argument. find it. */
+		    arg = o + 1;
+		    if (!*arg) {
+                if (ai + 1 >= argc)
+			    optFatal("option `%s' requires an argument",
+				     optString(opt_table[mi], 0));
+			arg = argv[ai+1];
+            (*tokens_consumed_p)++;
+		    }
+		    processed_arg = 1;
+		} else 
+            arg = NULL;
+		/* perform the action of this option. */
+		optExecute(opt_table[mi], arg, 0);
+		++o;
+    }
+}
+
+
+
+static void
+parse_long_option(char *argv[], const int argc, const int ai,
+                  const int namepos,
+                  const optEntry opt_table[], 
+                  int * const tokens_consumed_p) {
+
+    char *equals_arg;
+      /* The argument of an option, included in the same token, after a
+         "=".  NULL if no "=" in the token.
+         */
+    char *arg;     /* The argument of an option; NULL if none */
+    int mi;    /* index into option table */
+
+    /* The current token is an option, and its name starts at
+       Index 'namepos' in the argument.
+    */
+    *tokens_consumed_p = 1;  /* initial assumption */
+    /* find matching option */
+    if ((mi = optMatch(opt_table, &argv[ai][namepos], 1)) < 0)
+        optFatal("unrecognized option `%s'", argv[ai]);
+            
+    /* possibly locate the argument to this option. */
+    { 
+        char *p;
+        if ((p = strchr(argv[ai], '=')) != NULL)
+            equals_arg = p + 1;
+        else 
+            equals_arg = NULL;
+    }
+    /* does this option take an argument? */
+    if (optNeedsArgument(opt_table[mi])) {
+        /* option needs an argument. find it. */
+        if (equals_arg)
+            arg = equals_arg;
+        else {
+            if (ai + 1 == argc)
+                optFatal("option `%s' requires an argument",
+                         optString(opt_table[mi], 1));
+            arg = argv[ai+1];
+            (*tokens_consumed_p)++;
+        }
+    } else {
+        if (equals_arg)
+            optFatal("option `%s' doesn't allow an argument",
+                     optString(opt_table[mi], 1));
+        else 
+            arg = NULL;
+    }
+    /* perform the action of this option. */
+    optExecute(opt_table[mi], arg, 1);
+}
+
+
+
+/*------------------------------------------------------------------------
+ |  NAME          optParseOptions2
+ |
+ |  FUNCTION      Parse commandline options.
+ |
+ |  SYNOPSIS      #include "shhopt.h"
+ |                void optParseOptions2(int *argc, char *argv[],
+ |                                      optStruct2 opt, unsigned long flags);
+ |
+ |  INPUT         argc    Pointer to number of options.
+ |                argv    Array of option-/argument-strings.
+ |                opt     Structure describing option syntax.
+ |                flags   Result is undefined if not zero.
+ |                        For future expansion.
+ |
+ |  OUTPUT        argc    new argument count.
+ |                argv    array with arguments removed.
+ |
+ |  RETURNS       Nothing. Aborts in case of error.
+ |
+ |  DESCRIPTION   This function checks each option in the argv-array
+ |                against strings in the opt-array, and `executes' any
+ |                matching action. Any arguments to the options are
+ |                extracted and stored in the variables or passed to
+ |                functions pointed to by entries in opt.
+ |
+ |                This differs from optParseOptions in that it accepts
+ |                long options with just one hyphen and doesn't accept
+ |                any short options.  It also has accomodations for 
+ |                future expansion.
+ |
+ |                Options and arguments used are removed from the argv-
+ |                array, and argc is decreased accordingly.
+ |
+ |                Any error leads to program abortion.
+ */
+void
+optParseOptions2(int * const argc_p, char *argv[], const optStruct2 opt, 
+                 const unsigned long flags)
+/*----------------------------------------------------------------------------
+   This does the same thing as optParseOptions3(), except that there is no
+   "specified" return value.  
+
+   This function exists for backward compatibility.
+-----------------------------------------------------------------------------*/
+
+{
+    optStruct3 opt3;
+
+    opt3.short_allowed = opt.short_allowed;
+    opt3.allowNegNum   = opt.allowNegNum;
+    opt3.opt_table     = optStructTblToEntryTbl(opt.opt_table);
+
+    if (opt3.opt_table == NULL)
+        optFatal("Memory allocation failed (trying to allocate space for "
+                 "new-format option table)");
+    
+    optParseOptions3(argc_p, argv, opt3, sizeof(opt3), flags);
+
+    free(opt3.opt_table);
+}
+
+
+
+
+static void
+zero_specified(optEntry opt_table[]) {
+/*----------------------------------------------------------------------------
+   Set all the "number of times specified" return values identified in the
+   option table opt_table[] to zero.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+
+    for (i = 0; opt_table[i].type != OPT_END; i++) {
+        if (opt_table[i].specified)
+            *(opt_table[i].specified) = 0;
+    }
+}
+
+
+
+/*------------------------------------------------------------------------
+ |  NAME          optParseOptions3
+ |
+ |  FUNCTION      Parse commandline options.
+ |
+ |  INPUT         argc    Pointer to number of options.
+ |                argv    Array of option-/argument-strings.
+ |                opt     Structure describing option syntax.
+ |                optStructSize
+ |                        Size of "opt" (since the caller may be older
+ |                        than this function, it may be using a structure
+ |                        with fewer fields than exist today.  We use this
+ |                        parameter to handle those older callers). 
+ |                flags   Result is undefined if not zero.
+ |                        For future expansion.
+ |
+ |  OUTPUT        argc    new argument count.
+ |                argv    array with arguments removed.
+ |                
+ |                Areas pointed to by pointers in 'opt' get updated with
+ |                option values and counts.
+ |
+ |  RETURNS       Nothing. Aborts in case of error.
+ |
+ |  DESCRIPTION   This function checks each option in the argv-array
+ |                against strings in the opt-array, and `executes' any
+ |                matching action. Any arguments to the options are
+ |                extracted and stored in the variables or passed to
+ |                functions pointed to by entries in opt.
+ |
+ |                This differs from optParseOptions in that it accepts
+ |                long options with just one hyphen and doesn't accept
+ |                any short options.  It also has accomodations for 
+ |                future expansion.
+ |
+ |                Options and arguments used are removed from the argv-
+ |                array, and argc is decreased accordingly.
+ |
+ |                Any error leads to program abortion.
+ */
+void
+optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt, 
+                 const unsigned int optStructSize, const unsigned long flags)
+{
+    int  ai;        /* argv index. */
+    int tokens_consumed;
+    unsigned char no_more_options;  /* boolean */
+        /* We've encountered the "no more options" token */
+
+    zero_specified(opt.opt_table);
+
+    /*
+     *  Loop through all arguments.
+     */
+    no_more_options = 0;  /* initial value */
+    for (ai = 0; ai < *argc_p; ) {
+        if (no_more_options) 
+            /* Can't be an option -- there aren't any more */
+            ai++;
+        else if (argv[ai][0] != '-') 
+            /* Can't be an option -- doesn't start with a dash */
+            ai++;
+        else {
+            /* It starts with a dash -- could be an option */
+            if (argv[ai][1] == '\0') {
+                /* A dash by itself is not considered an option. */
+                ++ai;
+                tokens_consumed = 0;
+            } else if (opt.allowNegNum && ISDIGIT(argv[ai][1])) {
+                /* It's a negative number parameter, not an option */
+                ++ai;
+                tokens_consumed = 0;
+            } else if (argv[ai][1] == '-') {
+                /* It starts with -- */
+                if (argv[ai][2] == '\0') {
+                    /* The entire thing is "--".  That means no more options */
+                    tokens_consumed = 1;
+                    no_more_options = 1;
+                } else 
+                    /* It's an option that starts with "--" */
+                    parse_long_option(argv, *argc_p, ai, 2,
+                                      opt.opt_table, &tokens_consumed);
+            } else {
+                if (opt.short_allowed) {
+                    /* It's a cluster of (one or more) short options */
+                    parse_short_option_token(argv, *argc_p, ai,
+                                             opt.opt_table, &tokens_consumed);
+                } else {
+                    /* It's a long option that starts with "-" */
+                    parse_long_option(argv, *argc_p, ai, 1,
+                                      opt.opt_table, &tokens_consumed);
+                }
+            
+            }
+            /* remove option and any argument from the argv-array. */
+            {
+                int i;
+                for (i = 0; i < tokens_consumed; i++)
+                    argvRemove(argc_p, argv, ai);
+            }
+        }
+    }
+}
+
+
+
+void
+optDestroyNameValueList(struct optNameValue * const list) {
+
+    unsigned int i;
+
+    for (i = 0; list[i].name; ++i) {
+        strfree(list[i].name);
+        strfree(list[i].value);
+    }
+
+    free(list);
+}
diff --git a/lib/util/shhopt.h b/lib/util/shhopt.h
new file mode 100644
index 00000000..fd15b53c
--- /dev/null
+++ b/lib/util/shhopt.h
@@ -0,0 +1,240 @@
+/*==============================================================================
+HERE IS AN EXAMPLE OF THE USE OF SHHOPT:
+
+
+#include <shhopt.h>
+int 
+main ( int argc, char **argv ) {
+
+    int help_flag = 7;
+    unsigned int help_spec =7;
+    unsigned int height_spec =7;
+    unsigned int name_spec= 7;
+    char *name= "initial";
+    int height=7;
+    int verbose_flag=7;
+    int debug_flag=7;
+    struct optNameValue * optlist;
+    
+    optStruct3 opt;
+    unsigned int option_def_index = 0;
+    optEntry *option_def = malloc(100*sizeof(option_def[0]));
+
+    OPTENT3(0,   "help",     OPT_FLAG,     &help_flag,    &help_spec,   0);
+    OPTENT3(0,   "height",   OPT_INT,      &height,       &height_spec, 0);
+    OPTENT3('n', "name",     OPT_STRING,   &name,         &name_spec,   0);
+    OPTENT3('v', "verbose",  OPT_FLAG,     &verbose_flag, NULL,         0);
+    OPTENT3('g', "debug",    OPT_FLAG,     &debug_flag,   NULL,         0);
+    OPTENT3(0,   "options",  OPT_NAMELIST, &optlist,      NULL,         0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = 1;
+    opt.allowNegNum = 1;
+
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    
+
+    printf("argc=%d\n", argc);
+    printf("help_flag=%d\n", help_flag);
+    printf("help_spec=%d\n", help_spec);
+    printf("height=%d\n", height);
+    printf("height_spec=%d\n", height_spec);
+    printf("name='%s'\n", name);
+    printf("name_spec=%d\n", name_spec);
+    printf("verbose_flag=%d\n", verbose_flag);
+    printf("debug_flag=%d\n", verbose_flag);
+
+    if (optlist) {
+        unsigned int i;
+        while (optlist[i].name) {
+            printf("option '%s' = '%s'\n", optlist[i].name, optlist[i].value);
+            ++i;
+        }
+        optDestroyNameValueList(optlist);
+    } else
+        printf("No -options\n");
+}
+
+Now run this program with something like
+
+  myprog -vg --name=Bryan --hei 4 "My first argument" --help
+
+  or do it with opt.short_allowed=0 and
+
+  myprog -v /etc/passwd -name=Bryan --hei=4
+
+
+========================================================================*/
+
+
+#ifndef SHHOPT_H
+#define SHHOPT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#if 0
+} /* to fake out automatic code indenters */
+#endif
+
+/* constants for recognized option types. */
+typedef enum {
+    OPT_END,               /* nothing. used as ending element. */
+    OPT_FLAG,              /* no argument following. sets variable to 1. */
+    OPT_STRING,            /* string argument. */
+    OPT_INT,               /* signed integer argument. */
+    OPT_UINT,              /* unsigned integer argument. */
+    OPT_LONG,              /* signed long integer argument. */
+    OPT_ULONG,             /* unsigned long integer argument. */
+    OPT_FLOAT,             /* floating point argument. */
+    OPT_NAMELIST           /* list like "key1=val1,key2=val2" */
+} optArgType;
+
+
+typedef struct {
+    /* This structure describes a single program option in a form for
+     use by the optParseOptions() or optParseOptions2() function.
+    */
+    char       shortName;  /* short option name. */
+    const char *longName;  /* long option name, not including '--'. */
+    optArgType type;       /* option type. */
+    void       *arg;       /* pointer to variable to fill with argument,
+                            * or pointer to function if type == OPT_FUNC. */
+    int        flags;      /* modifier flags. */
+} optStruct;
+    
+typedef struct {
+    /* This structure describes a single program option in a form for
+     use by the optParseOptions3() function.
+    */
+    char       shortName;  /* short option name. */
+    const char *longName;  /* long option name, not including '--' or '-' */
+    optArgType type;       /* option type. */
+    void       *arg;       
+        /* pointer to variable in which to return option's argument (or TRUE
+           if it's a flag option), or pointer to function if 
+           type == OPT_FUNC.  If the option is specified multiple times, only 
+           the rightmost one affects this return value.
+        */
+    unsigned int *specified;
+        /* pointer to variable in which to return the number of times that
+           the option was specified.  If NULL, don't return anything.
+        */
+    int        flags;      /* modifier flags. */
+} optEntry;
+    
+
+typedef struct {
+    /* This structure describes the options of a program in a form for
+       use with the optParseOptions2() function.
+       */
+    unsigned char short_allowed;  /* boolean */
+        /* The syntax may include short (i.e. one-character) options.
+           These options may be stacked within a single token (e.g.
+           -abc = -a -b -c).  If this value is not true, the short option
+           member of the option table entry is meaningless and long 
+           options may have either one or two dashes.
+           */
+    unsigned char allowNegNum;  /* boolean */
+        /* Anything that starts with - and then a digit is a numeric
+           parameter, not an option 
+           */
+    optStruct *opt_table;
+} optStruct2;
+
+typedef struct {
+    /* Same as optStruct2, but for optParseOptions3() */
+    unsigned char short_allowed;  
+    unsigned char allowNegNum;    
+    optEntry *opt_table;
+} optStruct3;
+
+
+/* You can use OPTENTRY to assign a value to a dynamically or automatically
+   allocated optStruct structure with minimal typing and easy readability.
+
+   Here is an example:
+
+       unsigned int option_def_index = 0;
+       optStruct *option_def = malloc(100*sizeof(optStruct));
+       OPTENTRY('h', "help",     OPT_FLAG, &help_flag, 0);
+       OPTENTRY(0,   "alphaout", OPT_STRING, &alpha_filename, 0);
+*/   
+
+/* If you name your variables option_def and option_def_index like in the 
+   example above, everything's easy.  If you want to use OPTENTRY with other
+   variables, define macros OPTION_DEF and OPTION_DEF_INDEX before calling
+   OPTENTRY.
+*/
+
+#ifndef OPTION_DEF
+#define OPTION_DEF option_def
+#endif
+#ifndef OPTION_DEF_INDEX
+#define OPTION_DEF_INDEX option_def_index
+#endif
+
+#define OPTENTRY(shortvalue,longvalue,typevalue,outputvalue,flagvalue) {\
+    OPTION_DEF[OPTION_DEF_INDEX].shortName = (shortvalue); \
+    OPTION_DEF[OPTION_DEF_INDEX].longName = (longvalue); \
+    OPTION_DEF[OPTION_DEF_INDEX].type = (typevalue); \
+    OPTION_DEF[OPTION_DEF_INDEX].arg = (outputvalue); \
+    OPTION_DEF[OPTION_DEF_INDEX].flags = (flagvalue); \
+    OPTION_DEF[++OPTION_DEF_INDEX].type = OPT_END; \
+    }
+
+/* OPTENT3 is the same as OPTENTRY except that it also sets the "specified"
+   element of the table entry (so it assumes OPTION_DEF is a table of
+   optEntry instead of optStruct).
+
+   Here is an example:
+
+       unsigned int option_def_index = 0;
+       optEntry *option_def = malloc(100*sizeof(optEntry));
+       OPTENT3('h', "help",     OPT_FLAG, &help_flag, 0);
+       OPTENT3(0,   "alphaout", OPT_STRING, &alpha_filename, 0);
+*/
+
+#define OPTENT3(shortvalue,longvalue,typevalue,outputvalue,specifiedvalue, \
+                flagvalue) {\
+    OPTION_DEF[OPTION_DEF_INDEX].specified = (specifiedvalue); \
+    OPTENTRY(shortvalue, longvalue, typevalue, outputvalue, flagvalue) \
+    }
+
+
+struct optNameValue {
+    const char * name;
+    const char * value;
+};
+
+
+        
+void optSetFatalFunc(void (*f)(const char *, ...));
+void optParseOptions(int *argc, char *argv[],
+		     optStruct opt[], int allowNegNum);
+void
+optParseOptions2(int * const argc_p, char *argv[], const optStruct2 opt, 
+                 const unsigned long flags);
+void
+optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt, 
+                 const unsigned int optStructSize, const unsigned long flags);
+
+void
+optDestroyNameValueList(struct optNameValue * const list);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Here's a hack to help us introduce pm_c_util.h.  That should be
+   #included in nearly every Netpbm program, but we're too lazy to insert
+   it into hundreds of files right now.  But shhopt.h is included into
+   most of those files, so we #include it here!
+
+   Before 2005.12.03, the stuff that is now in pm_c_util.h was in pm.h,
+   but that's a bad place for it because pm.h is an external header file.
+*/
+#include "pm_c_util.h"
+
+#endif
diff --git a/lib/util/testnstring.c b/lib/util/testnstring.c
new file mode 100644
index 00000000..87e6139e
--- /dev/null
+++ b/lib/util/testnstring.c
@@ -0,0 +1,30 @@
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "nstring.h"
+
+#define true (1)
+#define false (0)
+
+int
+main(int argc, char **argv) {
+
+    char snprintfNResult[80];
+    const char * asprintfNResult; 
+
+    printf("Hello world.\n");
+
+    snprintfN(snprintfNResult, 19, "snprintfN test truncated");
+
+    printf("snprintfN result='%s'\n", snprintfNResult);
+
+    asprintfN(&asprintfNResult, "asprintf'ed string %d %u %s", 
+              5, 89, "substring");
+
+    printf("asprintfN result='%s'\n", asprintfNResult);
+
+    strfree(asprintfNResult);
+    return 0;
+}
diff --git a/lib/util/wordaccess.h b/lib/util/wordaccess.h
new file mode 100644
index 00000000..28963aee
--- /dev/null
+++ b/lib/util/wordaccess.h
@@ -0,0 +1,65 @@
+#ifndef WORDACCESS_H_INCLUDED
+#define WORDACCESS_H_INCLUDED
+
+/* These are facilities for accessing data in C programs in ways that
+   exploit the way the machine defines words in order to squeeze out
+   speed and CPU efficiency.
+
+   In particular, routines in this file exploit the endianness of the
+   machine and use explicit machine instructions to access C
+   variables.
+
+   A word is the amount of data that fits in a register; the amount of
+   data that a single machine instruction can process.  For example,
+   on IA32, a word is 32 bits because a single load or store
+   instruction moves that many bits and a single add instruction
+   operates on that many bits.
+
+
+   These facilities revolve around two data types:  wordInt and
+   wordIntBytes.
+
+   wordint is an unsigned integer with precision (size) of one word.
+   It is just the number -- nothing is implied about how it is
+   represented in memory.
+
+   wordintBytes is an array of bytes that represent a word-sized
+   unsigned integer.  x[0] is the high order 8 digits of the binary
+   coding of the integer, x[1] the next highest 8 digits, etc.
+   Note that it has big-endian form, regardless of what endianness the
+   underlying machine uses.
+
+   The actual size of word differs by machine.  Usually it is 32 or 64
+   bits.  Logically it can be as small as one byte.  Fixed bit sequences
+   in each program impose a lower limit of word width.  For example, the
+   longest bit sequence in pbmtog3 has 13 bits, so an 8-bit word won't
+   work with that.
+
+   We also assume that a char is 8 bits.
+*/
+#if (!defined(WORDACCESS_GENERIC) \
+     && defined(__GNUC__) && defined(__GLIBC__) \
+     && (__GNUC__ * 100 + __GNUC_MINOR__ >= 304) )
+
+    #if BYTE_ORDER==BIG_ENDIAN    /* defined by GCC */
+
+        #include "wordaccess_gcc3_be.h"
+
+    #elif defined(__ia64__) || defined(__amd64__) || defined(__x86_64__)
+         /* all these macros are defined by GCC */
+
+        #include "wordaccess_64_le.h"
+
+    #else
+
+        #include "wordaccess_gcc3_le.h"
+
+    #endif
+
+#else
+
+    #include "wordaccess_generic.h"
+
+#endif
+
+#endif
diff --git a/lib/util/wordaccess_64_le.h b/lib/util/wordaccess_64_le.h
new file mode 100644
index 00000000..2343b6d4
--- /dev/null
+++ b/lib/util/wordaccess_64_le.h
@@ -0,0 +1,52 @@
+/*=============================================================================
+  This file is the part of wordaccess.h for use under these
+  conditions:
+
+  * GCC (>=3.4), GLIBC
+  * 64 bit Little-Endian machines (IA64, X86-64, AMD64)
+=============================================================================*/
+   
+/*  
+    64 bit hton and ntoh do not exist.  Here we use bswap_64.
+    
+    While bswap_64 works on 64 bit data, __builtin_clzl works on "long" which
+    may or may not be 64 bits.  Code provided to find the right data type and
+    file off any extra when necessary.
+*/
+
+#include <byteswap.h>  /* See note above on bswap_64 */
+ 
+typedef uint64_t wordint;
+typedef unsigned char wordintBytes[sizeof(wordint)];
+
+static __inline__ wordint
+bytesToWordint(wordintBytes bytes) {
+    return ((wordint) bswap_64(*(wordint *)bytes));
+}
+
+
+
+static __inline__ void
+wordintToBytes(wordintBytes * const bytesP,
+               wordint        const wordInt) {
+    *(wordint *)bytesP = bswap_64(wordInt);
+}
+
+
+
+static __inline__ unsigned int
+wordintClz(wordint const x){
+
+    unsigned int s;
+
+    if (x == 0)
+        return sizeof(wordint) * 8;
+
+    /* Find the data type closest to 64 bits, and file off any extra. */
+    else if ((s=sizeof(long int)) >= 8)
+        return (__builtin_clzl((int)x << (s - 8) * 8));
+    else if ((s=sizeof(long long int)) >= 8)
+        return (__builtin_clzll((long long int)x << (s - 8) * 8));
+    else
+        pm_error("Long long int is less than 64 bits on this machine"); 
+}
diff --git a/lib/util/wordaccess_gcc3_be.h b/lib/util/wordaccess_gcc3_be.h
new file mode 100644
index 00000000..6f5d86fc
--- /dev/null
+++ b/lib/util/wordaccess_gcc3_be.h
@@ -0,0 +1,40 @@
+/*=============================================================================
+  This file is the part of wordaccess.h for use under these
+  conditions:
+
+  * GCC (>=3.4), GLIBC
+  * Big-Endian machines
+  
+  __builtin_clz is available on GCC 3.4 and above
+     
+  Note that the clz scheme does not work and requires adjustment
+  if long type does not make use of all bits for data storage.
+  
+  This is unlikely.  According to GNU MP (http://www.swox.com/gmp/),
+  in rare cases such as Cray, there are smaller data types that take up
+  the same space as long, but leave the higher bits silent.  Currently,
+  there are no known such cases for data type long.
+*===========================================================================*/
+
+typedef unsigned long int wordint;
+typedef unsigned char wordintBytes[sizeof(wordint)];
+
+static __inline__ wordint
+bytesToWordint(wordintBytes bytes) {
+    return *((wordint *)bytes);
+}
+
+
+
+static __inline__ void
+wordintToBytes(wordintBytes * const bytesP,
+               wordint        const wordInt) {
+    *(wordint *)bytesP = wordInt;
+}
+
+
+
+static __inline__ unsigned int
+wordintClz(wordint const x) {
+    return (x==0 ? sizeof(wordint)*8 : __builtin_clzl(x));
+}
diff --git a/lib/util/wordaccess_gcc3_le.h b/lib/util/wordaccess_gcc3_le.h
new file mode 100644
index 00000000..7db218db
--- /dev/null
+++ b/lib/util/wordaccess_gcc3_le.h
@@ -0,0 +1,54 @@
+/*=============================================================================
+
+  This file is the part of wordaccess.h for use under these
+  conditions:
+
+  * GCC (>=3.4), GLIBC
+  * 32 bit Little-Endian machines (intel MPUs 80386, Pentium, etc.)
+  * Other non-Big-Endian machines (very rare)
+  
+=============================================================================*/
+
+typedef uint32_t wordint;
+typedef unsigned char wordintBytes[sizeof(wordint)];
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+/*
+  Here we use the more widely used functions htonl and ntohl instead of
+  bswap_32.  This makes possible the handling of weird byte ordering
+  (neither Big-Endian nor Little-Endian) schemes, if any.
+*/
+
+static __inline__ wordint
+bytesToWordint(wordintBytes const bytes) {
+    return (wordint) ntohl(*(wordint *)bytes);
+}
+
+
+
+static __inline__ void
+wordintToBytes(wordintBytes * const bytesP,
+               wordint        const wordInt) {
+
+    *(wordint *)bytesP = htonl(wordInt);
+}
+
+
+
+static __inline__ unsigned int
+wordintClz(wordint const x) {
+
+    /* Find the data type closest to 32 bits, and file off any extra.  */
+
+    if (x == 0)
+        return sizeof(wordint) * 8;
+    else if (sizeof(int) >= 4)
+        return __builtin_clz((int)x << (sizeof(int) - 4) * 8);
+    else if (sizeof(long int) >= 4)
+        return __builtin_clzl((long int)x << (sizeof(long int) - 4) * 8);
+    else
+        pm_error("Long int is less than 32 bits on this machine"); 
+}
+
diff --git a/lib/util/wordaccess_generic.h b/lib/util/wordaccess_generic.h
new file mode 100644
index 00000000..7f27ef74
--- /dev/null
+++ b/lib/util/wordaccess_generic.h
@@ -0,0 +1,89 @@
+/*=============================================================================
+
+  This file is the part of wordaccess.h for use under these
+  conditions:
+
+  * Compilers other than GCC
+  * GCC before version 3.4
+  * c libraries other than Glibc
+  * Specified by the user with WORDACCESS_GENERIC
+=============================================================================*/
+
+typedef uint32_t wordint;
+typedef unsigned char wordintBytes[sizeof(wordint)];
+    
+static __inline__ wordint
+bytesToWordint(wordintBytes const bytes) {
+    wordint retval;
+    unsigned int i;
+
+    /* Note that 'bytes' is a pointer, due to C array degeneration.
+       That means sizeof(bytes) isn't what you think it is.
+    */
+    
+    for (i = 1, retval = bytes[0]; i < sizeof(wordint); ++i) {
+        retval = (retval << 8) + bytes[i];
+    }
+    return retval;
+}
+
+
+
+static __inline__ void
+wordintToBytes(wordintBytes * const bytesP,
+               wordint        const wordInt) {
+
+    wordint buffer;
+    int i;
+
+    for (i = sizeof(*bytesP)-1, buffer = wordInt; i >= 0; --i) {
+        (*bytesP)[i] = buffer & 0xFF;
+        buffer >>= 8;
+    }
+}
+    
+static unsigned char const clz8[256]= {
+    8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 
+};
+
+
+
+static __inline__ unsigned int
+clz16(wordint const x) {
+    if (x >> 8  != 0)
+        return clz8[x >> 8];
+    else
+        return clz8[x] + 8;
+}
+
+
+
+static __inline__  unsigned int
+clz32(wordint const x) {
+    if (x >> 16  != 0)
+        return clz16(x >> 16);
+    else
+        return clz16(x) +16;
+}
+
+
+
+static __inline__  unsigned int
+wordintClz(wordint const x) {
+    return clz32(x);
+}