From fb1dfd092940905883ea6473162f5f6e36624da2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 1 Mar 2011 23:31:09 +1030 Subject: [PATCH] asprintf: new asprintf module. asprintf is a PITA to use, and it's not in POSIX anyway. Provide replacements, and also provide a nicer-to-use afmt() wrapper. --- ccan/asprintf/LICENSE | 1 + ccan/asprintf/_info | 44 ++++++++++++++++++++++++ ccan/asprintf/asprintf.c | 55 ++++++++++++++++++++++++++++++ ccan/asprintf/asprintf.h | 49 +++++++++++++++++++++++++++ ccan/asprintf/test/run.c | 73 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 222 insertions(+) create mode 120000 ccan/asprintf/LICENSE create mode 100644 ccan/asprintf/_info create mode 100644 ccan/asprintf/asprintf.c create mode 100644 ccan/asprintf/asprintf.h create mode 100644 ccan/asprintf/test/run.c diff --git a/ccan/asprintf/LICENSE b/ccan/asprintf/LICENSE new file mode 120000 index 00000000..2354d129 --- /dev/null +++ b/ccan/asprintf/LICENSE @@ -0,0 +1 @@ +../../licenses/BSD-MIT \ No newline at end of file diff --git a/ccan/asprintf/_info b/ccan/asprintf/_info new file mode 100644 index 00000000..1aa5d34f --- /dev/null +++ b/ccan/asprintf/_info @@ -0,0 +1,44 @@ +#include +#include "config.h" + +/** + * asprintf - asprintf wrapper (and if necessary, implementation). + * + * This provides a convenient wrapper for asprintf, and also implements + * asprintf if necessary. + * + * Author: Rusty Russell + * + * License: MIT + * + * Example: + * #include + * #include + * #include + * + * int main(int argc, char *argv[]) + * { + * char *p = afmt("This program has %i arguments", argc); + * int ret; + * + * while ((ret = write(STDOUT_FILENO, p, strlen(p))) > 0) { + * p += ret; + * if (!*p) + * exit(0); + * } + * err(1, "Writing to stdout"); + * } + */ +int main(int argc, char *argv[]) +{ + /* Expect exactly one argument */ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/compiler\n"); + return 0; + } + + return 1; +} diff --git a/ccan/asprintf/asprintf.c b/ccan/asprintf/asprintf.c new file mode 100644 index 00000000..b6ccce6a --- /dev/null +++ b/ccan/asprintf/asprintf.c @@ -0,0 +1,55 @@ +#include +#include +#include + +char *PRINTF_FMT(1, 2) afmt(const char *fmt, ...) +{ + va_list ap; + char *ptr; + + va_start(ap, fmt); + /* The BSD version apparently sets ptr to NULL on fail. GNU loses. */ + if (vasprintf(&ptr, fmt, ap) < 0) + ptr = NULL; + va_end(ap); + return ptr; +} + +#if !HAVE_ASPRINTF +#include +#include + +int vasprintf(char **strp, const char *fmt, va_list ap) +{ + int len; + va_list ap_copy; + + /* We need to make a copy of ap, since it's a use-once. */ + va_copy(ap_copy, ap); + len = vsnprintf(NULL, 0, fmt, ap_copy); + va_end(ap_copy); + + /* Until version 2.0.6 glibc would return -1 on truncated output. + * OTOH, they had asprintf. */ + if (len < 0) + return -1; + + *strp = malloc(len+1); + if (!*strp) + return -1; + + return vsprintf(*strp, fmt, ap); +} + +int asprintf(char **strp, const char *fmt, ...) +{ + va_list ap; + int len; + + va_start(ap, fmt); + len = vasprintf(strp, fmt, ap); + va_end(ap); + + return len; +} +#endif /* !HAVE_ASPRINTF */ diff --git a/ccan/asprintf/asprintf.h b/ccan/asprintf/asprintf.h new file mode 100644 index 00000000..d006ea4d --- /dev/null +++ b/ccan/asprintf/asprintf.h @@ -0,0 +1,49 @@ +#ifndef CCAN_ASPRINTF_H +#define CCAN_ASPRINTF_H +#include "config.h" +#include + +/** + * afmt - allocate and populate a string with the given format. + * @fmt: printf-style format. + * + * This is a simplified asprintf interface. Returns NULL on error. + */ +char *PRINTF_FMT(1, 2) afmt(const char *fmt, ...); + +#if HAVE_ASPRINTF +#include +#else +#include +/** + * asprintf - printf to a dynamically-allocated string. + * @strp: pointer to the string to allocate. + * @fmt: printf-style format. + * + * Returns -1 (and leaves @strp undefined) on an error. Otherwise returns + * number of bytes printed into @strp. + * + * Example: + * static char *greeting(const char *name) + * { + * char *str; + * int len = asprintf(&str, "Hello %s", name); + * if (len < 0) + * return NULL; + * return str; + * } + */ +int PRINTF_FMT(2, 3) asprintf(char **strp, const char *fmt, ...); + +/** + * vasprintf - vprintf to a dynamically-allocated string. + * @strp: pointer to the string to allocate. + * @fmt: printf-style format. + * + * Returns -1 (and leaves @strp undefined) on an error. Otherwise returns + * number of bytes printed into @strp. + */ +int vasprintf(char **strp, const char *fmt, va_list ap); +#endif + +#endif /* CCAN_ASPRINTF_H */ diff --git a/ccan/asprintf/test/run.c b/ccan/asprintf/test/run.c new file mode 100644 index 00000000..64c9990a --- /dev/null +++ b/ccan/asprintf/test/run.c @@ -0,0 +1,73 @@ +#include +/* Include the C files directly. */ + +/* Override vasprintf for testing. */ +#if HAVE_ASPRINTF +#define vasprintf my_vasprintf +static int my_vasprintf(char **strp, const char *fmt, va_list ap); +#else +#include +#include +#define vsnprintf my_vsnprintf +static int my_vsnprintf(char *str, size_t size, const char *format, va_list ap); +#endif + +#include +#include +#include +#include +#include + +static bool fail; + +#if HAVE_ASPRINTF +#undef vasprintf +static int my_vasprintf(char **strp, const char *fmt, va_list ap) +{ + if (fail) { + /* Set strp to crap. */ + *strp = (char *)(long)1; + return -1; + } + return vasprintf(strp, fmt, ap); +} +#else +#undef vsnprintf +static int my_vsnprintf(char *str, size_t size, const char *format, va_list ap) +{ + if (fail) { + return -1; + } + return vsnprintf(str, size, format, ap); +} +#endif + +int main(void) +{ + char *p, nul = '\0'; + int ret; + + /* This is how many tests you plan to run */ + plan_tests(8); + + fail = false; + p = afmt("Test %u%cafter-nul", 1, nul); + ok1(p); + ok1(strlen(p) == strlen("Test 1")); + ok1(memcmp(p, "Test 1\0after-nul\0", 17) == 0); + free(p); + + ret = asprintf(&p, "Test %u%cafter-nul", 1, nul); + ok1(ret == 16); + ok1(p); + ok1(strlen(p) == strlen("Test 1")); + ok1(memcmp(p, "Test 1\0after-nul\0", 17) == 0); + free(p); + + fail = true; + p = afmt("Test %u%cafter-nul", 1, nul); + ok1(p == NULL); + + /* This exits depending on whether all tests passed */ + return exit_status(); +} -- 2.39.2