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) */  | 
