X-Git-Url: https://git.draconx.ca/gitweb/cdecl99.git/blobdiff_plain/3df85155e2dbf9307dd64bd222bf74389c45a75c..e92ef179a31b2357ec1dcec634767d7d1eed97e3:/src/error.c diff --git a/src/error.c b/src/error.c index e18ec80..fe0695c 100644 --- a/src/error.c +++ b/src/error.c @@ -1,12 +1,33 @@ +/* + * Error handling for libcdecl. + * Copyright © 2011-2012, 2021 Nick Bowler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include +#include #include #include #include +#include + +#include "cdecl.h" +#include "cdecl-internal.h" + #include #include -#include "cdecl.h" -#include "error.h" -#include "i18n.h" gl_once_define(static, tls_initialized); static gl_tls_key_t tls_key; @@ -14,14 +35,14 @@ 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. */ static struct err_state err_no_mem = { .err = { .code = CDECL_ENOMEM, - .str = "failed to allocate memory", + .str = NULL, }, }; @@ -36,68 +57,80 @@ static void free_err(void *err) static void initialize(void) { cdecl__init_i18n(); - err_no_mem.err.str = gettext(err_no_mem.err.str); + err_no_mem.err.str = _("failed to allocate memory"); gl_tls_key_init(tls_key, free_err); - - /* - * This default error message is a stop-gap measure until all library - * error conditions use the new interface. - */ - cdecl__set_error(&(const struct cdecl_error){ .code = CDECL_ENOPARSE }); } -const struct cdecl_error *cdecl_get_error(void) +/* + * 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; + int rc, try = 0; + va_list ap; gl_once(tls_initialized, initialize); state = gl_tls_get(tls_key); - assert(state); + if (!state || state == &err_no_mem) { + void *tmp = malloc(sizeof *state + 100); + + if (!tmp) { + state = &err_no_mem; + return; + } + + gl_tls_set(tls_key, (state = tmp)); + state->nstr = 100; + } + + if (code == CDECL_ENOMEM) { + if (state != &err_no_mem) + state->err = err_no_mem.err; + return; + } +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; + } + + state->err = err_no_mem.err; + return; + } - return &state->err; + state->err.str = state->str; + state->err.code = code; } -void cdecl__set_error(const struct cdecl_error *err) +const struct cdecl_error *cdecl_get_error(void) { struct err_state *state; - size_t need_len = 0; gl_once(tls_initialized, initialize); - if (err->str) { - need_len = strlen(err->str) + 1; - } - 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 (!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); - return; - } - - state = tmp; - state->nstr = need_len; - gl_tls_set(tls_key, state); - } - - state->err = *err; - if (err->str) { - memcpy(state->str, err->str, need_len); - state->err.str = state->str; - } else { - state->err.str = "unknown error"; - } + return state ? &state->err : NULL; }