dist_man_MANS = doc/man/cdecl99.1 doc/man/libcdecl.3
include_HEADERS = src/cdecl.h
-noinst_HEADERS = src/typemap.h src/output.h src/scan.h src/parse.h src/i18n.h
+noinst_HEADERS = src/typemap.h src/output.h src/scan.h src/parse.h src/i18n.h \
+ src/error.h
noinst_DATA = $(MOFILES)
libcdecl_la_LDFLAGS = -no-undefined \
-export-symbols-regex '^cdecl_[[:lower:]]'
libcdecl_la_SOURCES = src/scan.c src/parse.c src/parse-decl.c src/typemap.c \
- src/output.c src/explain.c src/declare.c src/i18n.c
+ src/output.c src/explain.c src/declare.c src/i18n.c src/error.c
libcdecl_la_LIBADD = libgnu.la $(LTLIBINTL) $(LTLIBTHREAD)
$(libcdecl_la_OBJECTS): $(gnulib_headers)
.Fd size_t cdecl_explain(char *buf, size_t n, struct cdecl *decl);
.Fd size_t cdecl_declare(char *buf, size_t n, struct cdecl *decl);
.Pp
+.Fd const struct cdecl_error *cdecl_get_error(void);
+.Pp
.Fd int cdecl_spec_kind(struct cdecl_declspec *spec);
.Sh DESCRIPTION
.Nm
otherwise). If
.Va variadic
is true, then the function is variadic.
+.Sh ERROR HANDLING
+Some functions in
+.Nm
+can fail. Such functions will be documented as indicating an error condition
+in a particular way. It is sometimes necessary to know more about a particular
+error in order to print an informative error message or perform some other
+action. To facilitate this,
+.Nm
+provides a structure which describes a particular error.
+.Bd -literal -offset indent
+struct cdecl_error {
+ unsigned code;
+ const char *str;
+};
+.Ed
+.Pp
+The
+.Va code
+member identifies the sort of error which has occurred, while the
+.Va str
+member points to a string containing a human-readable description of the error.
+This error information can be retrieved by calling the function
+.Pp
+.Fd const struct cdecl_error *cdecl_get_error(void);
+.Pp
+which returns a pointer to the error structure most recently generated in the
+current thread. It is therefore thread-safe in that errors occurring in
+another thread will not interfere with the current one. The returned pointer
+shall remain valid until the next call to any function from
+.Nm
+by the same thread, except that multiple consecutive calls to
+.Va cdecl_get_error
+shall all return the same value. The same applies to the
+.Va str
+pointer inside the error structure itself.
+.Pp
+If this function is called before an error has been indicated by an earlier
+call in the same thread, the behaviour is undefined.
.Sh PARSING DECLARATIONS
To parse a declaration, the function
.Pp
stdlib_h.m4
thread.m4
threadlib.m4
+tls.m4
uintmax_t.m4
unistd_h.m4
visibility.m4
# Specification in the form of a command-line invocation:
-# gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=. --makefile-name=gnulib.mk.in --conditional-dependencies --libtool --macro-prefix=gl --no-vc-files getopt-gnu gettext lock readline
+# gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=. --makefile-name=gnulib.mk.in --conditional-dependencies --libtool --macro-prefix=gl --no-vc-files getopt-gnu gettext lock readline tls
# Specification in the form of a few gnulib-tool.m4 macro invocations:
gl_LOCAL_DIR([])
gettext
lock
readline
+ tls
])
gl_AVOID([])
gl_SOURCE_BASE([lib])
return spec->type & ~0xffu;
}
+/* Error handling. */
+enum {
+ CDECL_ENOMEM,
+ CDECL_ENOPARSE,
+};
+
+struct cdecl_error {
+ unsigned code;
+ const char *str;
+};
+
+const struct cdecl_error *cdecl_get_error(void);
+
#endif
static int cmd_explain(const char *cmd, const char *arg)
{
+ const struct cdecl_error *err;
struct cdecl *decl;
const char *str;
int ret = -1;
decl = cdecl_parse_decl(arg);
- if (!decl)
+ if (!decl) {
+ err = cdecl_get_error();
+ fprintf(stderr, "%s\n", err->str);
goto out;
+ }
for (struct cdecl *i = decl; i; i = i->next) {
str = do_format(cdecl_explain, i);
static int cmd_simplify(const char *cmd, const char *arg)
{
+ const struct cdecl_error *err;
struct cdecl *decl;
const char *str;
int ret = -1;
decl = cdecl_parse_decl(arg);
- if (!decl)
+ if (!decl) {
+ err = cdecl_get_error();
+ fprintf(stderr, "%s\n", err->str);
goto out;
+ }
for (struct cdecl *i = decl; i; i = i->next) {
struct cdecl_declspec *s = i->specifiers;
static int cmd_declare(const char *cmd, const char *arg)
{
+ const struct cdecl_error *err;
struct cdecl *decl;
const char *str;
int ret = -1;
/* The name of the command is significant here. */
decl = cdecl_parse_english(cmd);
- if (!decl)
+ if (!decl) {
+ err = cdecl_get_error();
+ fprintf(stderr, "%s\n", err->str);
goto out;
+ }
/*
* English parses have at most one full declarator, so no loop is
--- /dev/null
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <glthread/lock.h>
+#include <glthread/tls.h>
+#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 = "failed to allocate memory",
+ },
+};
+
+static void free_err(void *err)
+{
+ if (err == &err_no_mem)
+ return;
+
+ free(err);
+}
+
+static void initialize(void)
+{
+ cdecl__init_i18n();
+ err_no_mem.err.str = gettext(err_no_mem.err.str);
+
+ 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)
+{
+ 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)
+{
+ 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";
+ }
+}
--- /dev/null
+#ifndef CDECL_ERROR_H_
+#define CDECL_ERROR_H_
+
+struct cdecl_error;
+void cdecl__set_error(const struct cdecl_error *err);
+
+#endif
#include <stdbool.h>
#include "scan.h"
+#include "error.h"
#include "cdecl.h"
#define FAIL(msg) do { \
if (strstr(err, "T_LEX_ERROR"))
return;
- fprintf(stderr, "%s\n", err);
+ cdecl__set_error(&(const struct cdecl_error){
+ .code = CDECL_ENOPARSE,
+ .str = err,
+ });
}