/passwd_change
/stamp-h1
.deps/
+.dirstamp
*.1
*.8
+*.a
*.o
-# 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
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
-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])
--- /dev/null
+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])])])
--- /dev/null
+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.])])])
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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) */
--- /dev/null
+/*
+ * 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 */