#include #include #include #include #include #include #include "cdecl.h" #include "error.h" #include "i18n.h" 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[]; }; /* This error is reserved for extremely dire out-of-memory conditions. */ static struct err_state err_no_mem = { .err = { .code = CDECL_ENOMEM, .str = NULL, }, }; const char *cdecl__strerror(unsigned code) { # include "errtab.h" assert(code < sizeof offtab / sizeof offtab[0]); assert(offtab[code] != 0); return gettext((char *)&strtab + offtab[code]); } static void free_err(void *err) { if (err == &err_no_mem) return; free(err); } static void set_error(const struct cdecl_error *err) { struct err_state *state; size_t need_len = 0; 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 = cdecl__strerror(state->err.code); } } static void initialize(void) { cdecl__init_i18n(); err_no_mem.err.str = cdecl__strerror(CDECL_ENOMEM); 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. */ set_error(&(const struct cdecl_error){ .code = CDECL_ENOPARSE }); } const struct cdecl_error *cdecl_get_error(void) { struct err_state *state; 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); }