]> git.draconx.ca Git - cdecl99.git/commitdiff
Start implementing proper error handling.
authorNick Bowler <nbowler@draconx.ca>
Thu, 29 Sep 2011 01:57:23 +0000 (21:57 -0400)
committerNick Bowler <nbowler@draconx.ca>
Sun, 23 Oct 2011 00:20:58 +0000 (20:20 -0400)
Since the library shouldn't be printing its own error messages, we need
a mechanism to propagate error details to the caller.  Implement a
system using thread-local storage which tracks the most recent errors.

Makefile.am
doc/man/libcdecl.3
m4/.gitignore
m4/gnulib-cache.m4
src/cdecl.h
src/cdecl99.c
src/error.c [new file with mode: 0644]
src/error.h [new file with mode: 0644]
src/parse.y

index de6c066c1064f4f82418a29721f9517854f1b4b8..f387ee348d3cbbe204294f2d1688cfa130dbdde2 100644 (file)
@@ -29,7 +29,8 @@ EXTRA_DIST = m4/gnulib-cache.m4 src/types.lst src/typenames.sed \
 dist_man_MANS = doc/man/cdecl99.1 doc/man/libcdecl.3
 
 include_HEADERS = src/cdecl.h
 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)
 
 
 noinst_DATA = $(MOFILES)
 
@@ -37,7 +38,7 @@ lib_LTLIBRARIES = libcdecl.la
 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 \
 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)
 
 libcdecl_la_LIBADD = libgnu.la $(LTLIBINTL) $(LTLIBTHREAD)
 $(libcdecl_la_OBJECTS): $(gnulib_headers)
 
index d92dcbfe478daf49790d5c5d6282e79aeccb05b1..489d7a9ef7eb8fce9a409c70d1aab18735e2efdd 100644 (file)
@@ -14,6 +14,8 @@
 .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 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
 .Fd int cdecl_spec_kind(struct cdecl_declspec *spec);
 .Sh DESCRIPTION
 .Nm
@@ -244,6 +246,44 @@ each function parameter has exactly one full declarator (abstract or
 otherwise).   If
 .Va variadic
 is true, then the function is variadic.
 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
 .Sh PARSING DECLARATIONS
 To parse a declaration, the function
 .Pp
index 14287ab6a99a215c80d82da74ad56d0d4c781eec..f93decd99af3df9b59a3db1f0ce750f729292596 100644 (file)
@@ -49,6 +49,7 @@ stdio_h.m4
 stdlib_h.m4
 thread.m4
 threadlib.m4
 stdlib_h.m4
 thread.m4
 threadlib.m4
+tls.m4
 uintmax_t.m4
 unistd_h.m4
 visibility.m4
 uintmax_t.m4
 unistd_h.m4
 visibility.m4
index 09fc0cdee865e5cbfabd209b4662dd5042b1df4d..3ecf1efdbca6d441df14a0c27fef8984d2a1ab9d 100644 (file)
@@ -15,7 +15,7 @@
 
 
 # Specification in the form of a command-line invocation:
 
 
 # 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([])
 
 # Specification in the form of a few gnulib-tool.m4 macro invocations:
 gl_LOCAL_DIR([])
@@ -24,6 +24,7 @@ gl_MODULES([
   gettext
   lock
   readline
   gettext
   lock
   readline
+  tls
 ])
 gl_AVOID([])
 gl_SOURCE_BASE([lib])
 ])
 gl_AVOID([])
 gl_SOURCE_BASE([lib])
index c4dad87a38106c6c107fa5ffc7515b5411cd878b..a8e994687ccc6dd02c84dcc783dac465364f1fbd 100644 (file)
@@ -107,4 +107,17 @@ static inline int cdecl_spec_kind(struct cdecl_declspec *spec)
        return spec->type & ~0xffu;
 }
 
        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
 #endif
index 04de1a6640e92225676f3e56872a63e26945a70d..c5f913bd12d1fd0a442bdc0f2aa82def63d3a5cf 100644 (file)
@@ -97,13 +97,17 @@ retry:
 
 static int cmd_explain(const char *cmd, const char *arg)
 {
 
 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);
        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;
                goto out;
+       }
 
        for (struct cdecl *i = decl; i; i = i->next) {
                str = do_format(cdecl_explain, i);
 
        for (struct cdecl *i = decl; i; i = i->next) {
                str = do_format(cdecl_explain, i);
@@ -121,13 +125,17 @@ out:
 
 static int cmd_simplify(const char *cmd, const char *arg)
 {
 
 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);
        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;
                goto out;
+       }
 
        for (struct cdecl *i = decl; i; i = i->next) {
                struct cdecl_declspec *s = i->specifiers;
 
        for (struct cdecl *i = decl; i; i = i->next) {
                struct cdecl_declspec *s = i->specifiers;
@@ -156,14 +164,18 @@ out:
 
 static int cmd_declare(const char *cmd, const char *arg)
 {
 
 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);
        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;
                goto out;
+       }
 
        /*
         * English parses have at most one full declarator, so no loop is
 
        /*
         * English parses have at most one full declarator, so no loop is
diff --git a/src/error.c b/src/error.c
new file mode 100644 (file)
index 0000000..e18ec80
--- /dev/null
@@ -0,0 +1,103 @@
+#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";
+       }
+}
diff --git a/src/error.h b/src/error.h
new file mode 100644 (file)
index 0000000..2404ab6
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef CDECL_ERROR_H_
+#define CDECL_ERROR_H_
+
+struct cdecl_error;
+void cdecl__set_error(const struct cdecl_error *err);
+
+#endif
index f90013a44c458989e890dde1c04a323b5176676f..5bcd9a947c6e587e44fc4d403ab319798b3be41d 100644 (file)
@@ -32,6 +32,7 @@
 #include <stdbool.h>
 
 #include "scan.h"
 #include <stdbool.h>
 
 #include "scan.h"
+#include "error.h"
 #include "cdecl.h"
 
 #define FAIL(msg) do { \
 #include "cdecl.h"
 
 #define FAIL(msg) do { \
@@ -584,5 +585,8 @@ yyerror(YYLTYPE *loc, yyscan_t scanner, struct cdecl **out,
        if (strstr(err, "T_LEX_ERROR"))
                return;
 
        if (strstr(err, "T_LEX_ERROR"))
                return;
 
-       fprintf(stderr, "%s\n", err);
+       cdecl__set_error(&(const struct cdecl_error){
+               .code = CDECL_ENOPARSE,
+               .str  = err,
+       });
 }
 }