aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavier.delcampo@orain.io>2020-10-14 08:27:19 +0200
committerXavier Del Campo Romero <xavier.delcampo@orain.io>2020-10-14 08:27:19 +0200
commit601a8133257c095032a3c43bd4319e2c0f105756 (patch)
treebe995e0b64b376c696c443ea8c5367f2e7925ec7
parent837c9427de10ddcb5295f3840ead2a6c3aa61e69 (diff)
downloaddynstr-601a8133257c095032a3c43bd4319e2c0f105756.tar.gz
Fix lifetime issues with va_list
The standard requires both va_start and va_end to be called from the same function. On the other hand, vsnprintf and vsprintf leave the va_list on an undefined state according to the standard, so a copy must be created before a second call to these functions.
-rw-r--r--dynstr.c87
1 files changed, 53 insertions, 34 deletions
diff --git a/dynstr.c b/dynstr.c
index 92930f2..e48e5c3 100644
--- a/dynstr.c
+++ b/dynstr.c
@@ -33,73 +33,92 @@ void dynstr_init(struct dynstr *const d)
enum dynstr_err dynstr_vappend(struct dynstr *const d, const char *const format, va_list ap)
{
enum dynstr_err err = DYNSTR_OK;
- const size_t src_len = vsnprintf(NULL, 0, format, ap);
- const size_t new_len = d->len + src_len + 1;
+ va_list apc;
- d->str = realloc(d->str, new_len * sizeof *d->str);
+ va_copy(apc, ap);
- if (d->str)
- {
- vsprintf(d->str + d->len, format, ap);
- d->len += src_len;
- }
- else
{
- err = DYNSTR_ERR_ALLOC;
+ const size_t src_len = vsnprintf(NULL, 0, format, ap);
+ const size_t new_len = d->len + src_len + 1;
+
+ d->str = realloc(d->str, new_len * sizeof *d->str);
+
+ if (d->str)
+ {
+ vsprintf(d->str + d->len, format, apc);
+ d->len += src_len;
+ }
+ else
+ {
+ err = DYNSTR_ERR_ALLOC;
+ }
}
- va_end(ap);
+ va_end(apc);
return err;
}
enum dynstr_err dynstr_append(struct dynstr *const d, const char *const format, ...)
{
va_list ap;
+ enum dynstr_err err;
va_start(ap, format);
- return dynstr_vappend(d, format, ap);
+ err = dynstr_vappend(d, format, ap);
+ va_end(ap);
+ return err;
}
enum dynstr_err dynstr_vprepend(struct dynstr *const d, const char *const format, va_list ap)
{
- const size_t src_len = vsnprintf(NULL, 0, format, ap);
- const size_t new_len = d->len + src_len + 1;
+ enum dynstr_err err = DYNSTR_OK;
+ va_list apc;
- d->str = realloc(d->str, new_len * sizeof *d->str);
+ va_copy(apc, ap);
- if (d->str && d->len)
{
- /* Keep byte that will be removed by later call to vsprintf. */
- const char c = *d->str;
+ const size_t src_len = vsnprintf(NULL, 0, format, ap);
+ const size_t new_len = d->len + src_len + 1;
+
+ d->str = realloc(d->str, new_len * sizeof *d->str);
+
+ if (d->str && d->len)
+ {
+ /* Keep byte that will be removed by later call to vsprintf. */
+ const char c = *d->str;
- for (size_t i = new_len - 1, j = d->len; j <= d->len; i--, j--)
+ for (size_t i = new_len - 1, j = d->len; j <= d->len; i--, j--)
+ {
+ d->str[i] = d->str[j];
+ }
+
+ vsprintf(d->str, format, apc);
+ d->str[src_len] = c;
+ }
+ else if (!d->len)
{
- d->str[i] = d->str[j];
+ vsprintf(d->str + d->len, format, apc);
+ }
+ else
+ {
+ err = DYNSTR_ERR_ALLOC;
}
- vsprintf(d->str, format, ap);
- d->str[src_len] = c;
- }
- else if (!d->len)
- {
- vsprintf(d->str + d->len, format, ap);
- }
- else
- {
- return DYNSTR_ERR_ALLOC;
+ d->len += src_len;
}
- va_end(ap);
- d->len += src_len;
- return DYNSTR_OK;
+ va_end(apc);
+ return err;
}
enum dynstr_err dynstr_prepend(struct dynstr *const d, const char *const format, ...)
{
va_list ap;
+ enum dynstr_err err;
va_start(ap, format);
- return dynstr_vprepend(d, format, ap);
+ err = dynstr_vprepend(d, format, ap);
+ return err;
}
enum dynstr_err dynstr_dup(struct dynstr *const dst, const struct dynstr *const src)