summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dstring.c77
-rw-r--r--dstring.h43
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 <tavianator@tavianator.com> *
+ * Copyright (C) 2016-2020 Tavian Barnes <tavianator@tavianator.com> *
* *
* 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 <tavianator@tavianator.com> *
+ * Copyright (C) 2016-2020 Tavian Barnes <tavianator@tavianator.com> *
* *
* Permission to use, copy, modify, and/or distribute this software for any *
* purpose with or without fee is hereby granted. *
@@ -107,6 +107,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.
*
* @param str
@@ -143,6 +155,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.
*
* @param dstr