/* * 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" 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[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 = NULL, }, }; 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) 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); }