*/
#include <config.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
+#include <stdarg.h>
#include <glthread/lock.h>
#include <glthread/tls.h>
},
};
-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)
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)
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;
}