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 /util | |
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.
Diffstat (limited to 'util')
-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 |
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) */ |