Compare commits

...

6 Commits

Author SHA1 Message Date
Xavier Del Campo Romero 601a813325 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.
2020-10-14 08:27:19 +02:00
Xavier Del Campo Romero 837c9427de Add missing do/while (0) on convenience macros 2020-10-08 11:24:31 +02:00
Xavier Del Campo Romero 083eca5323 Move convenience macros down for readability 2020-10-08 11:24:31 +02:00
Xavier Del Campo Romero 3e9016f23f Add convenience macros for dynstr_prepend 2020-10-08 11:24:31 +02:00
Xavier Del Campo Romero c862d84d0b Add dynstr_vprepend 2020-10-08 11:24:24 +02:00
Xavier Del Campo Romero 8bd1123857 Add dynstr_vappend 2020-10-06 09:20:57 +02:00
2 changed files with 134 additions and 55 deletions

View File

@ -30,45 +30,55 @@ void dynstr_init(struct dynstr *const d)
memset(d, 0, sizeof *d);
}
enum dynstr_err dynstr_append(struct dynstr *const d, const char *const format, ...)
enum dynstr_err dynstr_vappend(struct dynstr *const d, const char *const format, va_list ap)
{
va_list ap;
enum dynstr_err err = DYNSTR_OK;
va_list apc;
va_start(ap, format);
va_copy(apc, ap);
{
const size_t src_len = vsnprintf(NULL, 0, format, ap);
const size_t new_len = d->len + src_len + 1;
va_end(ap);
d->str = realloc(d->str, new_len * sizeof *d->str);
if (d->str)
{
va_start(ap, format);
vsprintf(d->str + d->len, format, ap);
va_end(ap);
vsprintf(d->str + d->len, format, apc);
d->len += src_len;
}
else
{
return DYNSTR_ERR_ALLOC;
err = DYNSTR_ERR_ALLOC;
}
}
return DYNSTR_OK;
va_end(apc);
return err;
}
enum dynstr_err dynstr_prepend(struct dynstr *const d, const char *const format, ...)
enum dynstr_err dynstr_append(struct dynstr *const d, const char *const format, ...)
{
va_list ap;
enum dynstr_err err;
va_start(ap, format);
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 err = DYNSTR_OK;
va_list apc;
va_copy(apc, ap);
{
const size_t src_len = vsnprintf(NULL, 0, format, ap);
const size_t new_len = d->len + src_len + 1;
va_end(ap);
d->str = realloc(d->str, new_len * sizeof *d->str);
@ -82,26 +92,33 @@ enum dynstr_err dynstr_prepend(struct dynstr *const d, const char *const format,
d->str[i] = d->str[j];
}
va_start(ap, format);
vsprintf(d->str, format, ap);
va_end(ap);
vsprintf(d->str, format, apc);
d->str[src_len] = c;
}
else if (!d->len)
{
va_start(ap, format);
vsprintf(d->str + d->len, format, ap);
va_end(ap);
vsprintf(d->str + d->len, format, apc);
}
else
{
return DYNSTR_ERR_ALLOC;
err = DYNSTR_ERR_ALLOC;
}
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);
err = dynstr_vprepend(d, format, ap);
return err;
}
enum dynstr_err dynstr_dup(struct dynstr *const dst, const struct dynstr *const src)

View File

@ -21,45 +21,10 @@
#error C99 support is mandatory for dynstr
#endif /* __STDC_VERSION < 199901L */
#include <stdarg.h>
#include <stddef.h>
#include <stdbool.h>
/* Since dynstr_append() might fail (as it is based on dynamic memory
* allocation), these macros can be used to avoid boilerplate code. */
/**
* Convenience macro that calls dynstr_append and returns NULL if failed.
*/
#define dynstr_append_or_ret_null(...) \
do {if (dynstr_append(__VA_ARGS__) != DYNSTR_OK) return NULL;} while (0)
/**
* Convenience macro that calls dynstr_append and returns false if failed.
*/
#define dynstr_append_or_ret_false(...) \
do {if (dynstr_append(__VA_ARGS__) != DYNSTR_OK) return false;} while (0)
/**
* Convenience macro that calls dynstr_append and returns its error code if failed.
*/
#define dynstr_append_or_ret(...) \
{ \
const enum dynstr_err err = dynstr_append(__VA_ARGS__); \
if (err != DYNSTR_OK) return err; \
}
/**
* Convenience macro that calls dynstr_append and returns zero if failed.
*/
#define dynstr_append_or_ret_zero(...) \
do {if (dynstr_append(__VA_ARGS__) != DYNSTR_OK) return 0;} while (0)
/**
* Convenience macro that calls dynstr_append and returns one if failed.
*/
#define dynstr_append_or_ret_nonzero(...) \
do {if (dynstr_append(__VA_ARGS__) != DYNSTR_OK) return 1;} while (0)
/**
* Dynamic string type used for this library.
* @note If needed, members can be safely read but should not be modified
@ -108,6 +73,19 @@ void dynstr_init(struct dynstr *d);
*/
enum dynstr_err dynstr_append(struct dynstr *d, const char *format, ...);
/**
* This function takes a string literal in printf format and a variable
* argument list, calculates its size and concatenates it into the
* dynamic string.
* @param d Dynamic string where new string will be appended.
* @param format String literal in printf format.
* @param ap Variable argument list.
* @return Returns one of the following error codes:
* # DYNSTR_OK if successful.
* # DYNSTR_ERR_ALLOC if no more memory is available.
*/
enum dynstr_err dynstr_vappend(struct dynstr *d, const char *format, va_list ap);
/**
* This function takes a string literal in printf format and a variable
* number of arguments, calculates its size and prepends it into the
@ -120,6 +98,19 @@ enum dynstr_err dynstr_append(struct dynstr *d, const char *format, ...);
*/
enum dynstr_err dynstr_prepend(struct dynstr *d, const char *format, ...);
/**
* This function takes a string literal in printf format and a variable
* argument list, calculates its size and prepends it into the beginning of the
* dynamic string.
* @param d Dynamic string where new string will be prepended.
* @param format String literal in printf format.
* @param ap Variable argument list.
* @return Returns one of the following error codes:
* # DYNSTR_OK if successful.
* # DYNSTR_ERR_ALLOC if no more memory is available.
*/
enum dynstr_err dynstr_vprepend(struct dynstr *d, const char *format, va_list ap);
/**
* This function duplicates a dynamic string to another instance.
* @attention Destination instance must be initialized before calling
@ -153,4 +144,75 @@ enum dynstr_err dynstr_dup(struct dynstr *dst, const struct dynstr *src);
*/
void dynstr_free(struct dynstr *d);
/* Since dynstr_append() might fail (as it is based on dynamic memory
* allocation), these macros can be used to avoid boilerplate code. */
/**
* Convenience macro that calls dynstr_append and returns NULL if failed.
*/
#define dynstr_append_or_ret_null(...) \
do {if (dynstr_append(__VA_ARGS__) != DYNSTR_OK) return NULL;} while (0)
/**
* Convenience macro that calls dynstr_append and returns false if failed.
*/
#define dynstr_append_or_ret_false(...) \
do {if (dynstr_append(__VA_ARGS__) != DYNSTR_OK) return false;} while (0)
/**
* Convenience macro that calls dynstr_append and returns its error code if failed.
*/
#define dynstr_append_or_ret(...) \
do \
{ \
const enum dynstr_err err = dynstr_append(__VA_ARGS__); \
if (err != DYNSTR_OK) return err; \
} while (0)
/**
* Convenience macro that calls dynstr_append and returns zero if failed.
*/
#define dynstr_append_or_ret_zero(...) \
do {if (dynstr_append(__VA_ARGS__) != DYNSTR_OK) return 0;} while (0)
/**
* Convenience macro that calls dynstr_append and returns one if failed.
*/
#define dynstr_append_or_ret_nonzero(...) \
do {if (dynstr_append(__VA_ARGS__) != DYNSTR_OK) return 1;} while (0)
/**
* Convenience macro that calls dynstr_prepend and returns NULL if failed.
*/
#define dynstr_prepend_or_ret_null(...) \
do {if (dynstr_prepend(__VA_ARGS__) != DYNSTR_OK) return NULL;} while (0)
/**
* Convenience macro that calls dynstr_prepend and returns false if failed.
*/
#define dynstr_prepend_or_ret_false(...) \
do {if (dynstr_prepend(__VA_ARGS__) != DYNSTR_OK) return false;} while (0)
/**
* Convenience macro that calls dynstr_prepend and returns its error code if failed.
*/
#define dynstr_prepend_or_ret(...) \
do \
{ \
const enum dynstr_err err = dynstr_prepend(__VA_ARGS__); \
if (err != DYNSTR_OK) return err; \
} while (0)
/**
* Convenience macro that calls dynstr_prepend and returns zero if failed.
*/
#define dynstr_prepend_or_ret_zero(...) \
do {if (dynstr_prepend(__VA_ARGS__) != DYNSTR_OK) return 0;} while (0)
/**
* Convenience macro that calls dynstr_prepend and returns one if failed.
*/
#define dynstr_prepend_or_ret_nonzero(...) \
do {if (dynstr_prepend(__VA_ARGS__) != DYNSTR_OK) return 1;} while (0)
#endif /* DYNSTR_H */