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.
This commit is contained in:
parent
837c9427de
commit
601a813325
87
dynstr.c
87
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 dynstr_vappend(struct dynstr *const d, const char *const format, va_list ap)
|
||||||
{
|
{
|
||||||
enum dynstr_err err = DYNSTR_OK;
|
enum dynstr_err err = DYNSTR_OK;
|
||||||
const size_t src_len = vsnprintf(NULL, 0, format, ap);
|
va_list apc;
|
||||||
const size_t new_len = d->len + src_len + 1;
|
|
||||||
|
|
||||||
d->str = realloc(d->str, new_len * sizeof *d->str);
|
va_copy(apc, ap);
|
||||||
|
|
||||||
if (d->str)
|
|
||||||
{
|
{
|
||||||
vsprintf(d->str + d->len, format, ap);
|
const size_t src_len = vsnprintf(NULL, 0, format, ap);
|
||||||
d->len += src_len;
|
const size_t new_len = d->len + src_len + 1;
|
||||||
}
|
|
||||||
else
|
d->str = realloc(d->str, new_len * sizeof *d->str);
|
||||||
{
|
|
||||||
err = DYNSTR_ERR_ALLOC;
|
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;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum dynstr_err dynstr_append(struct dynstr *const d, const char *const format, ...)
|
enum dynstr_err dynstr_append(struct dynstr *const d, const char *const format, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
enum dynstr_err err;
|
||||||
|
|
||||||
va_start(ap, format);
|
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)
|
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);
|
enum dynstr_err err = DYNSTR_OK;
|
||||||
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 && d->len)
|
|
||||||
{
|
{
|
||||||
/* Keep byte that will be removed by later call to vsprintf. */
|
const size_t src_len = vsnprintf(NULL, 0, format, ap);
|
||||||
const char c = *d->str;
|
const size_t new_len = d->len + src_len + 1;
|
||||||
|
|
||||||
for (size_t i = new_len - 1, j = d->len; j <= d->len; i--, j--)
|
d->str = realloc(d->str, new_len * sizeof *d->str);
|
||||||
|
|
||||||
|
if (d->str && d->len)
|
||||||
{
|
{
|
||||||
d->str[i] = d->str[j];
|
/* 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--)
|
||||||
|
{
|
||||||
|
d->str[i] = d->str[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
vsprintf(d->str, format, apc);
|
||||||
|
d->str[src_len] = c;
|
||||||
|
}
|
||||||
|
else if (!d->len)
|
||||||
|
{
|
||||||
|
vsprintf(d->str + d->len, format, apc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
err = DYNSTR_ERR_ALLOC;
|
||||||
}
|
}
|
||||||
|
|
||||||
vsprintf(d->str, format, ap);
|
d->len += src_len;
|
||||||
d->str[src_len] = c;
|
|
||||||
}
|
|
||||||
else if (!d->len)
|
|
||||||
{
|
|
||||||
vsprintf(d->str + d->len, format, ap);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return DYNSTR_ERR_ALLOC;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
va_end(ap);
|
va_end(apc);
|
||||||
d->len += src_len;
|
return err;
|
||||||
return DYNSTR_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum dynstr_err dynstr_prepend(struct dynstr *const d, const char *const format, ...)
|
enum dynstr_err dynstr_prepend(struct dynstr *const d, const char *const format, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
enum dynstr_err err;
|
||||||
|
|
||||||
va_start(ap, format);
|
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)
|
enum dynstr_err dynstr_dup(struct dynstr *const dst, const struct dynstr *const src)
|
||||||
|
|
Loading…
Reference in New Issue