provide asprintf functionality for internal use
authorKen Raeburn <raeburn@mit.edu>
Wed, 20 Jun 2007 01:09:10 +0000 (01:09 +0000)
committerKen Raeburn <raeburn@mit.edu>
Wed, 20 Jun 2007 01:09:10 +0000 (01:09 +0000)
I plan to use asprintf in some gssapi error-message management
routines, so let's make sure we have the functionality available,
implementing it locally if necessary.

This implementation assumes vsnprintf is available, an assumption that
the support library is already making at the moment.

Since this implementation requires calling vsnprintf potentially
multiple times with the same va_list, use va_copy if it's available,
or provide a hack version (which should work okay if va_list is a
scalar or array type that requires no other special handling, and if
va_end does nothing interesting, which is usually the case) if the
compiler doesn't provide it.

I also changed a couple bits of code to use asprintf, to make sure we
exercise our implementation in testing.

(C99 requires vsnprintf and va_copy; vasprintf is a GNU/BSD extension,
but an oh so useful one....)

* configure.in: Check for va_copy, or if va_list objects can be simply
assigned.  Define HAS_VA_COPY and CAN_COPY_VA_LIST as appropriate.
* include/k5-platform.h: Define a va_copy macro if the compiler
doesn't provide it.

* include/k5-platform.h: If vsnprintf isn't available from the OS,
abort compilation.  If vasprintf isn't available from the OS, provide
k5_{v,}asprintf based on vsnprintf and define {v,}asprintf macros.
* lib/krb5/keytab/t_keytab.c (do_test): Use asprintf.
* util/support/errors.c (krb5int_vset_error): Use asprintf
unconditionally.

ticket: new

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@19595 dc483132-0cff-0310-8789-dd5450dbe970

src/configure.in
src/include/k5-platform.h
src/lib/krb5/keytab/t_keytab.c
src/util/support/errors.c

index 0240e8cbcc61617c490849c1899506bef36817e5..947be9be1fe06c78246396e60cc0570af65fc27d 100644 (file)
@@ -14,6 +14,33 @@ dnl
 dnl
 AC_REQUIRE_CPP
 dnl
+AC_CACHE_CHECK(if va_copy is available, krb5_cv_va_copy,
+[AC_COMPILE_IFELSE([
+#include <stdarg.h>
+void f(va_list ap) {
+  va_list ap2;
+  va_copy(ap2, ap);
+  va_end(ap2);
+}], krb5_cv_va_copy=yes, krb5_cv_va_copy=no)])
+if test "$krb5_cv_va_copy" = yes; then
+  AC_DEFINE(HAS_VA_COPY,1,[Define if va_copy macro or function is available.])
+fi
+dnl
+dnl Note that this isn't checking if the copied value *works*, just
+dnl whether the C language constraints permit the copying.  If
+dnl va_list is defined as an array type, it can't be assigned.
+AC_CACHE_CHECK(if va_list objects can be copied by assignment,
+              krb5_cv_va_simple_copy,
+[AC_COMPILE_IFELSE([
+#include <stdarg.h>
+void f(va_list va2) {
+  va_list va1;
+  va1 = va2;
+}], krb5_cv_va_simple_copy=yes, krb5_cv_va_simple_copy=no)])
+if test "$krb5_cv_va_simple_copy" = yes; then
+  AC_DEFINE(CAN_COPY_VA_LIST,1,[Define if va_list objects can be simply copied by assignment.])
+fi
+dnl
 dnl The following lines are so that configure --help gives some global 
 dnl configuration options.
 dnl
index 757b122b3d57160c8894e8fed8b5d4a031701887..6cf446506632f412afcbb2aaf3a5b7bd44d86c73 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * k5-platform.h
  *
- * Copyright 2003, 2004, 2005 Massachusetts Institute of Technology.
+ * Copyright 2003, 2004, 2005, 2007 Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
  * + SIZE_MAX
  * + shared library init/fini hooks
  * + consistent getpwnam/getpwuid interfaces
+ * + va_copy fudged if not provided
+ * + [v]asprintf
  */
 
 #ifndef K5_PLATFORM_H
 #define K5_PLATFORM_H
 
 #include "autoconf.h"
-/* for memcpy */
 #include <string.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
 
 /* Initialization and finalization function support for libraries.
 
@@ -403,7 +408,6 @@ typedef struct { int error; unsigned char did_run; } k5_init_t;
 # define UINT64_TYPE unsigned long long
 #endif
 
-#include <limits.h>
 #ifndef SIZE_MAX
 # define SIZE_MAX ((size_t)((size_t)0 - 1))
 #endif
@@ -743,5 +747,90 @@ load_64_n (const unsigned char *p)
        (*(OUT) = getpwuid(UID), *(OUT) == NULL ? -1 : 0)
 #endif
 
+/* Since the original ANSI C spec left it undefined whether or
+   how you could copy around a va_list, C 99 added va_copy.
+   For old implementations, let's do our best to fake it.
+
+   XXX Doesn't yet handle implementations with __va_copy (early draft)
+   or GCC's __builtin_va_copy.  */
+#if defined(HAS_VA_COPY) || defined(va_copy)
+/* Do nothing.  */
+#elif defined(CAN_COPY_VA_LIST)
+#define va_copy(dest, src)     ((dest) = (src))
+#else
+/* Assume array type, but still simply copyable.
+
+   There is, theoretically, the possibility that va_start will
+   allocate some storage pointed to by the va_list, and in that case
+   we'll just lose.  If anyone cares, we could try to devise a test
+   for that case.  */
+#define va_copy(dest, src)     memcmp(dest, src, sizeof(va_list))
+#endif
+
+/* Provide [v]asprintf interfaces.  */
+#ifndef HAVE_VSNPRINTF
+#error We need an implementation of vsnprintf.
+#endif
+#ifndef HAVE_VASPRINTF
+#define vasprintf k5_vasprintf
+/* On error: BSD: Set *ret to NULL.  GNU: *ret is undefined.
+
+   Since we want to be able to use the GNU version directly, we need
+   provide only the weaker guarantee in this version.  */
+static inline int
+vasprintf(char **ret, const char *format, va_list ap)
+{
+    va_list ap2;
+    char *str = NULL, *nstr;
+    int len = 80, len2;
+
+    while (1) {
+       if (len < 0 || (size_t) len != len) {
+           free(str);
+           return -1;
+       }
+       nstr = realloc(str, (size_t) len);
+       if (nstr == NULL) {
+           free(str);
+           return -1;
+       }
+       str = nstr;
+       va_copy(ap2, ap);
+       len2 = vsnprintf(str, (size_t) len, format, ap2);
+       va_end(ap2);
+       if (len2 >= 0 && len2 < len) {
+           if (len2 < len-1) {
+               /* In a lot of cases, 80 will be quite a lot more than
+                  we need.  */
+               nstr = realloc(str, (size_t) len2+1);
+               if (nstr)
+                   str = nstr;
+           }
+           *ret = str;
+           return len2;
+       }
+       /* ISO C vsnprintf returns the needed length.  Some old
+          vsnprintf implementations return -1 on truncation.  */
+       if (len2 >= len)
+           len = len2 + 1;
+       else
+           len *= 2;
+    }
+}
+/* Assume HAVE_ASPRINTF iff HAVE_VASPRINTF.  */
+#define asprintf k5_asprintf
+static inline int
+k5_asprintf(char **ret, const char *format, ...)
+{
+    va_list ap;
+    int n;
+
+    va_start(ap, format);
+    n = vasprintf(ret, format, ap);
+    va_end(ap);
+    return n;
+}
+#endif
+
 
 #endif /* K5_PLATFORM_H */
index d16184e25d3fac6cda075daceb622330ba23008c..08610abac5b96d77a8f2ca08013a731daca8ff9c 100644 (file)
@@ -370,10 +370,16 @@ static void kt_test(krb5_context context, const char *name)
 static void do_test(krb5_context context, const char *prefix, 
                    krb5_boolean delete)
 {
-  char name[300], filename[300];
+  char *name, *filename;
 
-  sprintf(filename, "/tmp/kttest.%ld", (long) getpid());
-  sprintf(name, "%s%s", prefix, filename);
+  if (asprintf(&filename, "/tmp/kttest.%ld", (long) getpid()) < 0) {
+      perror("asprintf");
+      exit(1);
+  }
+  if (asprintf(&name, "%s%s", prefix, filename) < 0) {
+      perror("asprintf");
+      exit(1);
+  }
   printf("Starting test on %s\n", name);
   kt_test(context, name);
   printf("Test on %s passed\n", name);
index 56581218a2cdc7664f90429b01d31a6b23e3f499..e2101a2a9f1676afac8fb885433ee0705a65448c 100644 (file)
@@ -50,22 +50,24 @@ krb5int_vset_error (struct errinfo *ep, long code,
                    const char *fmt, va_list args)
 {
     char *p;
+    char *str = NULL;
+    va_list args2;
 
     if (ep->msg && ep->msg != ep->scratch_buf) {
        free (ep->msg);
        ep->msg = NULL;
     }
     ep->code = code;
-#ifdef HAVE_VASPRINTF
-    {
-       char *str = NULL;
-       if (vasprintf(&str, fmt, args) >= 0 && str != NULL) {
-           ep->msg = str;
-           return;
-       }
+    va_copy(args2, args);
+    if (vasprintf(&str, fmt, args2) >= 0 && str != NULL) {
+       va_end(args2);
+       ep->msg = str;
+       return;
     }
-#endif
+    va_end(args2);
+    /* Allocation failure?  */
     vsnprintf(ep->scratch_buf, sizeof(ep->scratch_buf), fmt, args);
+    /* Try again, just in case.  */
     p = strdup(ep->scratch_buf);
     ep->msg = p ? p : ep->scratch_buf;
 }