From 098aca8fe945d5c220ce5bfaa5020972e0f685c1 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 28 Nov 2020 11:30:38 -0500 Subject: dstring: New dstrdcat(), dstrcatf(), dstrvcatf() functions --- dstring.c | 77 +++++++++++++++++++++++++++++++++++++++++++++------------------ dstring.h | 43 ++++++++++++++++++++++++++++++++++- 2 files changed, 97 insertions(+), 23 deletions(-) diff --git a/dstring.c b/dstring.c index 2ec0d72..475bf65 100644 --- a/dstring.c +++ b/dstring.c @@ -1,6 +1,6 @@ /**************************************************************************** * bfs * - * Copyright (C) 2016-2019 Tavian Barnes * + * Copyright (C) 2016-2020 Tavian Barnes * * * * Permission to use, copy, modify, and/or distribute this software for any * * purpose with or without fee is hereby granted. * @@ -124,6 +124,10 @@ int dstrncat(char **dest, const char *src, size_t n) { return dstrcat_impl(dest, src, strnlen(src, n)); } +int dstrdcat(char **dest, const char *src) { + return dstrcat_impl(dest, src, dstrlen(src)); +} + int dstrapp(char **str, char c) { return dstrcat_impl(str, &c, 1); } @@ -139,40 +143,69 @@ char *dstrprintf(const char *format, ...) { } char *dstrvprintf(const char *format, va_list args) { - // Guess a length to try to avoid calling vsnprintf() twice - int len, cap = 2*strlen(format); - char *str = dstralloc(cap); + // Guess a capacity to try to avoid reallocating + char *str = dstralloc(2*strlen(format)); if (!str) { return NULL; } - va_list args2; - va_copy(args2, args); + if (dstrvcatf(&str, format, args) != 0) { + dstrfree(str); + return NULL; + } - len = vsnprintf(str, cap + 1, format, args); - if (len > cap) { - if (dstreserve(&str, len) != 0) { - goto fail; - } + return str; +} - cap = len; - len = vsnprintf(str, cap + 1, format, args2); - assert(len == cap); - } +int dstrcatf(char **str, const char *format, ...) { + va_list args; + + va_start(args, format); + int ret = dstrvcatf(str, format, args); + va_end(args); - va_end(args2); + return ret; +} + +int dstrvcatf(char **str, const char *format, va_list args) { + // Guess a capacity to try to avoid calling vsnprintf() twice + size_t len = dstrlen(*str); + dstreserve(str, len + 2*strlen(format)); + size_t cap = dstrheader(*str)->capacity; - if (len < 0) { + va_list copy; + va_copy(copy, args); + + char *tail = *str + len; + int ret = vsnprintf(tail, cap - len + 1, format, args); + if (ret < 0) { goto fail; } - struct dstring *header = dstrheader(str); - header->length = len; - return str; + size_t tail_len = ret; + if (tail_len > cap - len) { + cap = len + tail_len; + if (dstreserve(str, cap) != 0) { + goto fail; + } + + tail = *str + len; + ret = vsnprintf(tail, tail_len + 1, format, copy); + if (ret < 0 || (size_t)ret != tail_len) { + assert(false); + goto fail; + } + } + + va_end(copy); + + struct dstring *header = dstrheader(*str); + header->length += tail_len; + return 0; fail: - dstrfree(str); - return NULL; + *tail = '\0'; + return -1; } void dstrfree(char *dstr) { diff --git a/dstring.h b/dstring.h index b556313..54106f3 100644 --- a/dstring.h +++ b/dstring.h @@ -1,6 +1,6 @@ /**************************************************************************** * bfs * - * Copyright (C) 2016-2019 Tavian Barnes * + * Copyright (C) 2016-2020 Tavian Barnes * * * * Permission to use, copy, modify, and/or distribute this software for any * * purpose with or without fee is hereby granted. * @@ -106,6 +106,18 @@ int dstrcat(char **dest, const char *src); */ int dstrncat(char **dest, const char *src, size_t n); +/** + * Append a dynamic string to another dynamic string. + * + * @param dest + * The destination dynamic string. + * @param src + * The dynamic string to append. + * @return + * 0 on success, -1 on failure. + */ +int dstrdcat(char **dest, const char *src); + /** * Append a single character to a dynamic string. * @@ -142,6 +154,35 @@ char *dstrprintf(const char *format, ...); */ char *dstrvprintf(const char *format, va_list args); +/** + * Format some text onto the end of a dynamic string. + * + * @param str + * The destination dynamic string. + * @param format + * The format string to fill in. + * @param ... + * Any arguments for the format string. + * @return + * 0 on success, -1 on failure. + */ +BFS_FORMATTER(2, 3) +int dstrcatf(char **str, const char *format, ...); + +/** + * Format some text from a va_list onto the end of a dynamic string. + * + * @param str + * The destination dynamic string. + * @param format + * The format string to fill in. + * @param args + * The arguments for the format string. + * @return + * 0 on success, -1 on failure. + */ +int dstrvcatf(char **str, const char *format, va_list args); + /** * Free a dynamic string. * -- cgit v1.2.3