From 9ff667addf39128f43d08d4ec56a6a94ec3bb062 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Thu, 4 Oct 2007 22:21:19 +0000 Subject: 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. --- util/xmalloc.c | 238 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 util/xmalloc.c (limited to 'util/xmalloc.c') 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 +#include + +#include + +#include + +/* 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) */ -- cgit v1.2.3