summaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authorRuss Allbery <rra@stanford.edu>2007-10-04 22:21:19 +0000
committerRuss Allbery <rra@stanford.edu>2007-10-04 22:21:19 +0000
commit9ff667addf39128f43d08d4ec56a6a94ec3bb062 (patch)
tree41cd39045fb2d37d343608af57aebf844ecd5690 /util
parent2f9387bdf0e047bbd193532c4fed209acabd0e7a (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.
Diffstat (limited to 'util')
-rw-r--r--util/concat.c76
-rw-r--r--util/messages.c361
-rw-r--r--util/util.h174
-rw-r--r--util/xmalloc.c238
4 files changed, 849 insertions, 0 deletions
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) */