]> eyrie.org Git - kerberos/kadmin-remctl.git/commitdiff
Import the portability and utility library from rra-c-util
authorRuss Allbery <rra@stanford.edu>
Wed, 10 Feb 2010 22:12:51 +0000 (14:12 -0800)
committerRuss Allbery <rra@stanford.edu>
Wed, 10 Feb 2010 22:12:51 +0000 (14:12 -0800)
Synchronized with rra-c-util 3.0.  The portability and utility library
isn't used yet.  That will come in subsequent commits.

20 files changed:
.gitignore
Makefile.am
configure.ac
m4/snprintf.m4 [new file with mode: 0644]
m4/vamacros.m4 [new file with mode: 0644]
portable/asprintf.c [new file with mode: 0644]
portable/dummy.c [new file with mode: 0644]
portable/krb5-extra.c [new file with mode: 0644]
portable/krb5.h [new file with mode: 0644]
portable/macros.h [new file with mode: 0644]
portable/snprintf.c [new file with mode: 0644]
portable/stdbool.h [new file with mode: 0644]
portable/system.h [new file with mode: 0644]
util/macros.h [new file with mode: 0644]
util/messages-krb5.c [new file with mode: 0644]
util/messages-krb5.h [new file with mode: 0644]
util/messages.c [new file with mode: 0644]
util/messages.h [new file with mode: 0644]
util/xmalloc.c [new file with mode: 0644]
util/xmalloc.h [new file with mode: 0644]

index a03f396fb0d1d0d35d9be2b3c860abbc85ac81df..a6c45dd7d089cc97bfb48e2803470188e0e97031 100644 (file)
@@ -12,6 +12,8 @@
 /passwd_change
 /stamp-h1
 .deps/
+.dirstamp
 *.1
 *.8
+*.a
 *.o
index 7650341f54b05d3120f8790de6c34bf53eb2d571..2f6a93c2fe0ff05598d9060cd49604cf99f6dc4a 100644 (file)
@@ -1,11 +1,11 @@
-# Makefile.am -- Automake makefile for kadmin-remctl.
+# Automake makefile for kadmin-remctl.
 #
 # Written by Russ Allbery <rra@stanford.edu>
-# Copyright 2007, 2008 Board of Trustees, Leland Stanford Jr. University
+# Copyright 2007, 2008, 2010 Board of Trustees, Leland Stanford Jr. University
 #
 # See LICENSE for licensing terms.
 
-AUTOMAKE_OPTIONS = foreign
+AUTOMAKE_OPTIONS = foreign subdir-objects
 ACLOCAL_AMFLAGS = -I m4
 EXTRA_DIST = LICENSE docs/design ksetpass.pod passwd_change.pod \
        remctl/kadmin remctl/password
@@ -14,21 +14,35 @@ EXTRA_DIST = LICENSE docs/design ksetpass.pod passwd_change.pod \
 AM_CPPFLAGS = $(KRB5_CPPFLAGS)
 AM_LDFLAGS = $(KRB5_LDFLAGS)
 
+noinst_LIBRARIES = portable/libportable.a util/libutil.a
+portable_libportable_a_SOURCES = portable/dummy.c portable/krb5-extra.c \
+        portable/krb5.h portable/macros.h portable/stdbool.h            \
+        portable/system.h
+portable_libportable_a_CPPFLAGS = $(KRB5_CPPFLAGS)
+portable_libportable_a_LIBADD = $(LIBOBJS)
+util_libutil_a_SOURCES = util/macros.h util/messages-krb5.c                \
+        util/messages-krb5.h util/messages.c util/messages.h util/xmalloc.c \
+        util/xmalloc.h
+util_libutil_a_CPPFLAGS = $(KRB5_CPPFLAGS)
+
 bin_PROGRAMS = passwd_change ksetpass
 passwd_change_CPPFLAGS = $(REMCTL_CPPFLAGS) $(AM_CPPFLAGS)
 passwd_change_LDFLAGS = $(REMCTL_LDFLAGS) $(AM_LDFLAGS)
-passwd_change_LDADD = $(REMCTL_LIBS) $(KRB5_LIBS)
-ksetpass_LDADD = $(KRB5_LIBS)
+passwd_change_LDADD = util/libutil.a portable/libportable.a $(REMCTL_LIBS) \
+       $(KRB5_LIBS)
+ksetpass_LDADD = util/libutil.a portable/libportable.a $(KRB5_LIBS)
 
-dist_sbin_SCRIPTS = kadmin-backend kadmin-backend-heimdal
+dist_sbin_SCRIPTS = kadmin-backend kadmin-backend-heim
 
-dist_man_MANS = passwd_change.1 kadmin-backend.8 ksetpass.1
+dist_man_MANS = passwd_change.1 kadmin-backend.8 kadmin-backend-heim.8 \
+       ksetpass.1
 
 # Make maintainer-clean do the right thing rather than what GNU thinks it
 # should do.
 MAINTAINERCLEANFILES = Makefile.in aclocal.m4 build-aux/depcomp \
        build-aux/install-sh build-aux/missing config.h.in config.h.in~ \
-       configure passwd_change.1 kadmin-backend.8 ksetpass.1
+       configure passwd_change.1 kadmin-backend.8 kadmin-backend-heim.8 \
+       ksetpass.1
 
 # A set of flags for warnings.  Add -O because gcc won't find some warnings
 # without optimization turned on, and add -DDEBUG=1 so we'll also compile all
index 00d4a2e812d2cf35368dd70fb565bb29f2fb9210..ab317275d23548233d594aa2f75f2ccc6455a00a 100644 (file)
@@ -1,26 +1,42 @@
-dnl Process this file with Autoconf to produce a configure script.
+dnl Autoconf configuration for kadmin-remctl.
 dnl
 dnl Written by Russ Allbery <rra@stanford.edu>
-dnl Copyright 2007, 2008 Board of Trustees, Leland Stanford Jr. University
+dnl Copyright 2007, 2008, 2010
+dnl     Board of Trustees, Leland Stanford Jr. University
 dnl
 dnl See LICENSE for licensing terms.
 
 AC_PREREQ([2.64])
 AC_INIT([kadmin-remctl], [2.4], [rra@stanford.edu])
 AC_CONFIG_AUX_DIR([build-aux])
+AC_CONFIG_LIBOBJ_DIR([portable])
 AC_CONFIG_MACRO_DIR([m4])
 AM_INIT_AUTOMAKE([1.11 check-news silent-rules])
 AM_MAINTAINER_MODE
 
 AC_PROG_CC
+AC_USE_SYSTEM_EXTENSIONS
+AM_PROG_CC_C_O
 AC_PROG_INSTALL
+AC_PROG_RANLIB
 
 RRA_LIB_REMCTL
 RRA_LIB_KRB5
+RRA_LIB_KRB5_SWITCH
+AC_CHECK_FUNCS([krb5_get_init_creds_opt_alloc \
+    krb5_get_init_creds_opt_set_default_flags \
+    krb5_principal_get_realm])
+RRA_LIB_KRB5_RESTORE
 
-dnl Temporary hack until we use a proper error reporting library, since we may
-dnl not insist on -lcom_err if we have better error handling functions.
-LIBS="-lcom_err $LIBS"
+AC_HEADER_STDBOOL
+AC_CHECK_HEADERS([sys/bitypes.h syslog.h])
+AC_CHECK_DECLS([snprintf, vsnprintf])
+RRA_C_C99_VAMACROS
+RRA_C_GNU_VAMACROS
+AC_TYPE_LONG_LONG_INT
+RRA_FUNC_SNPRINTF
+AC_CHECK_FUNCS([setrlimit])
+AC_REPLACE_FUNCS([asprintf])
 
 AC_CONFIG_FILES([Makefile])
 AC_CONFIG_HEADER([config.h])
diff --git a/m4/snprintf.m4 b/m4/snprintf.m4
new file mode 100644 (file)
index 0000000..d933f55
--- /dev/null
@@ -0,0 +1,55 @@
+dnl Test for a working C99 snprintf.
+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, 2008, 2009
+dnl     Board of Trustees, Leland Stanford Jr. University
+dnl
+dnl See LICENSE for licensing terms.
+
+dnl Source used by RRA_FUNC_SNPRINTF.
+AC_DEFUN([_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_RUN_IFELSE([AC_LANG_SOURCE([_RRA_FUNC_SNPRINTF_SOURCE])],
+        [rra_cv_func_snprintf_works=yes],
+        [rra_cv_func_snprintf_works=no],
+        [rra_cv_func_snprintf_works=no])])
+ AS_IF([test x"$rra_cv_func_snprintf_works" = xyes],
+    [AC_DEFINE([HAVE_SNPRINTF], 1,
+        [Define if your system has a working snprintf function.])],
+    [AC_LIBOBJ([snprintf])])])
diff --git a/m4/vamacros.m4 b/m4/vamacros.m4
new file mode 100644 (file)
index 0000000..855bb40
--- /dev/null
@@ -0,0 +1,62 @@
+dnl Check for support for variadic macros.
+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, 2008, 2009
+dnl     Board of Trustees, Leland Stanford Jr. University
+dnl
+dnl See LICENSE for licensing terms.
+
+AC_DEFUN([_RRA_C_C99_VAMACROS_SOURCE], [[
+#include <stdio.h>
+#define error(...) fprintf(stderr, __VA_ARGS__)
+
+int
+main(void) {
+    error("foo");
+    error("foo %d", 0);
+    return 0;
+}
+]])
+
+AC_DEFUN([RRA_C_C99_VAMACROS],
+[AC_CACHE_CHECK([for C99 variadic macros], [rra_cv_c_c99_vamacros],
+    [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_RRA_C_C99_VAMACROS_SOURCE])],
+        [rra_cv_c_c99_vamacros=yes],
+        [rra_cv_c_c99_vamacros=no])])
+ AS_IF([test x"$rra_cv_c_c99_vamacros" = xyes],
+    [AC_DEFINE([HAVE_C99_VAMACROS], 1,
+        [Define if the compiler supports C99 variadic macros.])])])
+
+AC_DEFUN([_RRA_C_GNU_VAMACROS_SOURCE], [[
+#include <stdio.h>
+#define error(args...) fprintf(stderr, args)
+
+int
+main(void) {
+    error("foo");
+    error("foo %d", 0);
+    return 0;
+}
+]])
+
+AC_DEFUN([RRA_C_GNU_VAMACROS],
+[AC_CACHE_CHECK([for GNU-style variadic macros], [rra_cv_c_gnu_vamacros],
+    [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_RRA_C_GNU_VAMACROS_SOURCE])],
+        [rra_cv_c_gnu_vamacros=yes],
+        [rra_cv_c_gnu_vamacros=no])])
+ AS_IF([test x"$rra_cv_c_gnu_vamacros" = xyes],
+    [AC_DEFINE([HAVE_GNU_VAMACROS], 1,
+        [Define if the compiler supports GNU-style variadic macros.])])])
diff --git a/portable/asprintf.c b/portable/asprintf.c
new file mode 100644 (file)
index 0000000..4219a19
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Replacement for a missing asprintf and vasprintf.
+ *
+ * Provides the same functionality as the standard GNU library routines
+ * asprintf and vasprintf for those platforms that don't have them.
+ *
+ * Written by Russ Allbery <rra@stanford.edu>
+ * This work is hereby placed in the public domain by its author.
+ */
+
+#include <config.h>
+#include <portable/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 *, ...)
+    __attribute__((__format__(printf, 2, 3)));
+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 (file)
index 0000000..8a0d54d
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Dummy symbol to prevent an empty library.
+ *
+ * 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.
+ *
+ * Written by Russ Allbery <rra@stanford.edu>
+ * This work is hereby placed in the public domain by its author.
+ */
+
+/* Prototype to avoid gcc warnings. */
+int portable_dummy(void);
+
+int
+portable_dummy(void)
+{
+    return 42;
+}
diff --git a/portable/krb5-extra.c b/portable/krb5-extra.c
new file mode 100644 (file)
index 0000000..1d95d81
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Portability glue functions for Kerberos.
+ *
+ * This file provides definitions of the interfaces that portable/krb5.h
+ * ensures exist if the function wasn't available in the Kerberos libraries.
+ * Everything in this file will be protected by #ifndef.  If the native
+ * Kerberos libraries are fully capable, this file will be skipped.
+ *
+ * Written by Russ Allbery <rra@stanford.edu>
+ * This work is hereby placed in the public domain by its author.
+ */
+
+#include <config.h>
+#include <portable/krb5.h>
+#include <portable/system.h>
+
+#include <errno.h>
+
+/* Figure out what header files to include for error reporting. */
+#if !defined(HAVE_KRB5_GET_ERROR_MESSAGE) && !defined(HAVE_KRB5_GET_ERR_TEXT)
+# if !defined(HAVE_KRB5_GET_ERROR_STRING)
+#  if defined(HAVE_IBM_SVC_KRB5_SVC_H)
+#   include <ibm_svc/krb5_svc.h>
+#  elif defined(HAVE_ET_COM_ERR_H)
+#   include <et/com_err.h>
+#  else
+#   include <com_err.h>
+#  endif
+# endif
+#endif
+
+/* Used for unused parameters to silence gcc warnings. */
+#define UNUSED __attribute__((__unused__))
+
+/*
+ * This string is returned for unknown error messages.  We use a static
+ * variable so that we can be sure not to free it.
+ */
+static const char error_unknown[] = "unknown error";
+
+
+#ifndef HAVE_KRB5_GET_ERROR_MESSAGE
+/*
+ * Given a Kerberos error code, return the corresponding error.  Prefer the
+ * Kerberos interface if available since it will provide context-specific
+ * error information, whereas the error_message() call will only provide a
+ * fixed message.
+ */
+const char *
+krb5_get_error_message(krb5_context ctx UNUSED, krb5_error_code code UNUSED)
+{
+    const char *msg = NULL;
+
+# if defined(HAVE_KRB5_GET_ERROR_STRING)
+    msg = krb5_get_error_string(ctx);
+# elif defined(HAVE_KRB5_GET_ERR_TEXT)
+    msg = krb5_get_err_text(ctx, code);
+# elif defined(HAVE_KRB5_SVC_GET_MSG)
+    krb5_svc_get_msg(code, (char **) &msg);
+# else
+    msg = error_message(code);
+# endif
+    if (msg == NULL)
+        return error_unknown;
+    else
+        return msg;
+}
+#endif /* !HAVE_KRB5_GET_ERROR_MESSAGE */
+
+
+#ifndef HAVE_KRB5_FREE_ERROR_MESSAGE
+/*
+ * Free an error string if necessary.  If we returned a static string, make
+ * sure we don't free it.
+ *
+ * This code assumes that the set of implementations that have
+ * krb5_free_error_message is a subset of those with krb5_get_error_message.
+ * If this assumption ever breaks, we may call the wrong free function.
+ */
+static void
+krb5_free_error_message(krb5_context ctx UNUSED, const char *msg)
+{
+    if (msg == error_unknown)
+        return;
+# if defined(HAVE_KRB5_GET_ERROR_STRING)
+    krb5_free_error_string(ctx, (char *) msg);
+# elif defined(HAVE_KRB5_SVC_GET_MSG)
+    krb5_free_string(ctx, (char *) msg);
+# endif
+}
+#endif /* !HAVE_KRB5_FREE_ERROR_MESSAGE */
+
+
+#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
+/*
+ * Allocate and initialize a krb5_get_init_creds_opt struct.  This code
+ * assumes that an all-zero bit pattern will create a NULL pointer.
+ */
+krb5_error_code
+krb5_get_init_creds_opt_alloc(krb5_context ctx UNUSED,
+                              krb5_get_init_creds_opt **opts)
+{
+    *opts = calloc(1, sizeof(krb5_get_init_creds_opt));
+    if (*opts == NULL)
+        return errno;
+    krb5_get_init_creds_opt_init(*opts);
+    return 0;
+}
+#endif /* !HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC */
diff --git a/portable/krb5.h b/portable/krb5.h
new file mode 100644 (file)
index 0000000..8b8c696
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Portability wrapper around krb5.h.
+ *
+ * This header includes krb5.h and then adjusts for various portability
+ * issues, primarily between MIT Kerberos and Heimdal, so that code can be
+ * written to a consistent API.
+ *
+ * Unfortunately, due to the nature of the differences between MIT Kerberos
+ * and Heimdal, it's not possible to write code to either one of the APIs and
+ * adjust for the other one.  In general, this header tries to make available
+ * the Heimdal API and fix it for MIT Kerberos, but there are places where MIT
+ * Kerberos requires a more specific call.  For those cases, it provides the
+ * most specific interface.
+ *
+ * For example, MIT Kerberos has krb5_free_unparsed_name() whereas Heimdal
+ * prefers the generic krb5_xfree().  In this case, this header provides
+ * krb5_free_unparsed_name() for both APIs since it's the most specific call.
+ *
+ * Written by Russ Allbery <rra@stanford.edu>
+ * This work is hereby placed in the public domain by its author.
+ */
+
+#ifndef PORTABLE_KRB5_H
+#define PORTABLE_KRB5_H 1
+
+#include <config.h>
+#include <portable/macros.h>
+
+#include <krb5.h>
+
+BEGIN_DECLS
+
+/* Default to a hidden visibility for all portability functions. */
+#pragma GCC visibility push(hidden)
+
+/*
+ * krb5_{get,free}_error_message are the preferred APIs for both current MIT
+ * and current Heimdal, but there are tons of older APIs we may have to fall
+ * back on for earlier versions.
+ *
+ * This function should be called immediately after the corresponding error
+ * without any intervening Kerberos calls.  Otherwise, the correct error
+ * message and supporting information may not be returned.
+ */
+#ifndef HAVE_KRB5_GET_ERROR_MESSAGE
+const char *krb5_get_error_message(krb5_context, krb5_error_code);
+#endif
+#ifndef HAVE_KRB5_FREE_ERROR_MESSAGE
+void krb5_free_error_message(krb5_context, const char *);
+#endif
+
+/*
+ * Both current MIT and current Heimdal prefer _opt_alloc, but older versions
+ * of both require allocating your own struct and calling _opt_init.
+ */
+#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
+krb5_error_code krb5_get_init_creds_opt_alloc(krb5_context,
+                                              krb5_get_init_creds_opt **);
+#endif
+
+/* Heimdal-specific. */
+#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_DEFAULT_FLAGS
+#define krb5_get_init_creds_opt_set_default_flags(c, p, r, o) /* empty */
+#endif
+
+/*
+ * Heimdal provides a nice function that just returns a const char *.  On MIT,
+ * there's an accessor macro that returns the krb5_data pointer, wihch
+ * requires more work to get at the underlying char *.
+ */
+#ifndef HAVE_KRB5_PRINCIPAL_GET_REALM
+const char *krb5_principal_get_realm(krb5_context, krb5_const_principal);
+#endif
+
+/* Undo default visibility change. */
+#pragma GCC visibility pop
+
+#endif /* !PORTABLE_KRB5_H */
diff --git a/portable/macros.h b/portable/macros.h
new file mode 100644 (file)
index 0000000..8d5adbd
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Portability macros used in include files.
+ *
+ * Written by Russ Allbery <rra@stanford.edu>
+ * This work is hereby placed in the public domain by its author.
+ */
+
+#ifndef PORTABLE_MACROS_H
+#define PORTABLE_MACROS_H 1
+
+/*
+ * __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
+
+/*
+ * 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
+
+#endif /* !PORTABLE_MACROS_H */
diff --git a/portable/snprintf.c b/portable/snprintf.c
new file mode 100644 (file)
index 0000000..ab3121c
--- /dev/null
@@ -0,0 +1,942 @@
+/*
+ * 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)
+
+/* Assume all compilers support long double, per Autoconf documentation. */
+#define LDOUBLE long double
+
+#ifdef HAVE_LONG_LONG_INT
+# 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/portable/stdbool.h b/portable/stdbool.h
new file mode 100644 (file)
index 0000000..bfbf4c4
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Portability wrapper around <stdbool.h>.
+ *
+ * Provides the bool and _Bool types and the true and false constants,
+ * following the C99 specification, on hosts that don't have stdbool.h.  This
+ * logic is based heavily on the example in the Autoconf manual.
+ *
+ * Written by Russ Allbery <rra@stanford.edu>
+ * This work is hereby placed in the public domain by its author.
+ */
+
+#ifndef PORTABLE_STDBOOL_H
+#define PORTABLE_STDBOOL_H 1
+
+#if HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# if HAVE__BOOL
+#  define bool _Bool
+# else
+#  ifdef __cplusplus
+typedef bool _Bool;
+#  elif _WIN32
+#   include <windef.h>
+#   define bool BOOL
+#  else
+typedef unsigned char _Bool;
+#   define bool _Bool
+#  endif
+# endif
+# define false 0
+# define true  1
+# define __bool_true_false_are_defined 1
+#endif
+
+/*
+ * If we define bool and don't tell Perl, it will try to define its own and
+ * fail.  Only of interest for programs that also include Perl headers.
+ */
+#ifndef HAS_BOOL
+# define HAS_BOOL 1
+#endif
+
+#endif /* !PORTABLE_STDBOOL_H */
diff --git a/portable/system.h b/portable/system.h
new file mode 100644 (file)
index 0000000..70efa33
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Standard system includes and portability adjustments.
+ *
+ * Declarations of routines and variables in the C library.  Including this
+ * file is the equivalent of including all of the following headers,
+ * portably:
+ *
+ *     #include <sys/types.h>
+ *     #include <stdarg.h>
+ *     #include <stdbool.h>
+ *     #include <stdio.h>
+ *     #include <stdlib.h>
+ *     #include <stddef.h>
+ *     #include <stdint.h>
+ *     #include <string.h>
+ *     #include <unistd.h>
+ *
+ * Missing functions are provided via #define or prototyped if available from
+ * the portable helper library.  Also provides some standard #defines.
+ *
+ * Written by Russ Allbery <rra@stanford.edu>
+ * This work is hereby placed in the public domain by its author.
+ */
+
+#ifndef PORTABLE_SYSTEM_H
+#define PORTABLE_SYSTEM_H 1
+
+/* Make sure we have our configuration information. */
+#include <config.h>
+
+/* BEGIN_DECL and __attribute__. */
+#include <portable/macros.h>
+
+/* A set of standard ANSI C headers.  We don't care about pre-ANSI systems. */
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <string.h>
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+/* SCO OpenServer gets int32_t from here. */
+#if HAVE_SYS_BITYPES_H
+# include <sys/bitypes.h>
+#endif
+
+/* Get the bool type. */
+#include <portable/stdbool.h>
+
+BEGIN_DECLS
+
+/* Default to a hidden visibility for all portability functions. */
+#pragma GCC visibility push(hidden)
+
+/*
+ * 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 *, ...)
+    __attribute__((__format__(printf, 2, 3)));
+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
+
+/* Undo default visibility change. */
+#pragma GCC visibility pop
+
+END_DECLS
+
+/* Windows provides snprintf under a different name. */
+#ifdef _WIN32
+# define snprintf _snprintf
+#endif
+
+/*
+ * POSIX requires that these be defined in <unistd.h>.  If one of them has
+ * been defined, all the rest almost certainly have.
+ */
+#ifndef STDIN_FILENO
+# define STDIN_FILENO  0
+# define STDOUT_FILENO 1
+# define STDERR_FILENO 2
+#endif
+
+/*
+ * 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 /* !PORTABLE_SYSTEM_H */
diff --git a/util/macros.h b/util/macros.h
new file mode 100644 (file)
index 0000000..97b2c2b
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Some standard helpful macros.
+ *
+ * Written by Russ Allbery <rra@stanford.edu>
+ * This work is hereby placed in the public domain by its author.
+ */
+
+#ifndef UTIL_MACROS_H
+#define UTIL_MACROS_H 1
+
+#include <config.h>
+#include <portable/macros.h>
+
+/* Used for unused parameters to silence gcc warnings. */
+#define UNUSED __attribute__((__unused__))
+
+#endif /* UTIL_MACROS_H */
diff --git a/util/messages-krb5.c b/util/messages-krb5.c
new file mode 100644 (file)
index 0000000..7f35d29
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Error handling for Kerberos v5.
+ *
+ * Provides versions of die and warn that take a Kerberos context and a
+ * Kerberos error code and append the Kerberos error message to the provided
+ * formatted message.
+ *
+ * Written by Russ Allbery <rra@stanford.edu>
+ * Copyright 2006, 2007, 2008, 2009, 2010
+ *     Board of Trustees, Leland Stanford Jr. University
+ *
+ * See LICENSE for licensing terms.
+ */
+
+#include <config.h>
+#include <portable/krb5.h>
+#include <portable/system.h>
+
+#include <util/macros.h>
+#include <util/messages.h>
+#include <util/messages-krb5.h>
+#include <util/xmalloc.h>
+
+
+/*
+ * Report a Kerberos error and exit.
+ */
+void
+die_krb5(krb5_context ctx, krb5_error_code code, const char *format, ...)
+{
+    const char *k5_msg = NULL;
+    char *message;
+    va_list args;
+
+    k5_msg = krb5_get_error_message(ctx, code);
+    va_start(args, format);
+    if (xvasprintf(&message, format, args) < 0)
+        die("internal error: unable to format error message");
+    va_end(args);
+    die("%s: %s", message, k5_msg);
+}
+
+
+/*
+ * Report a Kerberos error.
+ */
+void
+warn_krb5(krb5_context ctx, krb5_error_code code, const char *format, ...)
+{
+    const char *k5_msg = NULL;
+    char *message;
+    va_list args;
+
+    k5_msg = krb5_get_error_message(ctx, code);
+    va_start(args, format);
+    if (xvasprintf(&message, format, args) < 0)
+        die("internal error: unable to format error message");
+    va_end(args);
+    warn("%s: %s", message, k5_msg);
+    free(message);
+    krb5_free_error_message(ctx, k5_msg);
+}
diff --git a/util/messages-krb5.h b/util/messages-krb5.h
new file mode 100644 (file)
index 0000000..3b763c8
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Prototypes for error handling for Kerberos.
+ *
+ * Written by Russ Allbery <rra@stanford.edu>
+ * Copyright 2006, 2007, 2008, 2009, 2010
+ *     Board of Trustees, Leland Stanford Jr. University
+ *
+ * See LICENSE for licensing terms.
+ */
+
+#ifndef UTIL_MESSAGES_KRB5_H
+#define UTIL_MESSAGES_KRB5_H 1
+
+#include <config.h>
+#include <portable/macros.h>
+
+#include <krb5.h>
+#include <sys/types.h>
+
+BEGIN_DECLS
+
+/* Default to a hidden visibility for all util functions. */
+#pragma GCC visibility push(hidden)
+
+/*
+ * The Kerberos versions of the reporting functions.  These take a context and
+ * an error code to get the Kerberos error.
+ */
+void die_krb5(krb5_context, krb5_error_code, const char *, ...)
+    __attribute__((__nonnull__, __noreturn__, __format__(printf, 3, 4)));
+void warn_krb5(krb5_context, krb5_error_code, const char *, ...)
+    __attribute__((__nonnull__, __format__(printf, 3, 4)));
+
+/* Undo default visibility change. */
+#pragma GCC visibility pop
+
+END_DECLS
+
+#endif /* UTIL_MESSAGES_KRB5_H */
diff --git a/util/messages.c b/util/messages.c
new file mode 100644 (file)
index 0000000..ef920b2
--- /dev/null
@@ -0,0 +1,377 @@
+/*
+ * 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 2008 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
+ *
+ * See LICENSE for licensing terms.
+ */
+
+#include <config.h>
+#include <portable/system.h>
+
+#include <errno.h>
+#ifdef HAVE_SYSLOG_H
+# include <syslog.h>
+#endif
+
+#ifdef _WIN32
+# include <windows.h>
+# define LOG_DEBUG      EVENTLOG_SUCCESS
+# define LOG_INFO       EVENTLOG_INFORMATION_TYPE
+# define LOG_NOTICE     EVENTLOG_INFORMATION_TYPE
+# define LOG_WARNING    EVENTLOG_WARNING_TYPE
+# define LOG_ERR        EVENTLOG_ERROR_TYPE
+# define LOG_CRIT       EVENTLOG_ERROR_TYPE
+#endif
+
+#include <util/macros.h>
+#include <util/messages.h>
+#include <util/xmalloc.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");
+    fflush(stdout);
+}
+
+
+/*
+ * 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.
+ *
+ * This needs further attention on Windows.  For example, it currently doesn't
+ * log the errno information.
+ */
+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);
+#ifdef _WIN32
+    {
+        HANDLE eventlog;
+
+        eventlog = RegisterEventSource(NULL, message_program_name);
+        if (eventlog != NULL) {
+            ReportEvent(eventlog, (WORD) pri, 0, 0, NULL, 1, 0, &buffer, NULL);
+            CloseEventLog(eventlog);
+        }
+    }
+#else /* !_WIN32 */
+    if (err == 0)
+        syslog(pri, "%s", buffer);
+    else
+        syslog(pri, "%s: %s", buffer, strerror(err));
+#endif /* !_WIN32 */
+    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/messages.h b/util/messages.h
new file mode 100644 (file)
index 0000000..ff86f39
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Prototypes for message and error reporting (possibly fatal).
+ *
+ * Copyright 2008, 2010 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
+ *
+ * See LICENSE for licensing terms.
+ */
+
+#ifndef UTIL_MESSAGES_H
+#define UTIL_MESSAGES_H 1
+
+#include <config.h>
+#include <portable/macros.h>
+
+#include <stdarg.h>
+
+BEGIN_DECLS
+
+/* Default to a hidden visibility for all util functions. */
+#pragma GCC visibility push(hidden)
+
+/*
+ * 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.
+ */
+void debug(const char *, ...)
+    __attribute__((__nonnull__, __format__(printf, 1, 2)));
+void notice(const char *, ...)
+    __attribute__((__nonnull__, __format__(printf, 1, 2)));
+void sysnotice(const char *, ...)
+    __attribute__((__nonnull__, __format__(printf, 1, 2)));
+void warn(const char *, ...)
+    __attribute__((__nonnull__, __format__(printf, 1, 2)));
+void syswarn(const char *, ...)
+    __attribute__((__nonnull__, __format__(printf, 1, 2)));
+void die(const char *, ...)
+    __attribute__((__nonnull__, __noreturn__, __format__(printf, 1, 2)));
+void sysdie(const char *, ...)
+    __attribute__((__nonnull__, __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.
+ */
+void message_handlers_debug(int count, ...);
+void message_handlers_notice(int count, ...);
+void message_handlers_warn(int count, ...);
+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.
+ */
+void message_log_stdout(int, const char *, va_list, int)
+    __attribute((__nonnull__));
+void message_log_stderr(int, const char *, va_list, int)
+    __attribute((__nonnull__));
+void message_log_syslog_debug(int, const char *, va_list, int)
+    __attribute((__nonnull__));
+void message_log_syslog_info(int, const char *, va_list, int)
+    __attribute((__nonnull__));
+void message_log_syslog_notice(int, const char *, va_list, int)
+    __attribute((__nonnull__));
+void message_log_syslog_warning(int, const char *, va_list, int)
+    __attribute((__nonnull__));
+void message_log_syslog_err(int, const char *, va_list, int)
+    __attribute((__nonnull__));
+void message_log_syslog_crit(int, const char *, va_list, int)
+    __attribute((__nonnull__));
+
+/* 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;
+
+/* Undo default visibility change. */
+#pragma GCC visibility pop
+
+END_DECLS
+
+#endif /* UTIL_MESSAGES_H */
diff --git a/util/xmalloc.c b/util/xmalloc.c
new file mode 100644 (file)
index 0000000..4e05f96
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * 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
+ *
+ * See LICENSE for licensing terms.
+ */
+
+#include <config.h>
+#include <portable/system.h>
+
+#include <errno.h>
+
+#include <util/messages.h>
+#include <util/xmalloc.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) */
diff --git a/util/xmalloc.h b/util/xmalloc.h
new file mode 100644 (file)
index 0000000..657a6bb
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Prototypes for malloc routines with failure handling.
+ *
+ * Copyright 2010 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
+ *
+ * See LICENSE for licensing terms.
+ */
+
+#ifndef UTIL_XMALLOC_H
+#define UTIL_XMALLOC_H 1
+
+#include <config.h>
+#include <portable/macros.h>
+
+#include <sys/types.h>
+
+/*
+ * 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
+
+BEGIN_DECLS
+
+/* Default to a hidden visibility for all util functions. */
+#pragma GCC visibility push(hidden)
+
+/*
+ * Last two arguments are always file and line number.  These are internal
+ * implementations that should not be called directly.
+ */
+void *x_calloc(size_t, size_t, const char *, int)
+    __attribute__((__alloc_size__(1, 2), __malloc__, __nonnull__));
+void *x_malloc(size_t, const char *, int)
+    __attribute__((__alloc_size__(1), __malloc__, __nonnull__));
+void *x_realloc(void *, size_t, const char *, int)
+    __attribute__((__alloc_size__(2), __malloc__, __nonnull__(3)));
+char *x_strdup(const char *, const char *, int)
+    __attribute__((__malloc__, __nonnull__));
+char *x_strndup(const char *, size_t, const char *, int)
+    __attribute__((__malloc__, __nonnull__));
+int x_vasprintf(char **, const char *, va_list, const char *, int)
+    __attribute__((__nonnull__));
+
+/* asprintf special case. */
+#if HAVE_C99_VAMACROS || HAVE_GNU_VAMACROS
+int x_asprintf(char **, const char *, int, const char *, ...)
+    __attribute__((__nonnull__, __format__(printf, 4, 5)));
+#else
+int x_asprintf(char **, const char *, ...)
+    __attribute__((__nonnull__, __format__(printf, 2, 3)));
+#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)
+    __attribute__((__nonnull__));
+
+/*
+ * Assign to this variable to choose a handler other than the default, which
+ * just calls sysdie.
+ */
+extern xmalloc_handler_type xmalloc_error_handler;
+
+/* Undo default visibility change. */
+#pragma GCC visibility pop
+
+END_DECLS
+
+#endif /* UTIL_XMALLOC_H */