X-Git-Url: https://git.draconx.ca/gitweb/cdecl99.git/blobdiff_plain/4ac33123a76ec76911d599f212e1b47412730614..e92ef179a31b2357ec1dcec634767d7d1eed97e3:/src/error.c diff --git a/src/error.c b/src/error.c index 3d0a38e..fe0695c 100644 --- a/src/error.c +++ b/src/error.c @@ -17,22 +17,25 @@ */ #include +#include #include #include #include -#include -#include +#include #include "cdecl.h" #include "cdecl-internal.h" +#include +#include + gl_once_define(static, tls_initialized); static gl_tls_key_t tls_key; struct err_state { struct cdecl_error err; size_t nstr; - char str[]; + char str[FLEXIBLE_ARRAY_MEMBER]; }; /* This error is reserved for extremely dire out-of-memory conditions. */ @@ -43,18 +46,6 @@ static struct err_state err_no_mem = { }, }; -const char *cdecl__strerror(unsigned code) -{ -#include "errtab.h" - - switch (code) { - case CDECL_ENOMEM: return gettext(strtab+err_enomem); - case CDECL_ENOPARSE: return gettext(strtab+err_enoparse); - } - - assert(0); -} - static void free_err(void *err) { if (err == &err_no_mem) @@ -63,59 +54,75 @@ static void free_err(void *err) free(err); } -static void set_error(const struct cdecl_error *err) +static void initialize(void) +{ + cdecl__init_i18n(); + err_no_mem.err.str = _("failed to allocate memory"); + + gl_tls_key_init(tls_key, free_err); +} + +/* + * cdecl__err(CDECL_ENOMEM); + * cdecl__err(code, fmt, ...); + * + * Sets the library error to code, with a printf-style error string. + */ +void cdecl__err(unsigned code, ...) { + const char *fmt; struct err_state *state; - size_t need_len = 0; + int rc, try = 0; + va_list ap; - if (err->str) { - need_len = strlen(err->str) + 1; - } + gl_once(tls_initialized, initialize); state = gl_tls_get(tls_key); - if (state == &err_no_mem) - state = NULL; - if (!state || state->nstr < need_len) { - struct err_state *tmp; - - tmp = realloc(state, sizeof *state + need_len); + if (!state || state == &err_no_mem) { + void *tmp = malloc(sizeof *state + 100); + if (!tmp) { - /* Re-use the existing state buffer, if any. */ - if (state) - state->err = err_no_mem.err; - else - state = &err_no_mem; - - gl_tls_set(tls_key, state); + state = &err_no_mem; return; } - state = tmp; - state->nstr = need_len; - gl_tls_set(tls_key, state); + gl_tls_set(tls_key, (state = tmp)); + state->nstr = 100; } - state->err = *err; - if (err->str) { - memcpy(state->str, err->str, need_len); - state->err.str = state->str; - } else { - state->err.str = cdecl__strerror(state->err.code); + if (code == CDECL_ENOMEM) { + if (state != &err_no_mem) + state->err = err_no_mem.err; + return; } -} - -static void initialize(void) -{ - cdecl__init_i18n(); - err_no_mem.err.str = cdecl__strerror(CDECL_ENOMEM); +retry: + va_start(ap, code); + fmt = va_arg(ap, const char *); + rc = vsnprintf(state->str, state->nstr, fmt, ap); + va_end(ap); + + if (rc > 0 && rc >= state->nstr) { + void *tmp; + size_t n; + + assert(try == 0 && rc < SIZE_MAX / 4); + + n = ((size_t)rc + 1) * 2; + tmp = realloc(state, sizeof *state + n); + if (tmp) { + state = tmp; + state->nstr = n; + try++; + + goto retry; + } - gl_tls_key_init(tls_key, free_err); + state->err = err_no_mem.err; + return; + } - /* - * This default error message is a stop-gap measure until all library - * error conditions use the new interface. - */ - set_error(&(const struct cdecl_error){ .code = CDECL_ENOPARSE }); + state->err.str = state->str; + state->err.code = code; } const struct cdecl_error *cdecl_get_error(void) @@ -125,14 +132,5 @@ const struct cdecl_error *cdecl_get_error(void) gl_once(tls_initialized, initialize); state = gl_tls_get(tls_key); - assert(state); - - return &state->err; -} - -void cdecl__set_error(const struct cdecl_error *err) -{ - gl_once(tls_initialized, initialize); - - set_error(err); + return state ? &state->err : NULL; }