summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am33
-rw-r--r--configure.ac10
-rw-r--r--m4/snprintf.m455
-rw-r--r--m4/vamacros.m443
-rw-r--r--portable/asprintf.c60
-rw-r--r--portable/dummy.c21
-rw-r--r--portable/snprintf.c944
-rw-r--r--system.h45
-rw-r--r--tests/TESTS5
-rw-r--r--tests/libtest.c206
-rw-r--r--tests/libtest.h56
-rw-r--r--tests/portable/asprintf-t.c51
-rw-r--r--tests/portable/asprintf.c2
-rw-r--r--tests/portable/snprintf-t.c197
-rw-r--r--tests/portable/snprintf.c2
-rw-r--r--tests/util/concat-t.c54
-rw-r--r--tests/util/messages-t.c278
-rw-r--r--tests/util/xmalloc-t.in107
-rw-r--r--tests/util/xmalloc.c294
-rw-r--r--util/concat.c76
-rw-r--r--util/messages.c361
-rw-r--r--util/util.h174
-rw-r--r--util/xmalloc.c238
23 files changed, 3307 insertions, 5 deletions
diff --git a/Makefile.am b/Makefile.am
index 4087d79..7716ba1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -6,6 +6,7 @@
# See README for licensing terms.
AUTOMAKE_OPTIONS = foreign subdir-objects
+ACLOCAL_AMFLAGS = -I m4
EXTRA_DIST = TODO client/wallet.pod docs/design docs/design-acl \
docs/design-api docs/netdb-role-api docs/notes docs/setup \
kasetkey/kasetkey.pod perl/Wallet/ACL.pm perl/Wallet/ACL/Base.pm \
@@ -17,6 +18,12 @@ EXTRA_DIST = TODO client/wallet.pod docs/design docs/design-acl \
tests/client/basic-t.in tests/data/README tests/data/cmd-fake \
tests/data/fake-keytab tests/data/wallet.conf
+noinst_LIBRARIES = portable/libportable.a util/libutil.a
+portable_libportable_a_SOURCES = portable/dummy.c
+portable_libportable_a_LIBADD = $(LIBOBJS)
+util_libutil_a_SOURCES = util/concat.c util/messages.c util/util.h \
+ util/xmalloc.c
+
bin_PROGRAMS = client/wallet
sbin_PROGRAMS = kasetkey/kasetkey
sbin_SCRIPTS = server/keytab-backend server/wallet-backend
@@ -24,10 +31,11 @@ client_wallet_SOURCES = client/internal.h client/srvtab.c client/wallet.c \
system.h
client_wallet_CPPFLAGS = @REMCTL_CPPFLAGS@
client_wallet_LDFLAGS = @REMCTL_LDFLAGS@
-client_wallet_LDADD = -lremctl -lkrb5
+client_wallet_LDADD = util/libutil.a portable/libportable.a -lremctl -lkrb5
kasetkey_kasetkey_CPPFLAGS = @AFS_CPPFLAGS@
kasetkey_kasetkey_LDFLAGS = @AFS_LDFLAGS@
-kasetkey_kasetkey_LDADD = @AFS_LIBS@ -lkrb4
+kasetkey_kasetkey_LDADD = util/libutil.a portable/libportable.a @AFS_LIBS@ \
+ -lkrb4
dist_man_MANS = client/wallet.1 kasetkey/kasetkey.8 server/keytab-backend.8 \
server/wallet-backend.8
@@ -79,7 +87,26 @@ clean-local:
cd perl && ( [ ! -f Makefile.old ] || mv Makefile.old Makefile )
# The bits below are for the test suite, not for the main package.
-check_PROGRAMS = tests/runtests
+check_PROGRAMS = tests/runtests tests/portable/asprintf-t \
+ tests/portable/snprintf-t tests/util/concat-t tests/util/messages-t \
+ tests/util/xmalloc
+check_LIBRARIES = tests/libtest.a
+tests_libtest_a_SOURCES = tests/libtest.c tests/libtest.h
+
+# All of the test programs.
+tests_portable_asprintf_t_SOURCES = tests/portable/asprintf-t.c \
+ tests/portable/asprintf.c
+tests_portable_asprintf_t_LDADD = tests/libtest.a util/libutil.a \
+ portable/libportable.a
+tests_portable_snprintf_t_SOURCES = tests/portable/snprintf-t.c \
+ tests/portable/snprintf.c
+tests_portable_snprintf_t_LDADD = tests/libtest.a util/libutil.a \
+ portable/libportable.a
+tests_util_concat_t_LDADD = tests/libtest.a util/libutil.a \
+ portable/libportable.a
+tests_util_messages_t_LDADD = tests/libtest.a util/libutil.a \
+ portable/libportable.
+tests_util_xmalloc_LDADD = util/libutil.a portable/libportable.a
check-local: $(check_PROGRAMS)
cd tests && ./runtests TESTS
diff --git a/configure.ac b/configure.ac
index 2a8361f..7ed2988 100644
--- a/configure.ac
+++ b/configure.ac
@@ -70,7 +70,13 @@ LDFLAGS="$save_LDFLAGS"
AC_SUBST([AFS_LIBS])
AC_CHECK_HEADERS([kerberosIV/krb.h])
+AC_CHECK_DECLS([snprintf, vsnprintf])
AC_CHECK_DECLS([ubik_Call], , , [#include <ubik.h>])
+RRA_C_C99_VAMACROS
+RRA_C_GNU_VAMACROS
+RRA_FUNC_SNPRINTF
+AC_REPLACE_FUNCS([asprintf])
+
save_LIBS=$LIBS
LIBS=-lkrb5
AC_CHECK_FUNCS([krb5_get_error_message \
@@ -78,9 +84,13 @@ AC_CHECK_FUNCS([krb5_get_error_message \
krb5_get_err_text])
LIBS=$save_LIBS
+dnl Needed to get prototypes for functions like asprintf on Linux.
+AC_DEFINE([_GNU_SOURCE], [1], [Define to 1 on Linux to get full prototypes.])
+
AC_CONFIG_HEADER([config.h])
AC_CONFIG_FILES([Makefile perl/Makefile.PL])
AC_CONFIG_FILES([tests/client/basic-t], [chmod +x tests/client/basic-t])
AC_CONFIG_FILES([tests/server/backend-t], [chmod +x tests/server/backend-t])
AC_CONFIG_FILES([tests/server/keytab-t], [chmod +x tests/server/keytab-t])
+AC_CONFIG_FILES([tests/util/xmalloc-t], [chmod +x tests/util/xmalloc-t])
AC_OUTPUT
diff --git a/m4/snprintf.m4 b/m4/snprintf.m4
new file mode 100644
index 0000000..a09833f
--- /dev/null
+++ b/m4/snprintf.m4
@@ -0,0 +1,55 @@
+dnl snprintf.m4 -- Test for a working C99 snprintf.
+dnl $Id$
+dnl
+dnl Check for a working snprintf. Some systems have an snprintf that doesn't
+dnl nul-terminate if the buffer isn't large enough. Others return -1 if the
+dnl string doesn't fit into the buffer instead of returning the number of
+dnl characters that would have been formatted. Still others don't support
+dnl NULL as the buffer argument (just to get a count of the formatted length).
+dnl
+dnl Provides RRA_FUNC_SNPRINTF, which adds snprintf.o to LIBOBJS unless a
+dnl fully working snprintf is found.
+dnl
+dnl Written by Russ Allbery <rra@stanford.edu>
+dnl Copyright 2006 Board of Trustees, Leland Stanford Jr. University
+dnl See README for licensing terms.
+
+dnl Source used by RRA_FUNC_SNPRINTF.
+define([_RRA_FUNC_SNPRINTF_SOURCE],
+[[#include <stdio.h>
+#include <stdarg.h>
+
+char buf[2];
+
+int
+test(char *format, ...)
+{
+ va_list args;
+ int count;
+
+ va_start(args, format);
+ count = vsnprintf(buf, sizeof buf, format, args);
+ va_end(args);
+ return count;
+}
+
+int
+main()
+{
+ return ((test("%s", "abcd") == 4 && buf[0] == 'a' && buf[1] == '\0'
+ && snprintf(NULL, 0, "%s", "abcd") == 4) ? 0 : 1);
+}]])
+
+dnl The user-callable test.
+AC_DEFUN([RRA_FUNC_SNPRINTF],
+[AC_CACHE_CHECK([for working snprintf], [rra_cv_func_snprintf_works],
+ [AC_TRY_RUN(_RRA_FUNC_SNPRINTF_SOURCE(),
+ [rra_cv_func_snprintf_works=yes],
+ [rra_cv_func_snprintf_works=no],
+ [rra_cv_func_snprintf_works=no])])
+if test "$rra_cv_func_snprintf_works" = yes ; then
+ AC_DEFINE([HAVE_SNPRINTF], 1,
+ [Define if your system has a working snprintf function.])
+else
+ AC_LIBOBJ([snprintf])
+fi])
diff --git a/m4/vamacros.m4 b/m4/vamacros.m4
new file mode 100644
index 0000000..1dd0f9f
--- /dev/null
+++ b/m4/vamacros.m4
@@ -0,0 +1,43 @@
+dnl vamacros.m4 -- Check for support for variadic macros.
+dnl $Id$
+dnl
+dnl This file defines two macros for probing for compiler support for variadic
+dnl macros. Provided are RRA_C_C99_VAMACROS, which checks for support for the
+dnl C99 variadic macro syntax, namely:
+dnl
+dnl #define macro(...) fprintf(stderr, __VA_ARGS__)
+dnl
+dnl and RRA_C_GNU_VAMACROS, which checks for support for the older GNU
+dnl variadic macro syntax, namely:
+dnl
+dnl #define macro(args...) fprintf(stderr, args)
+dnl
+dnl They set HAVE_C99_VAMACROS or HAVE_GNU_VAMACROS as appropriate.
+dnl
+dnl Written by Russ Allbery <rra@stanford.edu>
+dnl Copyright 2006 Board of Trustees, Leland Stanford Jr. University
+dnl See README for licensing terms.
+
+AC_DEFUN([RRA_C_C99_VAMACROS],
+[AC_CACHE_CHECK([for C99 variadic macros], [rra_cv_c_c99_vamacros],
+[AC_TRY_COMPILE(
+[#include <stdio.h>
+#define error(...) fprintf(stderr, __VA_ARGS__)],
+[error("foo"); error("foo %d", 0); return 0;],
+[rra_cv_c_c99_vamacros=yes], [rra_cv_c_c99_vamacros=no])])
+if test $rra_cv_c_c99_vamacros = yes ; then
+ AC_DEFINE([HAVE_C99_VAMACROS], 1,
+ [Define if the compiler supports C99 variadic macros.])
+fi])
+
+AC_DEFUN([RRA_C_GNU_VAMACROS],
+[AC_CACHE_CHECK([for GNU-style variadic macros], [rra_cv_c_gnu_vamacros],
+[AC_TRY_COMPILE(
+[#include <stdio.h>
+#define error(args...) fprintf(stderr, args)],
+[error("foo"); error("foo %d", 0); return 0;],
+[rra_cv_c_gnu_vamacros=yes], [rra_cv_c_gnu_vamacros=no])])
+if test $rra_cv_c_gnu_vamacros = yes ; then
+ AC_DEFINE([HAVE_GNU_VAMACROS], 1,
+ [Define if the compiler supports GNU-style variadic macros.])
+fi])
diff --git a/portable/asprintf.c b/portable/asprintf.c
new file mode 100644
index 0000000..56a69c4
--- /dev/null
+++ b/portable/asprintf.c
@@ -0,0 +1,60 @@
+/* $Id$
+**
+** Replacement for a missing asprintf and vasprintf.
+**
+** Written by Russ Allbery <rra@stanford.edu>
+** This work is hereby placed in the public domain by its author.
+**
+** Provides the same functionality as the standard GNU library routines
+** asprintf and vasprintf for those platforms that don't have them.
+*/
+
+#include <config.h>
+#include <system.h>
+
+/* If we're running the test suite, rename the functions to avoid conflicts
+ with the system versions. */
+#if TESTING
+# define asprintf test_asprintf
+# define vasprintf test_vasprintf
+int test_asprintf(char **, const char *, ...);
+int test_vasprintf(char **, const char *, va_list);
+#endif
+
+int
+asprintf(char **strp, const char *fmt, ...)
+{
+ va_list args;
+ int status;
+
+ va_start(args, fmt);
+ status = vasprintf(strp, fmt, args);
+ va_end(args);
+ return status;
+}
+
+int
+vasprintf(char **strp, const char *fmt, va_list args)
+{
+ va_list args_copy;
+ int status, needed;
+
+ va_copy(args_copy, args);
+ needed = vsnprintf(NULL, 0, fmt, args_copy);
+ va_end(args_copy);
+ if (needed < 0) {
+ *strp = NULL;
+ return needed;
+ }
+ *strp = malloc(needed + 1);
+ if (*strp == NULL)
+ return -1;
+ status = vsnprintf(*strp, needed + 1, fmt, args);
+ if (status >= 0)
+ return status;
+ else {
+ free(*strp);
+ *strp = NULL;
+ return status;
+ }
+}
diff --git a/portable/dummy.c b/portable/dummy.c
new file mode 100644
index 0000000..e5a6224
--- /dev/null
+++ b/portable/dummy.c
@@ -0,0 +1,21 @@
+/* $Id$
+**
+** Dummy symbol to prevent an empty library.
+**
+** Written by Russ Allbery <rra@stanford.edu>
+** This work is hereby placed in the public domain by its author.
+**
+** On platforms that already have all of the functions that libportable would
+** supply, Automake builds an empty library and then calls ar with
+** nonsensical arguments. Ensure that libportable always contains at least
+** one symbol.
+*/
+
+/* Prototype to avoid gcc warnings. */
+int portable_dummy(void);
+
+int
+portable_dummy(void)
+{
+ return 42;
+}
diff --git a/portable/snprintf.c b/portable/snprintf.c
new file mode 100644
index 0000000..bef3da7
--- /dev/null
+++ b/portable/snprintf.c
@@ -0,0 +1,944 @@
+/* $Id$
+**
+** Replacement for a missing snprintf or vsnprintf.
+**
+** The following implementation of snprintf was taken mostly verbatim from
+** <http://www.fiction.net/~blong/programs/>; it is the version of snprintf
+** used in Mutt.
+**
+** Please do not reformat or otherwise change this file more than
+** necessary so that later merges with the original source are easy.
+** Bug fixes and improvements should be sent back to the original author.
+*/
+
+/* If we're running the test suite, rename snprintf and vsnprintf to avoid
+ conflicts with the system version. */
+#if TESTING
+# define snprintf test_snprintf
+# define vsnprintf test_vsnprintf
+#endif
+
+/*
+ * Copyright Patrick Powell 1995
+ * This code is based on code written by Patrick Powell (papowell@astart.com)
+ * It may be used for any purpose as long as this notice remains intact
+ * on all source code distributions
+ */
+
+/**************************************************************
+ * Original:
+ * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
+ * A bombproof version of doprnt (dopr) included.
+ * Sigh. This sort of thing is always nasty do deal with. Note that
+ * the version here does not include floating point...
+ *
+ * snprintf() is used instead of sprintf() as it does limit checks
+ * for string length. This covers a nasty loophole.
+ *
+ * The other functions are there to prevent NULL pointers from
+ * causing nast effects.
+ *
+ * More Recently:
+ * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
+ * This was ugly. It is still ugly. I opted out of floating point
+ * numbers, but the formatter understands just about everything
+ * from the normal C string format, at least as far as I can tell from
+ * the Solaris 2.5 printf(3S) man page.
+ *
+ * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
+ * Ok, added some minimal floating point support, which means this
+ * probably requires libm on most operating systems. Don't yet
+ * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
+ * was pretty badly broken, it just wasn't being exercised in ways
+ * which showed it, so that's been fixed. Also, formated the code
+ * to mutt conventions, and removed dead code left over from the
+ * original. Also, there is now a builtin-test, just compile with:
+ * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
+ * and run snprintf for results.
+ *
+ * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
+ * The PGP code was using unsigned hexadecimal formats.
+ * Unfortunately, unsigned formats simply didn't work.
+ *
+ * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
+ * The original code assumed that both snprintf() and vsnprintf() were
+ * missing. Some systems only have snprintf() but not vsnprintf(), so
+ * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
+ *
+ * Andrew Tridgell (tridge@samba.org) Oct 1998
+ * fixed handling of %.0f
+ * added test for HAVE_LONG_DOUBLE
+ *
+ * Russ Allbery <rra@stanford.edu> 2000-08-26
+ * fixed return value to comply with C99
+ * fixed handling of snprintf(NULL, ...)
+ *
+ * Hrvoje Niksic <hniksic@arsdigita.com> 2000-11-04
+ * include <stdio.h> for NULL.
+ * added support for long long.
+ * don't declare argument types to (v)snprintf if stdarg is not used.
+ *
+ * Hrvoje Niksic <hniksic@xemacs.org> 2005-04-15
+ * use the PARAMS macro to handle prototypes.
+ * write function definitions in the ansi2knr-friendly way.
+ * if string precision is specified, don't read VALUE past it.
+ * fix bug in fmtfp that caused 0.01 to be printed as 0.1.
+ * don't include <ctype.h> because none of it is used.
+ * interpret precision as number of significant digits with %g
+ * omit trailing decimal zeros with %g
+ *
+ **************************************************************/
+
+#include <config.h>
+
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+
+#ifndef NULL
+# define NULL 0
+#endif
+
+/* varargs declarations: */
+
+#include <stdarg.h>
+#define HAVE_STDARGS /* let's hope that works everywhere (mj) */
+#define VA_LOCAL_DECL va_list ap
+#define VA_START(f) va_start(ap, f)
+#define VA_SHIFT(v,t) ; /* no-op for ANSI */
+#define VA_END va_end(ap)
+
+#ifdef HAVE_LONG_DOUBLE
+#define LDOUBLE long double
+#else
+#define LDOUBLE double
+#endif
+
+#ifdef HAVE_LONG_LONG
+# define LLONG long long
+#else
+# define LLONG long
+#endif
+
+int snprintf (char *str, size_t count, const char *fmt, ...);
+int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
+
+static int dopr (char *buffer, size_t maxlen, const char *format,
+ va_list args);
+static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
+ const char *value, int flags, int min, int max);
+static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
+ LLONG value, int base, int min, int max, int flags);
+static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+ LDOUBLE fvalue, int min, int max, int flags);
+static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
+
+/*
+ * dopr(): poor man's version of doprintf
+ */
+
+/* format read states */
+#define DP_S_DEFAULT 0
+#define DP_S_FLAGS 1
+#define DP_S_MIN 2
+#define DP_S_DOT 3
+#define DP_S_MAX 4
+#define DP_S_MOD 5
+#define DP_S_MOD_L 6
+#define DP_S_CONV 7
+#define DP_S_DONE 8
+
+/* format flags - Bits */
+#define DP_F_MINUS (1 << 0)
+#define DP_F_PLUS (1 << 1)
+#define DP_F_SPACE (1 << 2)
+#define DP_F_NUM (1 << 3)
+#define DP_F_ZERO (1 << 4)
+#define DP_F_UP (1 << 5)
+#define DP_F_UNSIGNED (1 << 6)
+#define DP_F_FP_G (1 << 7)
+
+/* Conversion Flags */
+#define DP_C_SHORT 1
+#define DP_C_LONG 2
+#define DP_C_LLONG 3
+#define DP_C_LDOUBLE 4
+
+#define char_to_int(p) (p - '0')
+#define MAX(p,q) ((p >= q) ? p : q)
+#define MIN(p,q) ((p <= q) ? p : q)
+
+static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
+{
+ char ch;
+ LLONG value;
+ LDOUBLE fvalue;
+ char *strvalue;
+ int min;
+ int max;
+ int state;
+ int flags;
+ int cflags;
+ int total;
+ size_t currlen;
+
+ state = DP_S_DEFAULT;
+ currlen = flags = cflags = min = 0;
+ max = -1;
+ ch = *format++;
+ total = 0;
+
+ while (state != DP_S_DONE)
+ {
+ if (ch == '\0')
+ state = DP_S_DONE;
+
+ switch(state)
+ {
+ case DP_S_DEFAULT:
+ if (ch == '%')
+ state = DP_S_FLAGS;
+ else
+ total += dopr_outch (buffer, &currlen, maxlen, ch);
+ ch = *format++;
+ break;
+ case DP_S_FLAGS:
+ switch (ch)
+ {
+ case '-':
+ flags |= DP_F_MINUS;
+ ch = *format++;
+ break;
+ case '+':
+ flags |= DP_F_PLUS;
+ ch = *format++;
+ break;
+ case ' ':
+ flags |= DP_F_SPACE;
+ ch = *format++;
+ break;
+ case '#':
+ flags |= DP_F_NUM;
+ ch = *format++;
+ break;
+ case '0':
+ flags |= DP_F_ZERO;
+ ch = *format++;
+ break;
+ default:
+ state = DP_S_MIN;
+ break;
+ }
+ break;
+ case DP_S_MIN:
+ if ('0' <= ch && ch <= '9')
+ {
+ min = 10*min + char_to_int (ch);
+ ch = *format++;
+ }
+ else if (ch == '*')
+ {
+ min = va_arg (args, int);
+ ch = *format++;
+ state = DP_S_DOT;
+ }
+ else
+ state = DP_S_DOT;
+ break;
+ case DP_S_DOT:
+ if (ch == '.')
+ {
+ state = DP_S_MAX;
+ ch = *format++;
+ }
+ else
+ state = DP_S_MOD;
+ break;
+ case DP_S_MAX:
+ if ('0' <= ch && ch <= '9')
+ {
+ if (max < 0)
+ max = 0;
+ max = 10*max + char_to_int (ch);
+ ch = *format++;
+ }
+ else if (ch == '*')
+ {
+ max = va_arg (args, int);
+ ch = *format++;
+ state = DP_S_MOD;
+ }
+ else
+ state = DP_S_MOD;
+ break;
+ case DP_S_MOD:
+ switch (ch)
+ {
+ case 'h':
+ cflags = DP_C_SHORT;
+ ch = *format++;
+ break;
+ case 'l':
+ cflags = DP_C_LONG;
+ ch = *format++;
+ break;
+ case 'L':
+ cflags = DP_C_LDOUBLE;
+ ch = *format++;
+ break;
+ default:
+ break;
+ }
+ if (cflags != DP_C_LONG)
+ state = DP_S_CONV;
+ else
+ state = DP_S_MOD_L;
+ break;
+ case DP_S_MOD_L:
+ switch (ch)
+ {
+ case 'l':
+ cflags = DP_C_LLONG;
+ ch = *format++;
+ break;
+ default:
+ break;
+ }
+ state = DP_S_CONV;
+ break;
+ case DP_S_CONV:
+ switch (ch)
+ {
+ case 'd':
+ case 'i':
+ if (cflags == DP_C_SHORT)
+ value = (short int) va_arg (args, int);
+ else if (cflags == DP_C_LONG)
+ value = va_arg (args, long int);
+ else if (cflags == DP_C_LLONG)
+ value = va_arg (args, LLONG);
+ else
+ value = va_arg (args, int);
+ total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+ break;
+ case 'o':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_SHORT)
+ value = (unsigned short int) va_arg (args, unsigned int);
+ else if (cflags == DP_C_LONG)
+ value = va_arg (args, unsigned long int);
+ else if (cflags == DP_C_LLONG)
+ value = va_arg (args, unsigned LLONG);
+ else
+ value = va_arg (args, unsigned int);
+ total += fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
+ break;
+ case 'u':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_SHORT)
+ value = (unsigned short int) va_arg (args, unsigned int);
+ else if (cflags == DP_C_LONG)
+ value = va_arg (args, unsigned long int);
+ else if (cflags == DP_C_LLONG)
+ value = va_arg (args, unsigned LLONG);
+ else
+ value = va_arg (args, unsigned int);
+ total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+ break;
+ case 'X':
+ flags |= DP_F_UP;
+ case 'x':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_SHORT)
+ value = (unsigned short int) va_arg (args, unsigned int);
+ else if (cflags == DP_C_LONG)
+ value = va_arg (args, unsigned long int);
+ else if (cflags == DP_C_LLONG)
+ value = va_arg (args, unsigned LLONG);
+ else
+ value = va_arg (args, unsigned int);
+ total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
+ break;
+ case 'f':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, LDOUBLE);
+ else
+ fvalue = va_arg (args, double);
+ total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+ break;
+ case 'E':
+ flags |= DP_F_UP;
+ case 'e':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, LDOUBLE);
+ else
+ fvalue = va_arg (args, double);
+ total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+ break;
+ case 'G':
+ flags |= DP_F_UP;
+ case 'g':
+ flags |= DP_F_FP_G;
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, LDOUBLE);
+ else
+ fvalue = va_arg (args, double);
+ if (max == 0)
+ /* C99 says: if precision [for %g] is zero, it is taken as one */
+ max = 1;
+ total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+ break;
+ case 'c':
+ total += dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
+ break;
+ case 's':
+ strvalue = va_arg (args, char *);
+ total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
+ break;
+ case 'p':
+ strvalue = va_arg (args, void *);
+ total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min,
+ max, flags);
+ break;
+ case 'n':
+ if (cflags == DP_C_SHORT)
+ {
+ short int *num;
+ num = va_arg (args, short int *);
+ *num = currlen;
+ }
+ else if (cflags == DP_C_LONG)
+ {
+ long int *num;
+ num = va_arg (args, long int *);
+ *num = currlen;
+ }
+ else if (cflags == DP_C_LLONG)
+ {
+ LLONG *num;
+ num = va_arg (args, LLONG *);
+ *num = currlen;
+ }
+ else
+ {
+ int *num;
+ num = va_arg (args, int *);
+ *num = currlen;
+ }
+ break;
+ case '%':
+ total += dopr_outch (buffer, &currlen, maxlen, ch);
+ break;
+ case 'w':
+ /* not supported yet, treat as next char */
+ ch = *format++;
+ break;
+ default:
+ /* Unknown, skip */
+ break;
+ }
+ ch = *format++;
+ state = DP_S_DEFAULT;
+ flags = cflags = min = 0;
+ max = -1;
+ break;
+ case DP_S_DONE:
+ break;
+ default:
+ /* hmm? */
+ break; /* some picky compilers need this */
+ }
+ }
+ if (buffer != NULL)
+ {
+ if (currlen < maxlen - 1)
+ buffer[currlen] = '\0';
+ else
+ buffer[maxlen - 1] = '\0';
+ }
+ return total;
+}
+
+static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
+ const char *value, int flags, int min, int max)
+{
+ int padlen, strln; /* amount to pad */
+ int cnt = 0;
+ int total = 0;
+
+ if (value == 0)
+ {
+ value = "(null)";
+ }
+
+ if (max < 0)
+ strln = strlen (value);
+ else
+ /* When precision is specified, don't read VALUE past precision. */
+ /*strln = strnlen (value, max);*/
+ for (strln = 0; strln < max && value[strln]; ++strln);
+ padlen = min - strln;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justify */
+
+ while (padlen > 0)
+ {
+ total += dopr_outch (buffer, currlen, maxlen, ' ');
+ --padlen;
+ }
+ while (*value && ((max < 0) || (cnt < max)))
+ {
+ total += dopr_outch (buffer, currlen, maxlen, *value++);
+ ++cnt;
+ }
+ while (padlen < 0)
+ {
+ total += dopr_outch (buffer, currlen, maxlen, ' ');
+ ++padlen;
+ }
+ return total;
+}
+
+/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
+
+static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
+ LLONG value, int base, int min, int max, int flags)
+{
+ int signvalue = 0;
+ unsigned LLONG uvalue;
+ char convert[24];
+ unsigned int place = 0;
+ int spadlen = 0; /* amount to space pad */
+ int zpadlen = 0; /* amount to zero pad */
+ const char *digits;
+ int total = 0;
+
+ if (max < 0)
+ max = 0;
+
+ uvalue = value;
+
+ if(!(flags & DP_F_UNSIGNED))
+ {
+ if( value < 0 ) {
+ signvalue = '-';
+ uvalue = -value;
+ }
+ else
+ if (flags & DP_F_PLUS) /* Do a sign (+/i) */
+ signvalue = '+';
+ else
+ if (flags & DP_F_SPACE)
+ signvalue = ' ';
+ }
+
+ if (flags & DP_F_UP)
+ /* Should characters be upper case? */
+ digits = "0123456789ABCDEF";
+ else
+ digits = "0123456789abcdef";
+
+ do {
+ convert[place++] = digits[uvalue % (unsigned)base];
+ uvalue = (uvalue / (unsigned)base );
+ } while(uvalue && (place < sizeof (convert)));
+ if (place == sizeof (convert)) place--;
+ convert[place] = 0;
+
+ zpadlen = max - place;
+ spadlen = min - MAX ((unsigned int)max, place) - (signvalue ? 1 : 0);
+ if (zpadlen < 0) zpadlen = 0;
+ if (spadlen < 0) spadlen = 0;
+ if (flags & DP_F_ZERO)
+ {
+ zpadlen = MAX(zpadlen, spadlen);
+ spadlen = 0;
+ }
+ if (flags & DP_F_MINUS)
+ spadlen = -spadlen; /* Left Justifty */
+
+#ifdef DEBUG_SNPRINTF
+ dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
+ zpadlen, spadlen, min, max, place));
+#endif
+
+ /* Spaces */
+ while (spadlen > 0)
+ {
+ total += dopr_outch (buffer, currlen, maxlen, ' ');
+ --spadlen;
+ }
+
+ /* Sign */
+ if (signvalue)
+ total += dopr_outch (buffer, currlen, maxlen, signvalue);
+
+ /* Zeros */
+ if (zpadlen > 0)
+ {
+ while (zpadlen > 0)
+ {
+ total += dopr_outch (buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+ }
+
+ /* Digits */
+ while (place > 0)
+ total += dopr_outch (buffer, currlen, maxlen, convert[--place]);
+
+ /* Left Justified spaces */
+ while (spadlen < 0) {
+ total += dopr_outch (buffer, currlen, maxlen, ' ');
+ ++spadlen;
+ }
+
+ return total;
+}
+
+static LDOUBLE abs_val (LDOUBLE value)
+{
+ LDOUBLE result = value;
+
+ if (value < 0)
+ result = -value;
+
+ return result;
+}
+
+static LDOUBLE pow10_int (int exp)
+{
+ LDOUBLE result = 1;
+
+ while (exp)
+ {
+ result *= 10;
+ exp--;
+ }
+
+ return result;
+}
+
+static LLONG round_int (LDOUBLE value)
+{
+ LLONG intpart;
+
+ intpart = value;
+ value = value - intpart;
+ if (value >= 0.5)
+ intpart++;
+
+ return intpart;
+}
+
+static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+ LDOUBLE fvalue, int min, int max, int flags)
+{
+ int signvalue = 0;
+ LDOUBLE ufvalue;
+ char iconvert[24];
+ char fconvert[24];
+ size_t iplace = 0;
+ size_t fplace = 0;
+ int padlen = 0; /* amount to pad */
+ int zpadlen = 0;
+ int total = 0;
+ LLONG intpart;
+ LLONG fracpart;
+ LLONG mask10;
+ int leadingfrac0s = 0; /* zeroes at the start of fractional part */
+ int omitzeros = 0;
+ size_t omitcount = 0;
+
+ /*
+ * AIX manpage says the default is 0, but Solaris says the default
+ * is 6, and sprintf on AIX defaults to 6
+ */
+ if (max < 0)
+ max = 6;
+
+ ufvalue = abs_val (fvalue);
+
+ if (fvalue < 0)
+ signvalue = '-';
+ else
+ if (flags & DP_F_PLUS) /* Do a sign (+/i) */
+ signvalue = '+';
+ else
+ if (flags & DP_F_SPACE)
+ signvalue = ' ';
+
+#if 0
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+#endif
+
+ intpart = ufvalue;
+
+ /* With %g precision is the number of significant digits, which
+ includes the digits in intpart. */
+ if (flags & DP_F_FP_G)
+ {
+ if (intpart != 0)
+ {
+ /* For each digit of INTPART, print one less fractional digit. */
+ LLONG temp = intpart;
+ for (temp = intpart; temp != 0; temp /= 10)
+ --max;
+ if (max < 0)
+ max = 0;
+ }
+ else
+ {
+ /* For each leading 0 in fractional part, print one more
+ fractional digit. */
+ LDOUBLE temp;
+ if (ufvalue != 0)
+ for (temp = ufvalue; temp < 0.1; temp *= 10)
+ ++max;
+ }
+ }
+
+ /* C99: trailing zeros are removed from the fractional portion of the
+ result unless the # flag is specified */
+ if ((flags & DP_F_FP_G) && !(flags & DP_F_NUM))
+ omitzeros = 1;
+
+#if SIZEOF_LONG_LONG > 0
+# define MAX_DIGITS 18 /* grok more digits with long long */
+#else
+# define MAX_DIGITS 9 /* just long */
+#endif
+
+ /*
+ * Sorry, we only support several digits past the decimal because of
+ * our conversion method
+ */
+ if (max > MAX_DIGITS)
+ max = MAX_DIGITS;
+
+ /* Factor of 10 with the needed number of digits, e.g. 1000 for max==3 */
+ mask10 = pow10_int (max);
+
+ /* We "cheat" by converting the fractional part to integer by
+ * multiplying by a factor of 10
+ */
+ fracpart = round_int (mask10 * (ufvalue - intpart));
+
+ if (fracpart >= mask10)
+ {
+ intpart++;
+ fracpart -= mask10;
+ }
+ else if (fracpart != 0)
+ /* If fracpart has less digits than the 10* mask, we need to
+ manually insert leading 0s. For example 2.01's fractional part
+ requires one leading zero to distinguish it from 2.1. */
+ while (fracpart < mask10 / 10)
+ {
+ ++leadingfrac0s;
+ mask10 /= 10;
+ }
+
+#ifdef DEBUG_SNPRINTF
+ dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
+#endif
+
+ /* Convert integer part */
+ do {
+ iconvert[iplace++] = '0' + intpart % 10;
+ intpart = (intpart / 10);
+ } while(intpart && (iplace < sizeof(iconvert)));
+ if (iplace == sizeof(iconvert)) iplace--;
+ iconvert[iplace] = 0;
+
+ /* Convert fractional part */
+ do {
+ fconvert[fplace++] = '0' + fracpart % 10;
+ fracpart = (fracpart / 10);
+ } while(fracpart && (fplace < sizeof(fconvert)));
+ while (leadingfrac0s-- > 0 && fplace < sizeof(fconvert))
+ fconvert[fplace++] = '0';
+ if (fplace == sizeof(fconvert)) fplace--;
+ fconvert[fplace] = 0;
+ if (omitzeros)
+ while (omitcount < fplace && fconvert[omitcount] == '0')
+ ++omitcount;
+
+ /* -1 for decimal point, another -1 if we are printing a sign */
+ padlen = min - iplace - (max - omitcount) - 1 - ((signvalue) ? 1 : 0);
+ if (!omitzeros)
+ zpadlen = max - fplace;
+ if (zpadlen < 0)
+ zpadlen = 0;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justifty */
+
+ if ((flags & DP_F_ZERO) && (padlen > 0))
+ {
+ if (signvalue)
+ {
+ total += dopr_outch (buffer, currlen, maxlen, signvalue);
+ --padlen;
+ signvalue = 0;
+ }
+ while (padlen > 0)
+ {
+ total += dopr_outch (buffer, currlen, maxlen, '0');
+ --padlen;
+ }
+ }
+ while (padlen > 0)
+ {
+ total += dopr_outch (buffer, currlen, maxlen, ' ');
+ --padlen;
+ }
+ if (signvalue)
+ total += dopr_outch (buffer, currlen, maxlen, signvalue);
+
+ while (iplace > 0)
+ total += dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
+
+ /*
+ * Decimal point. This should probably use locale to find the correct
+ * char to print out.
+ */
+ if (max > 0 && (fplace > omitcount || zpadlen > 0))
+ {
+ total += dopr_outch (buffer, currlen, maxlen, '.');
+
+ while (fplace > omitcount)
+ total += dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
+ }
+
+ while (zpadlen > 0)
+ {
+ total += dopr_outch (buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+
+ while (padlen < 0)
+ {
+ total += dopr_outch (buffer, currlen, maxlen, ' ');
+ ++padlen;
+ }
+
+ return total;
+}
+
+static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
+{
+ if (*currlen + 1 < maxlen)
+ buffer[(*currlen)++] = c;
+ return 1;
+}
+
+int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
+{
+ if (str != NULL)
+ str[0] = 0;
+ return dopr(str, count, fmt, args);
+}
+
+/* VARARGS3 */
+#ifdef HAVE_STDARGS
+int snprintf (char *str,size_t count,const char *fmt,...)
+#else
+int snprintf (va_alist) va_dcl
+#endif
+{
+#ifndef HAVE_STDARGS
+ char *str;
+ size_t count;
+ char *fmt;
+#endif
+ VA_LOCAL_DECL;
+ int total;
+
+ VA_START (fmt);
+ VA_SHIFT (str, char *);
+ VA_SHIFT (count, size_t );
+ VA_SHIFT (fmt, char *);
+ total = vsnprintf(str, count, fmt, ap);
+ VA_END;
+ return total;
+}
+
+#ifdef TEST_SNPRINTF
+#ifndef LONG_STRING
+#define LONG_STRING 1024
+#endif
+int main (void)
+{
+ char buf1[LONG_STRING];
+ char buf2[LONG_STRING];
+ char *fp_fmt[] = {
+ "%-1.5f",
+ "%1.5f",
+ "%123.9f",
+ "%10.5f",
+ "% 10.5f",
+ "%+22.9f",
+ "%+4.9f",
+ "%01.3f",
+ "%4f",
+ "%3.1f",
+ "%3.2f",
+ "%.0f",
+ "%.1f",
+ NULL
+ };
+ double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
+ 0.9996, 1.996, 4.136, 0};
+ char *int_fmt[] = {
+ "%-1.5d",
+ "%1.5d",
+ "%123.9d",
+ "%5.5d",
+ "%10.5d",
+ "% 10.5d",
+ "%+22.33d",
+ "%01.3d",
+ "%4d",
+ NULL
+ };
+ long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
+ int x, y;
+ int fail = 0;
+ int num = 0;
+
+ printf ("Testing snprintf format codes against system sprintf...\n");
+
+ for (x = 0; fp_fmt[x] != NULL ; x++)
+ for (y = 0; fp_nums[y] != 0 ; y++)
+ {
+ snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
+ sprintf (buf2, fp_fmt[x], fp_nums[y]);
+ if (strcmp (buf1, buf2))
+ {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
+ fp_fmt[x], buf1, buf2);
+ fail++;
+ }
+ num++;
+ }
+
+ for (x = 0; int_fmt[x] != NULL ; x++)
+ for (y = 0; int_nums[y] != 0 ; y++)
+ {
+ snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
+ sprintf (buf2, int_fmt[x], int_nums[y]);
+ if (strcmp (buf1, buf2))
+ {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
+ int_fmt[x], buf1, buf2);
+ fail++;
+ }
+ num++;
+ }
+ printf ("%d tests failed out of %d.\n", fail, num);
+}
+#endif /* SNPRINTF_TEST */
diff --git a/system.h b/system.h
index 4a8023e..064fecb 100644
--- a/system.h
+++ b/system.h
@@ -1,4 +1,4 @@
-/* $Id: clibrary.h 7121 2005-01-06 00:40:37Z rra $
+/* $Id$
**
** Declarations of routines and variables in the C library. Including this
** file is the equivalent of including all of the following headers,
@@ -40,4 +40,45 @@
# endif
#endif
-#endif /* !CLIBRARY_H */
+/* BEGIN_DECLS is used at the beginning of declarations so that C++
+ compilers don't mangle their names. END_DECLS is used at the end. */
+#undef BEGIN_DECLS
+#undef END_DECLS
+#ifdef __cplusplus
+# define BEGIN_DECLS extern "C" {
+# define END_DECLS }
+#else
+# define BEGIN_DECLS /* empty */
+# define END_DECLS /* empty */
+#endif
+
+BEGIN_DECLS
+
+/* Provide prototypes for functions not declared in system headers. Use the
+ HAVE_DECL macros for those functions that may be prototyped but
+ implemented incorrectly or implemented without a prototype. */
+#if !HAVE_ASPRINTF
+extern int asprintf(char **, const char *, ...);
+extern int vasprintf(char **, const char *, va_list);
+#endif
+#if !HAVE_DECL_SNPRINTF
+extern int snprintf(char *, size_t, const char *, ...)
+ __attribute__((__format__(printf, 3, 4)));
+#endif
+#if !HAVE_DECL_VSNPRINTF
+extern int vsnprintf(char *, size_t, const char *, va_list);
+#endif
+
+END_DECLS
+
+/* C99 requires va_copy. Older versions of GCC provide __va_copy. Per the
+ Autoconf manual, memcpy is a generally portable fallback. */
+#ifndef va_copy
+# ifdef __va_copy
+# define va_copy(d, s) __va_copy((d), (s))
+# else
+# define va_copy(d, s) memcpy(&(d), &(s), sizeof(va_list))
+# endif
+#endif
+
+#endif /* !SYSTEM_H */
diff --git a/tests/TESTS b/tests/TESTS
index 1c8ab65..298f7a6 100644
--- a/tests/TESTS
+++ b/tests/TESTS
@@ -1,3 +1,8 @@
client/basic
+portable/asprintf
+portable/snprintf
server/backend
server/keytab
+util/concat
+util/messages
+util/xmalloc
diff --git a/tests/libtest.c b/tests/libtest.c
new file mode 100644
index 0000000..727fc81
--- /dev/null
+++ b/tests/libtest.c
@@ -0,0 +1,206 @@
+/* $Id$
+**
+** Some utility routines for writing tests.
+**
+** Herein are a variety of utility routines for writing tests. All
+** routines of the form ok*() take a test number and some number of
+** appropriate arguments, check to be sure the results match the expected
+** output using the arguments, and print out something appropriate for that
+** test number. Other utility routines help in constructing more complex
+** tests.
+**
+** Copyright 2006
+** Board of Trustees, Leland Stanford Jr. University
+** Copyright (c) 2004, 2005, 2006
+** by Internet Systems Consortium, Inc. ("ISC")
+** Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+** 2002, 2003 by The Internet Software Consortium and Rich Salz
+**
+** This code is derived from software contributed to the Internet Software
+** Consortium by Rich Salz.
+**
+** Permission to use, copy, modify, and distribute this software for any
+** purpose with or without fee is hereby granted, provided that the above
+** copyright notice and this permission notice appear in all copies.
+**
+** THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+** REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY
+** SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+** WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+** ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+** OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <config.h>
+#include <system.h>
+
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <tests/libtest.h>
+#include <util/util.h>
+
+/* A global buffer into which message_log_buffer stores error messages. */
+char *errors = NULL;
+
+
+/*
+** Initialize things. Turns on line buffering on stdout and then prints out
+** the number of tests in the test suite.
+*/
+void
+test_init(int count)
+{
+ if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0)
+ syswarn("cannot set stdout to line buffered");
+ printf("%d\n", count);
+}
+
+
+/*
+** Takes a boolean success value and assumes the test passes if that value
+** is true and fails if that value is false.
+*/
+void
+ok(int n, int success)
+{
+ printf("%sok %d\n", success ? "" : "not ", n);
+}
+
+
+/*
+** Takes an expected integer and a seen integer and assumes the test passes
+** if those two numbers match.
+*/
+void
+ok_int(int n, int wanted, int seen)
+{
+ if (wanted == seen)
+ printf("ok %d\n", n);
+ else
+ printf("not ok %d\n wanted: %d\n seen: %d\n", n, wanted, seen);
+}
+
+
+/*
+** Takes a string and what the string should be, and assumes the test
+** passes if those strings match (using strcmp).
+*/
+void
+ok_string(int n, const char *wanted, const char *seen)
+{
+ if (wanted == NULL)
+ wanted = "(null)";
+ if (seen == NULL)
+ seen = "(null)";
+ if (strcmp(wanted, seen) != 0)
+ printf("not ok %d\n wanted: %s\n seen: %s\n", n, wanted, seen);
+ else
+ printf("ok %d\n", n);
+}
+
+
+/*
+** Takes an expected integer and a seen integer and assumes the test passes
+** if those two numbers match.
+*/
+void
+ok_double(int n, double wanted, double seen)
+{
+ if (wanted == seen)
+ printf("ok %d\n", n);
+ else
+ printf("not ok %d\n wanted: %g\n seen: %g\n", n, wanted, seen);
+}
+
+
+/*
+** Skip a test.
+*/
+void
+skip(int n, const char *reason)
+{
+ printf("ok %d # skip", n);
+ if (reason != NULL)
+ printf(" - %s", reason);
+ putchar('\n');
+}
+
+
+/*
+** Report the same status on the next count tests.
+*/
+void
+ok_block(int n, int count, int status)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ ok(n++, status);
+}
+
+
+/*
+** Skip the next count tests.
+*/
+void
+skip_block(int n, int count, const char *reason)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ skip(n++, reason);
+}
+
+
+/*
+** An error handler that appends all errors to the errors global. Used by
+** error_capture.
+*/
+static void
+message_log_buffer(int len, const char *fmt, va_list args, int error UNUSED)
+{
+ char *message;
+
+ message = xmalloc(len + 1);
+ vsnprintf(message, len + 1, fmt, args);
+ if (errors == NULL) {
+ errors = concat(message, "\n", (char *) 0);
+ } else {
+ char *new_errors;
+
+ new_errors = concat(errors, message, "\n", (char *) 0);
+ free(errors);
+ errors = new_errors;
+ }
+ free(message);
+}
+
+
+/*
+** Turn on the capturing of errors. Errors will be stored in the global
+** errors variable where they can be checked by the test suite. Capturing is
+** turned off with errors_uncapture.
+*/
+void
+errors_capture(void)
+{
+ if (errors != NULL) {
+ free(errors);
+ errors = NULL;
+ }
+ message_handlers_warn(1, message_log_buffer);
+ message_handlers_notice(1, message_log_buffer);
+}
+
+
+/*
+** Turn off the capturing of errors again.
+*/
+void
+errors_uncapture(void)
+{
+ message_handlers_warn(1, message_log_stderr);
+ message_handlers_notice(1, message_log_stdout);
+}
diff --git a/tests/libtest.h b/tests/libtest.h
new file mode 100644
index 0000000..aedbaa6
--- /dev/null
+++ b/tests/libtest.h
@@ -0,0 +1,56 @@
+/* $Id$
+**
+** Some utility routines for writing tests.
+*/
+
+#ifndef LIBTEST_H
+#define LIBTEST_H 1
+
+#include <config.h>
+
+/* BEGIN_DECLS is used at the beginning of declarations so that C++
+ compilers don't mangle their names. END_DECLS is used at the end. */
+#undef BEGIN_DECLS
+#undef END_DECLS
+#ifdef __cplusplus
+# define BEGIN_DECLS extern "C" {
+# define END_DECLS }
+#else
+# define BEGIN_DECLS /* empty */
+# define END_DECLS /* empty */
+#endif
+
+/* Used for iterating through arrays. ARRAY_SIZE returns the number of
+ elements in the array (useful for a < upper bound in a for loop) and
+ ARRAY_END returns a pointer to the element past the end (ISO C99 makes it
+ legal to refer to such a pointer as long as it's never dereferenced). */
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
+#define ARRAY_END(array) (&(array)[ARRAY_SIZE(array)])
+
+/* A global buffer into which errors_capture stores errors. */
+extern char *errors;
+
+BEGIN_DECLS
+
+void ok(int n, int success);
+void ok_int(int n, int wanted, int seen);
+void ok_double(int n, double wanted, double seen);
+void ok_string(int n, const char *wanted, const char *seen);
+void skip(int n, const char *reason);
+
+/* Report the same status on, or skip, the next count tests. */
+void ok_block(int n, int count, int success);
+void skip_block(int n, int count, const char *reason);
+
+/* Print out the number of tests and set standard output to line buffered. */
+void test_init(int count);
+
+/* Turn on capturing of errors with errors_capture. Errors reported by warn
+ will be stored in the global errors variable. Turn this off again with
+ errors_uncapture. Caller is responsible for freeing errors when done. */
+void errors_capture(void);
+void errors_uncapture(void);
+
+END_DECLS
+
+#endif /* LIBTEST_H */
diff --git a/tests/portable/asprintf-t.c b/tests/portable/asprintf-t.c
new file mode 100644
index 0000000..a362755
--- /dev/null
+++ b/tests/portable/asprintf-t.c
@@ -0,0 +1,51 @@
+/* $Id$ */
+/* asprintf and vasprintf test suite. */
+
+/* Written by Russ Allbery <rra@stanford.edu>
+ Copyright 2006 Board of Trustees, Leland Stanford Jr. University
+ See README for licensing terms. */
+
+#include <config.h>
+#include <system.h>
+
+#include <tests/libtest.h>
+
+static int
+vatest(char **result, const char *format, ...)
+{
+ va_list args;
+ int status;
+
+ va_start(args, format);
+ status = vasprintf(result, format, args);
+ va_end(args);
+ return status;
+}
+
+int
+main(void)
+{
+ char *result = NULL;
+
+ test_init(12);
+
+ ok_int(1, 7, asprintf(&result, "%s", "testing"));
+ ok_string(2, "testing", result);
+ free(result);
+ ok(3, 1);
+ ok_int(4, 0, asprintf(&result, "%s", ""));
+ ok_string(5, "", result);
+ free(result);
+ ok(6, 1);
+
+ ok_int(7, 6, vatest(&result, "%d %s", 2, "test"));
+ ok_string(8, "2 test", result);
+ free(result);
+ ok(9, 1);
+ ok_int(10, 0, vatest(&result, "%s", ""));
+ ok_string(11, "", result);
+ free(result);
+ ok(12, 1);
+
+ return 0;
+}
diff --git a/tests/portable/asprintf.c b/tests/portable/asprintf.c
new file mode 100644
index 0000000..221c993
--- /dev/null
+++ b/tests/portable/asprintf.c
@@ -0,0 +1,2 @@
+#define TESTING 1
+#include <portable/asprintf.c>
diff --git a/tests/portable/snprintf-t.c b/tests/portable/snprintf-t.c
new file mode 100644
index 0000000..9159fcf
--- /dev/null
+++ b/tests/portable/snprintf-t.c
@@ -0,0 +1,197 @@
+/* $Id$ */
+/* snprintf test suite. */
+
+/* Copyright (c) 2004, 2005, 2006
+ by Internet Systems Consortium, Inc. ("ISC")
+ Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ 2002, 2003 by The Internet Software Consortium and Rich Salz
+
+ This code is derived from software contributed to the Internet Software
+ Consortium by Rich Salz.
+
+ Permission to use, copy, modify, and distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY
+ SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <config.h>
+#include <system.h>
+
+#include <tests/libtest.h>
+
+int test_snprintf(char *str, size_t count, const char *fmt, ...);
+int test_vsnprintf(char *str, size_t count, const char *fmt, va_list args);
+
+static const char string[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+
+static const char *const fp_formats[] = {
+ "%-1.5f", "%1.5f", "%31.9f", "%10.5f", "% 10.5f", "%+22.9f",
+ "%+4.9f", "%01.3f", "%3.1f", "%3.2f", "%.0f", "%.1f",
+ "%f",
+
+ /* %e and %g formats aren't really implemented yet. */
+#if 0
+ "%-1.5e", "%1.5e", "%31.9e", "%10.5e", "% 10.5e", "%+22.9e",
+ "%+4.9e", "%01.3e", "%3.1e", "%3.2e", "%.0e", "%.1e",
+ "%e",
+ "%-1.5g", "%1.5g", "%31.9g", "%10.5g", "% 10.5g", "%+22.9g",
+ "%+4.9g", "%01.3g", "%3.1g", "%3.2g", "%.0g", "%.1g",
+ "%g",
+#endif
+ NULL
+};
+static const char *const int_formats[] = {
+ "%-1.5d", "%1.5d", "%31.9d", "%5.5d", "%10.5d", "% 10.5d",
+ "%+22.30d", "%01.3d", "%4d", "%d", "%ld", NULL
+};
+static const char *const uint_formats[] = {
+ "%-1.5lu", "%1.5lu", "%31.9lu", "%5.5lu", "%10.5lu", "% 10.5lu",
+ "%+6.30lu", "%01.3lu", "%4lu", "%lu", "%4lx", "%4lX",
+ "%01.3lx", "%1lo", NULL
+};
+static const char *const llong_formats[] = {
+ "%lld", "%-1.5lld", "%1.5lld", "%123.9lld", "%5.5lld",
+ "%10.5lld", "% 10.5lld", "%+22.33lld", "%01.3lld", "%4lld",
+ NULL
+};
+static const char *const ullong_formats[] = {
+ "%llu", "%-1.5llu", "%1.5llu", "%123.9llu", "%5.5llu",
+ "%10.5llu", "% 10.5llu", "%+22.33llu", "%01.3llu", "%4llu",
+ "%llx", "%llo", NULL
+};
+
+static const double fp_nums[] = {
+ -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, 0.9996, 1.996,
+ 4.136, 0.1, 0.01, 0.001, 10.1, 0
+};
+static long int_nums[] = {
+ -1, 134, 91340, 341, 0203, 0
+};
+static unsigned long uint_nums[] = {
+ (unsigned long) -1, 134, 91340, 341, 0203, 0
+};
+static long long llong_nums[] = {
+ ~(long long) 0, /* All-1 bit pattern. */
+ (~(unsigned long long) 0) >> 1, /* Largest signed long long. */
+ -150, 134, 91340, 341,
+ 0
+};
+static unsigned long long ullong_nums[] = {
+ ~(unsigned long long) 0, /* All-1 bit pattern. */
+ (~(unsigned long long) 0) >> 1, /* Largest signed long long. */
+ 134, 91340, 341,
+ 0
+};
+
+static void
+test_format(int n, int truncate, const char *expected, int count,
+ const char *format, ...)
+{
+ char buf[128];
+ int result;
+ va_list args;
+
+ va_start(args, format);
+ result = test_vsnprintf(buf, truncate ? 32 : sizeof(buf), format, args);
+ va_end(args);
+ if (!strcmp(buf, expected) && result == count) {
+ printf("ok %d\n", n);
+ } else {
+ printf("not ok %d\n", n);
+ printf(" format: %s\n", format);
+ if (strcmp(buf, expected))
+ printf(" saw: %s\n want: %s\n", buf, expected);
+ if (result != count)
+ printf(" %d != %d\n", result, count);
+ }
+}
+
+int
+main(void)
+{
+ int n, i, count;
+ unsigned int j;
+ long lcount;
+ char lgbuf[128];
+
+ test_init((26 + (ARRAY_SIZE(fp_formats) - 1) * ARRAY_SIZE(fp_nums)
+ + (ARRAY_SIZE(int_formats) - 1) * ARRAY_SIZE(int_nums)
+ + (ARRAY_SIZE(uint_formats) - 1) * ARRAY_SIZE(uint_nums)
+ + (ARRAY_SIZE(llong_formats) - 1) * ARRAY_SIZE(llong_nums)
+ + (ARRAY_SIZE(ullong_formats) - 1) * ARRAY_SIZE(ullong_nums)));
+
+ ok(1, test_snprintf(NULL, 0, "%s", "abcd") == 4);
+ ok(2, test_snprintf(NULL, 0, "%d", 20) == 2);
+ ok(3, test_snprintf(NULL, 0, "Test %.2s", "abcd") == 7);
+ ok(4, test_snprintf(NULL, 0, "%c", 'a') == 1);
+ ok(5, test_snprintf(NULL, 0, "") == 0);
+
+ test_format(6, 1, "abcd", 4, "%s", "abcd");
+ test_format(7, 1, "20", 2, "%d", 20);
+ test_format(8, 1, "Test ab", 7, "Test %.2s", "abcd");
+ test_format(9, 1, "a", 1, "%c", 'a');
+ test_format(10, 1, "", 0, "");
+ test_format(11, 1, "abcdefghijklmnopqrstuvwxyz01234", 36, "%s",
+ string);
+ test_format(12, 1, "abcdefghij", 10, "%.10s", string);
+ test_format(13, 1, " abcdefghij", 12, "%12.10s", string);
+ test_format(14, 1, " abcdefghijklmnopqrstuvwxyz0", 40, "%40s",
+ string);
+ test_format(15, 1, "abcdefghij ", 14, "%-14.10s", string);
+ test_format(16, 1, " abcdefghijklmnopq", 50, "%50s",
+ string);
+ test_format(17, 1, "%abcd%", 6, "%%%0s%%", "abcd");
+ test_format(18, 1, "", 0, "%.0s", string);
+ test_format(19, 1, "abcdefghijklmnopqrstuvwxyz 444", 32, "%.26s %d",
+ string, 4444);
+ test_format(20, 1, "abcdefghijklmnopqrstuvwxyz -2.", 32,
+ "%.26s %.1f", string, -2.5);
+ test_format(21, 1, "abcdefghij4444", 14, "%.10s%n%d", string, &count,
+ 4444);
+ ok(22, count == 10);
+ test_format(23, 1, "abcdefghijklmnopqrstuvwxyz01234", 36, "%n%s%ln",
+ &count, string, &lcount);
+ ok(24, count == 0);
+ ok(25, lcount == 31);
+ test_format(26, 1, "(null)", 6, "%s", NULL);
+
+ n = 26;
+ for (i = 0; fp_formats[i] != NULL; i++)
+ for (j = 0; j < ARRAY_SIZE(fp_nums); j++) {
+ count = sprintf(lgbuf, fp_formats[i], fp_nums[j]);
+ test_format(++n, 0, lgbuf, count, fp_formats[i], fp_nums[j]);
+ }
+ for (i = 0; int_formats[i] != NULL; i++)
+ for (j = 0; j < ARRAY_SIZE(int_nums); j++) {
+ count = sprintf(lgbuf, int_formats[i], int_nums[j]);
+ test_format(++n, 0, lgbuf, count, int_formats[i],
+ int_nums[j]);
+ }
+ for (i = 0; uint_formats[i] != NULL; i++)
+ for (j = 0; j < ARRAY_SIZE(uint_nums); j++) {
+ count = sprintf(lgbuf, uint_formats[i], uint_nums[j]);
+ test_format(++n, 0, lgbuf, count, uint_formats[i],
+ uint_nums[j]);
+ }
+ for (i = 0; llong_formats[i] != NULL; i++)
+ for (j = 0; j < ARRAY_SIZE(llong_nums); j++) {
+ count = sprintf(lgbuf, llong_formats[i], llong_nums[j]);
+ test_format(++n, 0, lgbuf, count, llong_formats[i],
+ llong_nums[j]);
+ }
+ for (i = 0; ullong_formats[i] != NULL; i++)
+ for (j = 0; j < ARRAY_SIZE(ullong_nums); j++) {
+ count = sprintf(lgbuf, ullong_formats[i], ullong_nums[j]);
+ test_format(++n, 0, lgbuf, count, ullong_formats[i],
+ ullong_nums[j]);
+ }
+
+ return 0;
+}
diff --git a/tests/portable/snprintf.c b/tests/portable/snprintf.c
new file mode 100644
index 0000000..cff95b3
--- /dev/null
+++ b/tests/portable/snprintf.c
@@ -0,0 +1,2 @@
+#define TESTING 1
+#include <portable/snprintf.c>
diff --git a/tests/util/concat-t.c b/tests/util/concat-t.c
new file mode 100644
index 0000000..c18cd19
--- /dev/null
+++ b/tests/util/concat-t.c
@@ -0,0 +1,54 @@
+/* $Id$ */
+/* concat test suite. */
+
+/* Copyright (c) 2004, 2005, 2006
+ by Internet Systems Consortium, Inc. ("ISC")
+ Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ 2002, 2003 by The Internet Software Consortium and Rich Salz
+
+ This code is derived from software contributed to the Internet Software
+ Consortium by Rich Salz.
+
+ Permission to use, copy, modify, and distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY
+ SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <config.h>
+#include <system.h>
+
+#include <tests/libtest.h>
+#include <util/util.h>
+
+#define END (char *) 0
+
+/* Memory leaks everywhere! Whoo-hoo! */
+int
+main(void)
+{
+ test_init(13);
+
+ ok_string( 1, "a", concat("a", END));
+ ok_string( 2, "ab", concat("a", "b", END));
+ ok_string( 3, "ab", concat("ab", "", END));
+ ok_string( 4, "ab", concat("", "ab", END));
+ ok_string( 5, "", concat("", END));
+ ok_string( 6, "abcde", concat("ab", "c", "", "de", END));
+ ok_string( 7, "abcde", concat("abc", "de", END, "f", END));
+
+ ok_string( 8, "/foo", concatpath("/bar", "/foo"));
+ ok_string( 9, "/foo/bar", concatpath("/foo", "bar"));
+ ok_string(10, "./bar", concatpath("/foo", "./bar"));
+ ok_string(11, "/bar/baz/foo/bar", concatpath("/bar/baz", "foo/bar"));
+ ok_string(12, "./foo", concatpath(NULL, "foo"));
+ ok_string(13, "/foo/bar", concatpath(NULL, "/foo/bar"));
+
+ return 0;
+}
diff --git a/tests/util/messages-t.c b/tests/util/messages-t.c
new file mode 100644
index 0000000..ef58737
--- /dev/null
+++ b/tests/util/messages-t.c
@@ -0,0 +1,278 @@
+/* $Id$ */
+/* Test suite for error handling routines. */
+
+/* Copyright (c) 2004, 2005, 2006
+ by Internet Systems Consortium, Inc. ("ISC")
+ Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ 2002, 2003 by The Internet Software Consortium and Rich Salz
+
+ This code is derived from software contributed to the Internet Software
+ Consortium by Rich Salz.
+
+ Permission to use, copy, modify, and distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY
+ SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <config.h>
+#include <system.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <tests/libtest.h>
+#include <util/util.h>
+
+#define END (char *) 0
+
+/* Test function type. */
+typedef void (*test_function_t)(void);
+
+/* Fork and execute the provided function, connecting stdout and stderr to a
+ pipe. Captures the output into the provided buffer and returns the exit
+ status as a waitpid status value. */
+static int
+run_test(test_function_t function, char *buf, size_t buflen)
+{
+ int fds[2];
+ pid_t child;
+ ssize_t count, status;
+ int rval;
+
+ /* Flush stdout before we start to avoid odd forking issues. */
+ fflush(stdout);
+
+ /* Set up the pipe and call the function, collecting its output. */
+ if (pipe(fds) == -1)
+ sysdie("can't create pipe");
+ child = fork();
+ if (child == (pid_t) -1) {
+ sysdie("can't fork");
+ } else if (child == 0) {
+ /* In child. Set up our stdout and stderr. */
+ close(fds[0]);
+ if (dup2(fds[1], 1) == -1)
+ _exit(255);
+ if (dup2(fds[1], 2) == -1)
+ _exit(255);
+
+ /* Now, run the function and exit successfully if it returns. */
+ (*function)();
+ fflush(stdout);
+ _exit(0);
+ } else {
+ /* In the parent; close the extra file descriptor, read the output
+ if any, and then collect the exit status. */
+ close(fds[1]);
+ count = 0;
+ do {
+ status = read(fds[0], buf + count, buflen - count - 1);
+ if (status > 0)
+ count += status;
+ } while (status > 0);
+ buf[count < 0 ? 0 : count] = '\0';
+ if (waitpid(child, &rval, 0) == (pid_t) -1)
+ sysdie("waitpid failed");
+ }
+ return rval;
+}
+
+/* Test functions. */
+static void test1(void) { warn("warning"); }
+static void test2(void) { die("fatal"); }
+static void test3(void) { errno = EPERM; syswarn("permissions"); }
+static void test4(void) { errno = EACCES; sysdie("fatal access"); }
+static void test5(void) {
+ message_program_name = "test5";
+ warn("warning");
+}
+static void test6(void) {
+ message_program_name = "test6";
+ die("fatal");
+}
+static void test7(void) {
+ message_program_name = "test7";
+ errno = EPERM;
+ syswarn("perms %d", 7);
+}
+static void test8(void) {
+ message_program_name = "test8";
+ errno = EACCES;
+ sysdie("%st%s", "fa", "al");
+}
+
+static int return10(void) { return 10; }
+
+static void test9(void) {
+ message_fatal_cleanup = return10;
+ die("fatal");
+}
+static void test10(void) {
+ message_program_name = 0;
+ message_fatal_cleanup = return10;
+ errno = EPERM;
+ sysdie("fatal perm");
+}
+static void test11(void) {
+ message_program_name = "test11";
+ message_fatal_cleanup = return10;
+ errno = EPERM;
+ fputs("1st ", stdout);
+ sysdie("fatal");
+}
+
+static void log_msg(int len, const char *format, va_list args, int error) {
+ fprintf(stderr, "%d %d ", len, error);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+}
+
+static void test12(void) {
+ message_handlers_warn(1, log_msg);
+ warn("warning");
+}
+static void test13(void) {
+ message_handlers_die(1, log_msg);
+ die("fatal");
+}
+static void test14(void) {
+ message_handlers_warn(2, log_msg, log_msg);
+ errno = EPERM;
+ syswarn("warning");
+}
+static void test15(void) {
+ message_handlers_die(2, log_msg, log_msg);
+ message_fatal_cleanup = return10;
+ errno = EPERM;
+ sysdie("fatal");
+}
+static void test16(void) {
+ message_handlers_warn(2, message_log_stderr, log_msg);
+ message_program_name = "test16";
+ errno = EPERM;
+ syswarn("warning");
+}
+static void test17(void) { notice("notice"); }
+static void test18(void) {
+ message_program_name = "test18";
+ notice("notice");
+}
+static void test19(void) { debug("debug"); }
+static void test20(void) {
+ message_handlers_notice(1, log_msg);
+ notice("foo");
+}
+static void test21(void) {
+ message_handlers_debug(1, message_log_stdout);
+ message_program_name = "test23";
+ debug("baz");
+}
+static void test22(void) {
+ message_handlers_die(0);
+ die("hi mom!");
+}
+static void test23(void) {
+ message_handlers_warn(0);
+ warn("this is a test");
+}
+static void test24(void) {
+ notice("first");
+ message_handlers_notice(0);
+ notice("second");
+ message_handlers_notice(1, message_log_stdout);
+ notice("third");
+}
+
+/* Given the test number, intended exit status and message, and the function
+ to run, print ok or not ok. */
+static void
+test_error(int n, int status, const char *output, test_function_t function)
+{
+ int real_status;
+ char buf[256];
+ int succeeded = 1;
+
+ real_status = run_test(function, buf, sizeof(buf));
+ if (!WIFEXITED(real_status) || status != WEXITSTATUS(real_status)) {
+ printf(" unexpected exit status %d\n", real_status);
+ succeeded = 0;
+ }
+ if (strcmp(output, buf)) {
+ printf(" unexpected output: %s", buf);
+ printf(" expected output: %s", output);
+ succeeded = 0;
+ }
+ printf("%sok %d\n", succeeded ? "" : "not ", n);
+}
+
+/* Given the test number, intended status, intended message sans the
+ appended strerror output, errno, and the function to run, print ok or not
+ ok. */
+static void
+test_strerror(int n, int status, const char *output, int error,
+ test_function_t function)
+{
+ char *full_output;
+
+ full_output = concat(output, ": ", strerror(error), "\n", END);
+ test_error(n, status, full_output, function);
+ free(full_output);
+}
+
+/* Run the tests. */
+int
+main(void)
+{
+ char buff[32];
+
+ test_init(24);
+
+ test_error(1, 0, "warning\n", test1);
+ test_error(2, 1, "fatal\n", test2);
+ test_strerror(3, 0, "permissions", EPERM, test3);
+ test_strerror(4, 1, "fatal access", EACCES, test4);
+ test_error(5, 0, "test5: warning\n", test5);
+ test_error(6, 1, "test6: fatal\n", test6);
+ test_strerror(7, 0, "test7: perms 7", EPERM, test7);
+ test_strerror(8, 1, "test8: fatal", EACCES, test8);
+ test_error(9, 10, "fatal\n", test9);
+ test_strerror(10, 10, "fatal perm", EPERM, test10);
+ test_strerror(11, 10, "1st test11: fatal", EPERM, test11);
+ test_error(12, 0, "7 0 warning\n", test12);
+ test_error(13, 1, "5 0 fatal\n", test13);
+
+ sprintf(buff, "%d", EPERM);
+
+ test_error(14, 0,
+ concat("7 ", buff, " warning\n7 ", buff, " warning\n", END),
+ test14);
+ test_error(15, 10,
+ concat("5 ", buff, " fatal\n5 ", buff, " fatal\n", END),
+ test15);
+ test_error(16, 0,
+ concat("test16: warning: ", strerror(EPERM), "\n7 ", buff,
+ " warning\n", END),
+ test16);
+
+ test_error(17, 0, "notice\n", test17);
+ test_error(18, 0, "test18: notice\n", test18);
+ test_error(19, 0, "", test19);
+ test_error(20, 0, "3 0 foo\n", test20);
+ test_error(21, 0, "test23: baz\n", test21);
+
+ /* Make sure that it's possible to turn off a message type entirely. */
+ test_error(22, 1, "", test22);
+ test_error(23, 0, "", test23);
+ test_error(24, 0, "first\nthird\n", test24);
+
+ return 0;
+}
diff --git a/tests/util/xmalloc-t.in b/tests/util/xmalloc-t.in
new file mode 100644
index 0000000..e4e971d
--- /dev/null
+++ b/tests/util/xmalloc-t.in
@@ -0,0 +1,107 @@
+#! /bin/sh
+# $Id$
+#
+# Test suite for xmalloc and friends.
+
+# The count starts at 1 and is updated each time ok is printed. printcount
+# takes "ok" or "not ok".
+count=1
+printcount () {
+ echo "$1 $count $2"
+ count=`expr $count + 1`
+}
+
+# Run a program expected to succeed, and print ok if it does.
+runsuccess () {
+ output=`$xmalloc "$1" "$2" "$3" 2>&1 >/dev/null`
+ status=$?
+ if test $status = 0 && test -z "$output" ; then
+ printcount "ok"
+ else
+ if test $status = 2 ; then
+ printcount "ok" "# skip no data limit support"
+ else
+ printcount "not ok"
+ echo " $output"
+ fi
+ fi
+}
+
+# Run a program expected to fail and make sure it fails with an exit status
+# of 2 and the right failure message. Strip the colon and everything after
+# it off the error message since it's system-specific.
+runfailure () {
+ output=`$xmalloc "$1" "$2" "$3" 2>&1 >/dev/null`
+ status=$?
+ output=`echo "$output" | sed 's/:.*//' \
+ | sed 's% [^ ]*/xmalloc.c% xmalloc.c%'`
+ if test $status = 1 && test x"$output" = x"$4" ; then
+ printcount "ok"
+ else
+ if test $status = 2 ; then
+ printcount "ok" "# skip no data limit support"
+ else
+ printcount "not ok"
+ echo " saw: $output"
+ echo " not: $4"
+ fi
+ fi
+}
+
+# Find where the helper program is.
+xmalloc="@abs_top_builddir@/tests/util/xmalloc"
+
+# Total tests.
+echo 36
+
+# First run the tests expected to succeed.
+runsuccess "m" "21" "0"
+runsuccess "m" "128000" "0"
+runsuccess "m" "0" "0"
+runsuccess "r" "21" "0"
+runsuccess "r" "128000" "0"
+runsuccess "s" "21" "0"
+runsuccess "s" "128000" "0"
+runsuccess "n" "21" "0"
+runsuccess "n" "128000" "0"
+runsuccess "c" "24" "0"
+runsuccess "c" "128000" "0"
+runsuccess "a" "24" "0"
+runsuccess "a" "128000" "0"
+runsuccess "v" "24" "0"
+runsuccess "v" "128000" "0"
+
+# Now limit our memory to 120KB and then try the large ones again, all of
+# which should fail.
+runfailure "m" "128000" "120000" \
+ "failed to malloc 128000 bytes at xmalloc.c line 54"
+runfailure "r" "128000" "120000" \
+ "failed to realloc 128000 bytes at xmalloc.c line 80"
+runfailure "s" "64000" "120000" \
+ "failed to strdup 64000 bytes at xmalloc.c line 109"
+runfailure "n" "64000" "120000" \
+ "failed to strndup 64000 bytes at xmalloc.c line 133"
+runfailure "c" "128000" "120000" \
+ "failed to calloc 128000 bytes at xmalloc.c line 155"
+runfailure "a" "64000" "120000" \
+ "failed to asprintf 64000 bytes at xmalloc.c line 177"
+runfailure "v" "64000" "120000" \
+ "failed to vasprintf 64000 bytes at xmalloc.c line 196"
+
+# Check our custom error handler.
+runfailure "M" "128000" "120000" "malloc 128000 xmalloc.c 54"
+runfailure "R" "128000" "120000" "realloc 128000 xmalloc.c 80"
+runfailure "S" "64000" "120000" "strdup 64000 xmalloc.c 109"
+runfailure "N" "64000" "120000" "strndup 64000 xmalloc.c 133"
+runfailure "C" "128000" "120000" "calloc 128000 xmalloc.c 155"
+runfailure "A" "64000" "120000" "asprintf 64000 xmalloc.c 177"
+runfailure "V" "64000" "120000" "vasprintf 64000 xmalloc.c 196"
+
+# Check the smaller ones again just for grins.
+runsuccess "m" "21" "96000"
+runsuccess "r" "32" "96000"
+runsuccess "s" "64" "96000"
+runsuccess "n" "20" "96000"
+runsuccess "c" "24" "96000"
+runsuccess "a" "30" "96000"
+runsuccess "v" "35" "96000"
diff --git a/tests/util/xmalloc.c b/tests/util/xmalloc.c
new file mode 100644
index 0000000..4327681
--- /dev/null
+++ b/tests/util/xmalloc.c
@@ -0,0 +1,294 @@
+/* $Id$ */
+/* Test suite for xmalloc and family. */
+
+/* Copyright (c) 2004, 2005, 2006
+ by Internet Systems Consortium, Inc. ("ISC")
+ Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ 2002, 2003 by The Internet Software Consortium and Rich Salz
+
+ This code is derived from software contributed to the Internet Software
+ Consortium by Rich Salz.
+
+ Permission to use, copy, modify, and distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY
+ SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <config.h>
+#include <system.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+/* Linux requires sys/time.h be included before sys/resource.h. */
+#include <sys/resource.h>
+
+#include <util/util.h>
+
+/* A customized error handler for checking xmalloc's support of them.
+ Prints out the error message and exits with status 1. */
+static void
+test_handler(const char *function, size_t size, const char *file, int line)
+{
+ die("%s %lu %s %d", function, (unsigned long) size, file, line);
+}
+
+/* Allocate the amount of memory given and write to all of it to make sure
+ we can, returning true if that succeeded and false on any sort of
+ detectable error. */
+static int
+test_malloc(size_t size)
+{
+ char *buffer;
+ size_t i;
+
+ buffer = xmalloc(size);
+ if (buffer == NULL)
+ return 0;
+ if (size > 0)
+ memset(buffer, 1, size);
+ for (i = 0; i < size; i++)
+ if (buffer[i] != 1)
+ return 0;
+ free(buffer);
+ return 1;
+}
+
+/* Allocate half the memory given, write to it, then reallocate to the
+ desired size, writing to the rest and then checking it all. Returns true
+ on success, false on any failure. */
+static int
+test_realloc(size_t size)
+{
+ char *buffer;
+ size_t i;
+
+ buffer = xmalloc(size / 2);
+ if (buffer == NULL)
+ return 0;
+ if (size / 2 > 0)
+ memset(buffer, 1, size / 2);
+ buffer = xrealloc(buffer, size);
+ if (buffer == NULL)
+ return 0;
+ if (size > 0)
+ memset(buffer + size / 2, 2, size - size / 2);
+ for (i = 0; i < size / 2; i++)
+ if (buffer[i] != 1)
+ return 0;
+ for (i = size / 2; i < size; i++)
+ if (buffer[i] != 2)
+ return 0;
+ free(buffer);
+ return 1;
+}
+
+/* Generate a string of the size indicated, call xstrdup on it, and then
+ ensure the result matches. Returns true on success, false on any
+ failure. */
+static int
+test_strdup(size_t size)
+{
+ char *string, *copy;
+ int match;
+
+ string = xmalloc(size);
+ if (string == NULL)
+ return 0;
+ memset(string, 1, size - 1);
+ string[size - 1] = '\0';
+ copy = xstrdup(string);
+ if (copy == NULL)
+ return 0;
+ match = strcmp(string, copy);
+ free(string);
+ free(copy);
+ return (match == 0);
+}
+
+/* Generate a string of the size indicated plus some, call xstrndup on it, and
+ then ensure the result matches. Returns true on success, false on any
+ failure. */
+static int
+test_strndup(size_t size)
+{
+ char *string, *copy;
+ int match, toomuch;
+
+ string = xmalloc(size + 1);
+ if (string == NULL)
+ return 0;
+ memset(string, 1, size - 1);
+ string[size - 1] = 2;
+ string[size] = '\0';
+ copy = xstrndup(string, size - 1);
+ if (copy == NULL)
+ return 0;
+ match = strncmp(string, copy, size - 1);
+ toomuch = strcmp(string, copy);
+ free(string);
+ free(copy);
+ return (match == 0 && toomuch != 0);
+}
+
+/* Allocate the amount of memory given and check that it's all zeroed,
+ returning true if that succeeded and false on any sort of detectable
+ error. */
+static int
+test_calloc(size_t size)
+{
+ char *buffer;
+ size_t i, nelems;
+
+ nelems = size / 4;
+ if (nelems * 4 != size)
+ return 0;
+ buffer = xcalloc(nelems, 4);
+ if (buffer == NULL)
+ return 0;
+ for (i = 0; i < size; i++)
+ if (buffer[i] != 0)
+ return 0;
+ free(buffer);
+ return 1;
+}
+
+/* Test asprintf with a large string (essentially using it as strdup).
+ Returns true if successful, false otherwise. */
+static int
+test_asprintf(size_t size)
+{
+ char *copy, *string;
+ int status;
+ size_t i;
+
+ string = xmalloc(size);
+ memset(string, 42, size - 1);
+ string[size - 1] = '\0';
+ status = xasprintf(&copy, "%s", string);
+ free(string);
+ for (i = 0; i < size - 1; i++)
+ if (copy[i] != 42)
+ return 0;
+ if (copy[size - 1] != '\0')
+ return 0;
+ free(copy);
+ return 1;
+}
+
+/* Wrapper around vasprintf to do the va_list stuff. */
+static int
+xvasprintf_wrapper(char **strp, const char *format, ...)
+{
+ va_list args;
+ int status;
+
+ va_start(args, format);
+ status = xvasprintf(strp, format, args);
+ va_end(args);
+ return status;
+}
+
+/* Test vasprintf with a large string (essentially using it as strdup).
+ Returns true if successful, false otherwise. */
+static int
+test_vasprintf(size_t size)
+{
+ char *copy, *string;
+ int status;
+ size_t i;
+
+ string = xmalloc(size);
+ memset(string, 42, size - 1);
+ string[size - 1] = '\0';
+ status = xvasprintf_wrapper(&copy, "%s", string);
+ free(string);
+ for (i = 0; i < size - 1; i++)
+ if (copy[i] != 42)
+ return 0;
+ if (copy[size - 1] != '\0')
+ return 0;
+ free(copy);
+ return 1;
+}
+
+/* Take the amount of memory to allocate in bytes as a command-line argument
+ and call test_malloc with that amount of memory. */
+int
+main(int argc, char *argv[])
+{
+ size_t size, max;
+ size_t limit = 0;
+ int willfail = 0;
+ unsigned char code;
+ struct rlimit rl;
+
+ if (argc < 3)
+ die("Usage error. Type, size, and limit must be given.");
+ errno = 0;
+ size = strtol(argv[2], 0, 10);
+ if (size == 0 && errno != 0)
+ sysdie("Invalid size");
+ errno = 0;
+ limit = strtol(argv[3], 0, 10);
+ if (limit == 0 && errno != 0)
+ sysdie("Invalid limit");
+
+ /* If a memory limit was given and we can set memory limits, set it.
+ Otherwise, exit 2, signalling to the driver that the test should be
+ skipped. We do this here rather than in the driver due to some
+ pathological problems with Linux (setting ulimit in the shell caused
+ the shell to die). */
+ if (limit > 0) {
+#if HAVE_SETRLIMIT && defined(RLIMIT_DATA)
+ rl.rlim_cur = limit;
+ rl.rlim_max = limit;
+ if (setrlimit(RLIMIT_DATA, &rl) < 0) {
+ syswarn("Can't set data limit to %lu", (unsigned long) limit);
+ exit(2);
+ }
+#else
+ warn("Data limits aren't supported.");
+ exit(2);
+#endif
+ }
+
+ /* If the code is capitalized, install our customized error handler. */
+ code = argv[1][0];
+ if (isupper(code)) {
+ xmalloc_error_handler = test_handler;
+ code = tolower(code);
+ }
+
+ /* Decide if the allocation should fail. If it should, set willfail to
+ 2, so that if it unexpectedly succeeds, we exit with a status
+ indicating that the test should be skipped. */
+ max = size;
+ if (code == 's' || code == 'n' || code == 'a' || code == 'v')
+ max *= 2;
+ if (limit > 0 && max > limit)
+ willfail = 2;
+
+ switch (code) {
+ case 'c': exit(test_calloc(size) ? willfail : 1);
+ case 'm': exit(test_malloc(size) ? willfail : 1);
+ case 'r': exit(test_realloc(size) ? willfail : 1);
+ case 's': exit(test_strdup(size) ? willfail : 1);
+ case 'n': exit(test_strndup(size) ? willfail : 1);
+ case 'a': exit(test_asprintf(size) ? willfail : 1);
+ case 'v': exit(test_vasprintf(size) ? willfail : 1);
+ default:
+ die("Unknown mode %c", argv[1][0]);
+ break;
+ }
+ exit(1);
+}
diff --git a/util/concat.c b/util/concat.c
new file mode 100644
index 0000000..65ca04c
--- /dev/null
+++ b/util/concat.c
@@ -0,0 +1,76 @@
+/* $Id$
+**
+** Concatenate strings with dynamic memory allocation.
+**
+** Written by Russ Allbery <rra@stanford.edu>
+** This work is hereby placed in the public domain by its author.
+**
+** Usage:
+**
+** string = concat(string1, string2, ..., (char *) 0);
+** path = concatpath(base, name);
+**
+** Dynamically allocates (using xmalloc) sufficient memory to hold all of
+** the strings given and then concatenates them together into that
+** allocated memory, returning a pointer to it. Caller is responsible for
+** freeing. Assumes xmalloc is available. The last argument must be a
+** null pointer (to a char *, if you actually find a platform where it
+** matters).
+**
+** concatpath is similar, except that it only takes two arguments. If the
+** second argument begins with / or ./, a copy of it is returned;
+** otherwise, the first argument, a slash, and the second argument are
+** concatenated together and returned. This is useful for building file
+** names where names that aren't fully qualified are qualified with some
+** particular directory.
+*/
+
+#include <config.h>
+#include <system.h>
+
+#include <util/util.h>
+
+/* Abbreviation for cleaner code. */
+#define VA_NEXT(var, type) ((var) = (type) va_arg(args, type))
+
+/* ANSI C requires at least one named parameter. */
+char *
+concat(const char *first, ...)
+{
+ va_list args;
+ char *result, *p;
+ const char *string;
+ size_t length = 0;
+
+ /* Find the total memory required. */
+ va_start(args, first);
+ for (string = first; string != NULL; VA_NEXT(string, const char *))
+ length += strlen(string);
+ va_end(args);
+ length++;
+
+ /* Create the string. Doing the copy ourselves avoids useless string
+ traversals of result, if using strcat, or string, if using strlen to
+ increment a pointer into result, at the cost of losing the native
+ optimization of strcat if any. */
+ result = xmalloc(length);
+ p = result;
+ va_start(args, first);
+ for (string = first; string != NULL; VA_NEXT(string, const char *))
+ while (*string != '\0')
+ *p++ = *string++;
+ va_end(args);
+ *p = '\0';
+
+ return result;
+}
+
+
+char *
+concatpath(const char *base, const char *name)
+{
+ if (name[0] == '/' || (name[0] == '.' && name[1] == '/'))
+ return xstrdup(name);
+ else
+ return concat(base != NULL ? base : ".", "/", name, (char *) 0);
+}
diff --git a/util/messages.c b/util/messages.c
new file mode 100644
index 0000000..4a975c3
--- /dev/null
+++ b/util/messages.c
@@ -0,0 +1,361 @@
+/* $Id$
+**
+** Message and error reporting (possibly fatal).
+**
+** Usage:
+**
+** extern int cleanup(void);
+** extern void log(int, const char *, va_list, int);
+**
+** message_fatal_cleanup = cleanup;
+** message_program_name = argv[0];
+**
+** warn("Something horrible happened at %lu", time);
+** syswarn("Couldn't unlink temporary file %s", tmpfile);
+**
+** die("Something fatal happened at %lu", time);
+** sysdie("open of %s failed", filename);
+**
+** debug("Some debugging message about %s", string);
+** notice("Informational notices");
+**
+** message_handlers_warn(1, log);
+** warn("This now goes through our log function");
+**
+** These functions implement message reporting through user-configurable
+** handler functions. debug() only does something if DEBUG is defined, and
+** notice() and warn() just output messages as configured. die() similarly
+** outputs a message but then exits, normally with a status of 1.
+**
+** The sys* versions do the same, but append a colon, a space, and the
+** results of strerror(errno) to the end of the message. All functions
+** accept printf-style formatting strings and arguments.
+**
+** If message_fatal_cleanup is non-NULL, it is called before exit by die and
+** sysdie and its return value is used as the argument to exit. It is a
+** pointer to a function taking no arguments and returning an int, and can be
+** used to call cleanup functions or to exit in some alternate fashion (such
+** as by calling _exit).
+**
+** If message_program_name is non-NULL, the string it points to, followed by
+** a colon and a space, is prepended to all error messages logged through the
+** message_log_stdout and message_log_stderr message handlers (the former is
+** the default for notice, and the latter is the default for warn and die).
+**
+** Honoring error_program_name and printing to stderr is just the default
+** handler; with message_handlers_* the handlers for any message function can
+** be changed. By default, notice prints to stdout, warn and die print to
+** stderr, and the others don't do anything at all. These functions take a
+** count of handlers and then that many function pointers, each one to a
+** function that takes a message length (the number of characters snprintf
+** generates given the format and arguments), a format, an argument list as a
+** va_list, and the applicable errno value (if any).
+**
+** Copyright (c) 2004, 2005, 2006
+** by Internet Systems Consortium, Inc. ("ISC")
+** Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+** 2002, 2003 by The Internet Software Consortium and Rich Salz
+**
+** This code is derived from software contributed to the Internet Software
+** Consortium by Rich Salz.
+**
+** Permission to use, copy, modify, and distribute this software for any
+** purpose with or without fee is hereby granted, provided that the above
+** copyright notice and this permission notice appear in all copies.
+**
+** THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+** REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY
+** SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+** WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+** ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+** OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <config.h>
+#include <system.h>
+
+#include <errno.h>
+#include <syslog.h>
+
+#include <util/util.h>
+
+/* The default handler lists. */
+static message_handler_func stdout_handlers[2] = {
+ message_log_stdout, NULL
+};
+static message_handler_func stderr_handlers[2] = {
+ message_log_stderr, NULL
+};
+
+/* The list of logging functions currently in effect. */
+static message_handler_func *debug_handlers = NULL;
+static message_handler_func *notice_handlers = stdout_handlers;
+static message_handler_func *warn_handlers = stderr_handlers;
+static message_handler_func *die_handlers = stderr_handlers;
+
+/* If non-NULL, called before exit and its return value passed to exit. */
+int (*message_fatal_cleanup)(void) = NULL;
+
+/* If non-NULL, prepended (followed by ": ") to messages. */
+const char *message_program_name = NULL;
+
+
+/*
+** Set the handlers for a particular message function. Takes a pointer to
+** the handler list, the count of handlers, and the argument list.
+*/
+static void
+message_handlers(message_handler_func **list, int count, va_list args)
+{
+ int i;
+
+ if (*list != stdout_handlers && *list != stderr_handlers)
+ free(*list);
+ *list = xmalloc(sizeof(message_handler_func) * (count + 1));
+ for (i = 0; i < count; i++)
+ (*list)[i] = (message_handler_func) va_arg(args, message_handler_func);
+ (*list)[count] = NULL;
+}
+
+
+/*
+** There's no good way of writing these handlers without a bunch of code
+** duplication since we can't assume variadic macros, but I can at least make
+** it easier to write and keep them consistent.
+*/
+#define HANDLER_FUNCTION(type) \
+ void \
+ message_handlers_ ## type(int count, ...) \
+ { \
+ va_list args; \
+ \
+ va_start(args, count); \
+ message_handlers(& type ## _handlers, count, args); \
+ va_end(args); \
+ }
+HANDLER_FUNCTION(debug)
+HANDLER_FUNCTION(notice)
+HANDLER_FUNCTION(warn)
+HANDLER_FUNCTION(die)
+
+
+/*
+** Print a message to stdout, supporting message_program_name.
+*/
+void
+message_log_stdout(int len UNUSED, const char *fmt, va_list args, int err)
+{
+ if (message_program_name != NULL)
+ fprintf(stdout, "%s: ", message_program_name);
+ vfprintf(stdout, fmt, args);
+ if (err)
+ fprintf(stdout, ": %s", strerror(err));
+ fprintf(stdout, "\n");
+}
+
+
+/*
+** Print a message to stderr, supporting message_program_name. Also flush
+** stdout so that errors and regular output occur in the right order.
+*/
+void
+message_log_stderr(int len UNUSED, const char *fmt, va_list args, int err)
+{
+ fflush(stdout);
+ if (message_program_name != NULL)
+ fprintf(stderr, "%s: ", message_program_name);
+ vfprintf(stderr, fmt, args);
+ if (err)
+ fprintf(stderr, ": %s", strerror(err));
+ fprintf(stderr, "\n");
+}
+
+
+/*
+** Log a message to syslog. This is a helper function used to implement all
+** of the syslog message log handlers. It takes the same arguments as a
+** regular message handler function but with an additional priority
+** argument.
+*/
+static void
+message_log_syslog(int pri, int len, const char *fmt, va_list args, int err)
+{
+ char *buffer;
+
+ buffer = malloc(len + 1);
+ if (buffer == NULL) {
+ fprintf(stderr, "failed to malloc %u bytes at %s line %d: %s",
+ len + 1, __FILE__, __LINE__, strerror(errno));
+ exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
+ }
+ vsnprintf(buffer, len + 1, fmt, args);
+ if (err == 0)
+ syslog(pri, "%s", buffer);
+ else
+ syslog(pri, "%s: %s", buffer, strerror(err));
+ free(buffer);
+}
+
+
+/*
+** Do the same sort of wrapper to generate all of the separate syslog logging
+** functions.
+*/
+#define SYSLOG_FUNCTION(name, type) \
+ void \
+ message_log_syslog_ ## name(int l, const char *f, va_list a, int e) \
+ { \
+ message_log_syslog(LOG_ ## type, l, f, a, e); \
+ }
+SYSLOG_FUNCTION(debug, DEBUG)
+SYSLOG_FUNCTION(info, INFO)
+SYSLOG_FUNCTION(notice, NOTICE)
+SYSLOG_FUNCTION(warning, WARNING)
+SYSLOG_FUNCTION(err, ERR)
+SYSLOG_FUNCTION(crit, CRIT)
+
+
+/*
+** All of the message handlers. There's a lot of code duplication here too,
+** but each one is still *slightly* different and va_start has to be called
+** multiple times, so it's hard to get rid of the duplication.
+*/
+
+void
+debug(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+
+ if (debug_handlers == NULL)
+ return;
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length < 0)
+ return;
+ for (log = debug_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, 0);
+ va_end(args);
+ }
+}
+
+void
+notice(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length < 0)
+ return;
+ for (log = notice_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, 0);
+ va_end(args);
+ }
+}
+
+void
+sysnotice(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+ int error = errno;
+
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length < 0)
+ return;
+ for (log = notice_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, error);
+ va_end(args);
+ }
+}
+
+void
+warn(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length < 0)
+ return;
+ for (log = warn_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, 0);
+ va_end(args);
+ }
+}
+
+void
+syswarn(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+ int error = errno;
+
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length < 0)
+ return;
+ for (log = warn_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, error);
+ va_end(args);
+ }
+}
+
+void
+die(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length >= 0)
+ for (log = die_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, 0);
+ va_end(args);
+ }
+ exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
+}
+
+void
+sysdie(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+ int error = errno;
+
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length >= 0)
+ for (log = die_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, error);
+ va_end(args);
+ }
+ exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
+}
diff --git a/util/util.h b/util/util.h
new file mode 100644
index 0000000..e2fce86
--- /dev/null
+++ b/util/util.h
@@ -0,0 +1,174 @@
+/* $Id$
+**
+** Utility functions.
+**
+** This is a variety of utility functions that are used internally by the
+** wallet client. Many of them came originally from INN.
+**
+** Written by Russ Allbery <rra@stanford.edu>
+** Copyright 2002, 2003, 2004, 2005, 2006, 2007
+** Board of Trustees, Leland Stanford Jr. University
+** Copyright (c) 2004, 2005, 2006, 2007
+** by Internet Systems Consortium, Inc. ("ISC")
+** Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+** 2002, 2003 by The Internet Software Consortium and Rich Salz
+**
+** See README for licensing terms.
+*/
+
+#ifndef UTIL_UTIL_H
+#define UTIL_UTIL_H 1
+
+#include <config.h>
+#include <portable/gssapi.h>
+
+#include <stdarg.h>
+#include <sys/types.h>
+
+/* __attribute__ is available in gcc 2.5 and later, but only with gcc 2.7
+ could you use the __format__ form of the attributes, which is what we use
+ (to avoid confusion with other macros). */
+#ifndef __attribute__
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
+# define __attribute__(spec) /* empty */
+# endif
+#endif
+
+/* Used for unused parameters to silence gcc warnings. */
+#define UNUSED __attribute__((__unused__))
+
+/* BEGIN_DECLS is used at the beginning of declarations so that C++
+ compilers don't mangle their names. END_DECLS is used at the end. */
+#undef BEGIN_DECLS
+#undef END_DECLS
+#ifdef __cplusplus
+# define BEGIN_DECLS extern "C" {
+# define END_DECLS }
+#else
+# define BEGIN_DECLS /* empty */
+# define END_DECLS /* empty */
+#endif
+
+BEGIN_DECLS
+
+/* Forward declarations to avoid includes. */
+struct addrinfo;
+struct iovec;
+struct sockaddr;
+
+/* Concatenate NULL-terminated strings into a newly allocated string. */
+extern char *concat(const char *first, ...);
+
+/* Given a base path and a file name, create a newly allocated path string.
+ The name will be appended to base with a / between them. Exceptionally, if
+ name begins with a slash, it will be strdup'd and returned as-is. */
+extern char *concatpath(const char *base, const char *name);
+
+/* The reporting functions. The ones prefaced by "sys" add a colon, a space,
+ and the results of strerror(errno) to the output and are intended for
+ reporting failures of system calls. */
+extern void debug(const char *, ...)
+ __attribute__((__format__(printf, 1, 2)));
+extern void notice(const char *, ...)
+ __attribute__((__format__(printf, 1, 2)));
+extern void sysnotice(const char *, ...)
+ __attribute__((__format__(printf, 1, 2)));
+extern void warn(const char *, ...)
+ __attribute__((__format__(printf, 1, 2)));
+extern void syswarn(const char *, ...)
+ __attribute__((__format__(printf, 1, 2)));
+extern void die(const char *, ...)
+ __attribute__((__noreturn__, __format__(printf, 1, 2)));
+extern void sysdie(const char *, ...)
+ __attribute__((__noreturn__, __format__(printf, 1, 2)));
+
+/* Set the handlers for various message functions. All of these functions
+ take a count of the number of handlers and then function pointers for each
+ of those handlers. These functions are not thread-safe; they set global
+ variables. */
+extern void message_handlers_debug(int count, ...);
+extern void message_handlers_notice(int count, ...);
+extern void message_handlers_warn(int count, ...);
+extern void message_handlers_die(int count, ...);
+
+/* Some useful handlers, intended to be passed to message_handlers_*. All
+ handlers take the length of the formatted message, the format, a variadic
+ argument list, and the errno setting if any. */
+extern void message_log_stdout(int, const char *, va_list, int);
+extern void message_log_stderr(int, const char *, va_list, int);
+extern void message_log_syslog_debug(int, const char *, va_list, int);
+extern void message_log_syslog_info(int, const char *, va_list, int);
+extern void message_log_syslog_notice(int, const char *, va_list, int);
+extern void message_log_syslog_warning(int, const char *, va_list, int);
+extern void message_log_syslog_err(int, const char *, va_list, int);
+extern void message_log_syslog_crit(int, const char *, va_list, int);
+
+/* The type of a message handler. */
+typedef void (*message_handler_func)(int, const char *, va_list, int);
+
+/* If non-NULL, called before exit and its return value passed to exit. */
+extern int (*message_fatal_cleanup)(void);
+
+/* If non-NULL, prepended (followed by ": ") to all messages printed by either
+ message_log_stdout or message_log_stderr. */
+extern const char *message_program_name;
+
+/* The functions are actually macros so that we can pick up the file and line
+ number information for debugging error messages without the user having to
+ pass those in every time. */
+#define xcalloc(n, size) x_calloc((n), (size), __FILE__, __LINE__)
+#define xmalloc(size) x_malloc((size), __FILE__, __LINE__)
+#define xrealloc(p, size) x_realloc((p), (size), __FILE__, __LINE__)
+#define xstrdup(p) x_strdup((p), __FILE__, __LINE__)
+#define xstrndup(p, size) x_strndup((p), (size), __FILE__, __LINE__)
+#define xvasprintf(p, f, a) x_vasprintf((p), (f), (a), __FILE__, __LINE__)
+
+/* asprintf is a special case since it takes variable arguments. If we have
+ support for variadic macros, we can still pass in the file and line and
+ just need to put them somewhere else in the argument list than last.
+ Otherwise, just call x_asprintf directly. This means that the number of
+ arguments x_asprintf takes must vary depending on whether variadic macros
+ are supported. */
+#ifdef HAVE_C99_VAMACROS
+# define xasprintf(p, f, ...) \
+ x_asprintf((p), __FILE__, __LINE__, (f), __VA_ARGS__)
+#elif HAVE_GNU_VAMACROS
+# define xasprintf(p, f, args...) \
+ x_asprintf((p), __FILE__, __LINE__, (f), args)
+#else
+# define xasprintf x_asprintf
+#endif
+
+/* Last two arguments are always file and line number. These are internal
+ implementations that should not be called directly. ISO C99 says that
+ identifiers beginning with _ and a lowercase letter are reserved for
+ identifiers of file scope, so while the position of libraries in the
+ standard isn't clear, it's probably not entirely kosher to use _xmalloc
+ here. Use x_malloc instead. */
+extern void *x_calloc(size_t, size_t, const char *, int);
+extern void *x_malloc(size_t, const char *, int);
+extern void *x_realloc(void *, size_t, const char *, int);
+extern char *x_strdup(const char *, const char *, int);
+extern char *x_strndup(const char *, size_t, const char *, int);
+extern int x_vasprintf(char **, const char *, va_list, const char *, int);
+
+/* asprintf special case. */
+#if HAVE_C99_VAMACROS || HAVE_GNU_VAMACROS
+extern int x_asprintf(char **, const char *, int, const char *, ...);
+#else
+extern int x_asprintf(char **, const char *, ...);
+#endif
+
+/* Failure handler takes the function, the size, the file, and the line. */
+typedef void (*xmalloc_handler_type)(const char *, size_t, const char *, int);
+
+/* The default error handler. */
+void xmalloc_fail(const char *, size_t, const char *, int);
+
+/* Assign to this variable to choose a handler other than the default, which
+ just calls sysdie. */
+extern xmalloc_handler_type xmalloc_error_handler;
+
+END_DECLS
+
+#endif /* UTIL_UTIL_H */
diff --git a/util/xmalloc.c b/util/xmalloc.c
new file mode 100644
index 0000000..635af75
--- /dev/null
+++ b/util/xmalloc.c
@@ -0,0 +1,238 @@
+/* $Id$
+**
+** malloc routines with failure handling.
+**
+** Usage:
+**
+** extern xmalloc_handler_t memory_error;
+** extern const char *string;
+** char *buffer;
+** va_list args;
+**
+** xmalloc_error_handler = memory_error;
+** buffer = xmalloc(1024);
+** xrealloc(buffer, 2048);
+** free(buffer);
+** buffer = xcalloc(1024);
+** free(buffer);
+** buffer = xstrdup(string);
+** free(buffer);
+** buffer = xstrndup(string, 25);
+** free(buffer);
+** xasprintf(&buffer, "%s", "some string");
+** free(buffer);
+** xvasprintf(&buffer, "%s", args);
+**
+** xmalloc, xcalloc, xrealloc, and xstrdup behave exactly like their C
+** library counterparts without the leading x except that they will never
+** return NULL. Instead, on error, they call xmalloc_error_handler,
+** passing it the name of the function whose memory allocation failed, the
+** amount of the allocation, and the file and line number where the
+** allocation function was invoked (from __FILE__ and __LINE__). This
+** function may do whatever it wishes, such as some action to free up
+** memory or a call to sleep to hope that system resources return. If the
+** handler returns, the interrupted memory allocation function will try its
+** allocation again (calling the handler again if it still fails).
+**
+** xstrndup behaves like xstrdup but only copies the given number of
+** characters. It allocates an additional byte over its second argument and
+** always nul-terminates the string.
+**
+** xasprintf and xvasprintf behave just like their GNU glibc library
+** implementations except that they do the same checking as described above.
+** xasprintf will only be able to provide accurate file and line information
+** on systems that support variadic macros.
+**
+** The default error handler, if none is set by the caller, prints an error
+** message to stderr and exits with exit status 1. An error handler must
+** take a const char * (function name), size_t (bytes allocated), const
+** char * (file), and int (line).
+**
+** xmalloc will return a pointer to a valid memory region on an xmalloc of 0
+** bytes, ensuring this by allocating space for one character instead of 0
+** bytes.
+**
+** The functions defined here are actually x_malloc, x_realloc, etc. The
+** header file defines macros named xmalloc, etc. that pass the file name
+** and line number to these functions.
+**
+** Copyright (c) 2004, 2005, 2006
+** by Internet Systems Consortium, Inc. ("ISC")
+** Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+** 2002, 2003 by The Internet Software Consortium and Rich Salz
+**
+** This code is derived from software contributed to the Internet Software
+** Consortium by Rich Salz.
+**
+** Permission to use, copy, modify, and distribute this software for any
+** purpose with or without fee is hereby granted, provided that the above
+** copyright notice and this permission notice appear in all copies.
+**
+** THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+** REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY
+** SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+** WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+** ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+** OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <config.h>
+#include <system.h>
+
+#include <errno.h>
+
+#include <util/util.h>
+
+/* The default error handler. */
+void
+xmalloc_fail(const char *function, size_t size, const char *file, int line)
+{
+ sysdie("failed to %s %lu bytes at %s line %d", function,
+ (unsigned long) size, file, line);
+}
+
+/* Assign to this variable to choose a handler other than the default. */
+xmalloc_handler_type xmalloc_error_handler = xmalloc_fail;
+
+void *
+x_malloc(size_t size, const char *file, int line)
+{
+ void *p;
+ size_t real_size;
+
+ real_size = (size > 0) ? size : 1;
+ p = malloc(real_size);
+ while (p == NULL) {
+ (*xmalloc_error_handler)("malloc", size, file, line);
+ p = malloc(real_size);
+ }
+ return p;
+}
+
+void *
+x_calloc(size_t n, size_t size, const char *file, int line)
+{
+ void *p;
+
+ n = (n > 0) ? n : 1;
+ size = (size > 0) ? size : 1;
+ p = calloc(n, size);
+ while (p == NULL) {
+ (*xmalloc_error_handler)("calloc", n * size, file, line);
+ p = calloc(n, size);
+ }
+ return p;
+}
+
+void *
+x_realloc(void *p, size_t size, const char *file, int line)
+{
+ void *newp;
+
+ newp = realloc(p, size);
+ while (newp == NULL && size > 0) {
+ (*xmalloc_error_handler)("realloc", size, file, line);
+ newp = realloc(p, size);
+ }
+ return newp;
+}
+
+char *
+x_strdup(const char *s, const char *file, int line)
+{
+ char *p;
+ size_t len;
+
+ len = strlen(s) + 1;
+ p = malloc(len);
+ while (p == NULL) {
+ (*xmalloc_error_handler)("strdup", len, file, line);
+ p = malloc(len);
+ }
+ memcpy(p, s, len);
+ return p;
+}
+
+char *
+x_strndup(const char *s, size_t size, const char *file, int line)
+{
+ char *p;
+
+ p = malloc(size + 1);
+ while (p == NULL) {
+ (*xmalloc_error_handler)("strndup", size + 1, file, line);
+ p = malloc(size + 1);
+ }
+ memcpy(p, s, size);
+ p[size] = '\0';
+ return p;
+}
+
+int
+x_vasprintf(char **strp, const char *fmt, va_list args, const char *file,
+ int line)
+{
+ va_list args_copy;
+ int status;
+
+ va_copy(args_copy, args);
+ status = vasprintf(strp, fmt, args_copy);
+ va_end(args_copy);
+ while (status < 0 && errno == ENOMEM) {
+ va_copy(args_copy, args);
+ status = vsnprintf(NULL, 0, fmt, args_copy);
+ va_end(args_copy);
+ (*xmalloc_error_handler)("vasprintf", status + 1, file, line);
+ va_copy(args_copy, args);
+ status = vasprintf(strp, fmt, args_copy);
+ va_end(args_copy);
+ }
+ return status;
+}
+
+#if HAVE_C99_VAMACROS || HAVE_GNU_VAMACROS
+int
+x_asprintf(char **strp, const char *file, int line, const char *fmt, ...)
+{
+ va_list args, args_copy;
+ int status;
+
+ va_start(args, fmt);
+ va_copy(args_copy, args);
+ status = vasprintf(strp, fmt, args_copy);
+ va_end(args_copy);
+ while (status < 0 && errno == ENOMEM) {
+ va_copy(args_copy, args);
+ status = vsnprintf(NULL, 0, fmt, args_copy);
+ va_end(args_copy);
+ (*xmalloc_error_handler)("asprintf", status + 1, file, line);
+ va_copy(args_copy, args);
+ status = vasprintf(strp, fmt, args_copy);
+ va_end(args_copy);
+ }
+ return status;
+}
+#else /* !(HAVE_C99_VAMACROS || HAVE_GNU_VAMACROS) */
+int
+x_asprintf(char **strp, const char *fmt, ...)
+{
+ va_list args, args_copy;
+ int status;
+
+ va_start(args, fmt);
+ va_copy(args_copy, args);
+ status = vasprintf(strp, fmt, args_copy);
+ va_end(args_copy);
+ while (status < 0 && errno == ENOMEM) {
+ va_copy(args_copy, args);
+ status = vsnprintf(NULL, 0, fmt, args_copy);
+ va_end(args_copy);
+ (*xmalloc_error_handler)("asprintf", status + 1, __FILE__, __LINE__);
+ va_copy(args_copy, args);
+ status = vasprintf(strp, fmt, args_copy);
+ va_end(args_copy);
+ }
+ return status;
+}
+#endif /* !(HAVE_C99_VAMACROS || HAVE_GNU_VAMACROS) */