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