lib_LTLIBRARIES = libcdecl.la
libcdecl_la_LDFLAGS = -export-symbols-regex '^cdecl_[[:lower:]]' \
- -no-undefined
+ -no-undefined -version-info 1:0:0
libcdecl_la_SOURCES = src/scan.c src/parse.c src/parse-decl.c src/output.c \
src/explain.c src/declare.c src/i18n.c src/error.c \
- src/normalize.c src/cdecl-internal.h src/errtab.h
+ src/normalize.c src/cdecl-internal.h
libcdecl_la_LIBADD = libgnu.la $(LTLIBINTL) $(LTLIBTHREAD)
$(libcdecl_la_OBJECTS): $(gnulib_headers)
src/parse.lo: src/scan.h
src/scan.lo: src/parse.h
src/parse-decl.lo: src/scan.h src/parse.h src/typemap.h
-src/error.lo: src/errtab.h
src/output.lo: src/specstr.h
test/declgen.lo: test/typegen.h
MAINTAINERCLEANFILES += $(OPTFILES:.opt=.h)
EXTRA_DIST += $(DX_BASEDIR)/scripts/gen-options.awk $(OPTFILES)
-STRFILES = src/commands.str src/errtab.str
+STRFILES = src/commands.str
.str.h:
$(AM_V_GEN) $(AWK) -f $(DX_BASEDIR)/scripts/gen-strtab.awk $< >$@.tmp
$(AM_V_at) mv -f $@.tmp $@
Release 1a:
- * Various bug fixes.
+ * Error messages are no longer printed by libcdecl.
+ * Various bug fixes and improvements.
Release 1:
* Initial release.
-.Dd March 6, 2021
+.Dd March 13, 2021
.Os cdecl99
.Dt LIBCDECL \&3 "Cdecl99 Developer's Manual"
.Sh NAME
.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
+It is therefore thread-safe in the sense that errors occurring concurrently
+in another thread will not interfere with a call to
+.Fn cdecl_get_error .
+The returned structure 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.
+.Nm ,
+by the same thread, other than another call to
+.Fn cdecl_get_error .
.Pp
-If this function is called before an error has been indicated by an earlier
-call in the same thread, the behaviour is undefined.
+If no prior
+.Nm
+call has indicated that an error occurred in the current thread, the result
+from calling
+.Fn cdecl_get_error
+is unspecified.
.Sh PARSING DECLARATIONS
To parse a declaration, the function
.Pp
void cdecl__init_i18n(void);
const char *cdecl__strerror(unsigned code);
-void cdecl__set_error(const struct cdecl_error *err);
+void cdecl__err(unsigned code, ...);
unsigned long cdecl__build_typemap(struct cdecl_declspec *s);
struct cdecl_declspec *cdecl__normalize_specs(struct cdecl_declspec *specs);
/* Error handling. */
enum {
- CDECL_ENOMEM,
+ CDECL_ENOMEM = 1,
CDECL_ENOPARSE,
+ CDECL_EBADARRAY,
+ CDECL_EBADDECL,
+ CDECL_EBADPARAMS,
+ CDECL_EBADPOINTER,
+ CDECL_EBADQUAL,
+ CDECL_EBADRETURN,
+ CDECL_EBADSTOR,
+ CDECL_EBADTYPE,
+ CDECL_ENOTFUNC,
+ CDECL_EVOIDPARAM
};
struct cdecl_error {
*/
#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;
}
+++ /dev/null
-&err_enomem failed to allocate memory
-&err_enoparse syntax error
if (map & bit) {
if (bit == 1ul << MAP_LLONG_BIT)
- fprintf(stderr, "too many long specifiers\n");
+ cdecl__err(CDECL_EBADTYPE, _("too many long specifiers"));
else
- fprintf(stderr, "duplicate type specifier\n");
+ cdecl__err(CDECL_EBADTYPE, _("duplicate type specifier"));
return false;
}
map |= bit;
return true;
if (map == 0)
- fprintf(stderr, "no type specified\n");
+ cdecl__err(CDECL_EBADTYPE, _("no type specified"));
else
- fprintf(stderr, "invalid type specified\n");
+ cdecl__err(CDECL_EBADTYPE, _("invalid type specified"));
return false;
}
if (c->type == CDECL_TYPE_VOID &&
(d->type == CDECL_DECL_IDENT
|| d->type == CDECL_DECL_ARRAY)) {
- fprintf(stderr, "invalid declaration of type void\n");
+ cdecl__err(CDECL_EBADTYPE, _("invalid declaration of type void"));
return false;
}
continue;
case CDECL_SPEC_STOR:
if (top && abstract) {
- fprintf(stderr, "type names cannot have storage-class specifiers\n");
+ cdecl__err(CDECL_EBADSTOR, _("type names cannot have storage-class specifiers"));
return false;
}
if (!top && c->type != CDECL_STOR_REGISTER) {
- fprintf(stderr, "function parameters may only have register storage\n");
+ cdecl__err(CDECL_EBADSTOR, _("function parameters may only have register storage"));
return false;
}
if (++num_storage > 1) {
- fprintf(stderr, "too many storage-class specifiers\n");
+ cdecl__err(CDECL_EBADSTOR, _("too many storage-class specifiers"));
return false;
}
break;
* pointer qualifier list, which isn't checked here.
*/
if (c->type == CDECL_QUAL_RESTRICT) {
- fprintf(stderr, "only pointer types can be restrict-qualified.\n");
+ cdecl__err(CDECL_EBADQUAL, _("only pointer types can be restrict-qualified"));
return false;
}
break;
case CDECL_SPEC_FUNC:
if (abstract) {
- fprintf(stderr, "type names cannot have function specifiers\n");
+ cdecl__err(CDECL_ENOTFUNC, _("type names cannot have function specifiers"));
return false;
}
if (!top || d->type != CDECL_DECL_FUNCTION) {
- fprintf(stderr, "only function declarations may have function specifiers.\n");
+ cdecl__err(CDECL_ENOTFUNC, _("only function declarations can have function specifiers"));
return false;
}
break;
}
if (d->child->type != CDECL_DECL_NULL) {
- fprintf(stderr, "invalid function parameter\n");
+ cdecl__err(CDECL_EBADPARAMS, _("invalid function parameter"));
return -1;
}
if (decl->type == CDECL_DECL_FUNCTION
&& decl->child->type == CDECL_DECL_NULL
&& !function_is_reducible(decl)) {
- fprintf(stderr, "too many parentheses in function\n");
+ cdecl__err(CDECL_EBADPARAMS, _("too many parentheses in function"));
return -1;
}
continue;
if (spec != param->specifiers || spec->next != NULL) {
- fprintf(stderr, "void parameter must not have extra specifiers\n");
+ cdecl__err(CDECL_EVOIDPARAM, _("void parameter cannot have extra specifiers"));
return -1;
} else if (d->u.function.parameters->next) {
- fprintf(stderr, "a void parameter must stand alone\n");
+ cdecl__err(CDECL_EVOIDPARAM, _("void parameter must stand alone"));
return -1;
} else if (d->u.function.variadic) {
- fprintf(stderr, "variadic functions cannot have a void parameter\n");
+ cdecl__err(CDECL_EVOIDPARAM, _("variadic function cannot have void parameter"));
return -1;
}
}
switch (d->type) {
case CDECL_DECL_FUNCTION:
- fprintf(stderr, "functions cannot return functions\n");
+ cdecl__err(CDECL_EBADRETURN, _("functions cannot return functions"));
return -1;
case CDECL_DECL_ARRAY:
- fprintf(stderr, "functions cannot return arrays\n");
+ cdecl__err(CDECL_EBADRETURN, _("functions cannot return arrays"));
return -1;
}
switch (d->type) {
case CDECL_DECL_FUNCTION:
- fprintf(stderr, "array members cannot be functions\n");
+ cdecl__err(CDECL_EBADARRAY, _("array members cannot be functions"));
return -1;
}
for (spec = ptr->qualifiers; spec; spec = spec->next) {
if (spec->type == CDECL_QUAL_RESTRICT
&& d->type == CDECL_DECL_FUNCTION) {
- fprintf(stderr, "function pointers cannot be restrict-qualified\n");
+ cdecl__err(CDECL_EBADPOINTER, _("function pointers cannot be restrict-qualified"));
return -1;
}
}
if (cdecl_is_abstract(i->declarators)
&& (i != decl || i->next)) {
- fprintf(stderr, "mixing type names and declarations is not allowed\n");
+ cdecl__err(CDECL_EBADDECL, _("mixing type names and declarations is not allowed"));
goto err;
}
}
#define ALLOC(ptr, size) do { \
(ptr) = malloc(size); \
- if (!(ptr)) \
- FAIL("failed to allocate memory"); \
+ if (!(ptr)) { \
+ cdecl__err(CDECL_ENOMEM); \
+ YYERROR; \
+ } \
} while (0)
#define ALLOC_STRUCT(ptr, type, ...) do { \
%code provides {
void cdecl__free(struct cdecl *);
-void cdecl__yyerror(YYLTYPE *, void *, struct cdecl **, const char *);
int cdecl__yyparse(void *scanner, struct cdecl **out);
}
{
free_decl(decl);
}
+
+static void
+yyerror(YYLTYPE *loc, yyscan_t scanner, struct cdecl **out, const char *err)
+{
+ if (strstr(err, "T_LEX_ERROR"))
+ return;
+
+ cdecl__err(CDECL_ENOPARSE, "%s", err);
+}
%}
%destructor { free($$); } <strval>
array: T_LBRACKET T_UINT T_RBRACKET {
if ($2 == 0)
- FAIL("array length must be positive");
+ FAIL(_("array length must be positive"));
ALLOC_STRUCT($$, struct cdecl_declarator,
.type = CDECL_DECL_ARRAY,
.u.array.vla = $3);
} | T_ARRAY T_UINT T_OF {
if ($2 == 0)
- FAIL("array length must be positive");
+ FAIL(_("array length must be positive"));
ALLOC_STRUCT($$, struct cdecl_declarator,
.type = CDECL_DECL_ARRAY,
ALLOC($$, sizeof "");
strcpy($$, "");
}
-
-%%
-void
-yyerror(YYLTYPE *loc, yyscan_t scanner, struct cdecl **out, const char *err)
-{
- if (strstr(err, "T_LEX_ERROR"))
- return;
-
- cdecl__set_error(&(const struct cdecl_error){
- .code = CDECL_ENOPARSE,
- .str = err,
- });
-}
%option prefix="cdecl__yy"
%{
-#define lex_error(msg) do { \
- cdecl__yyerror(yylloc, NULL, NULL, (msg)); \
+#include <ctype.h>
+#include "cdecl-internal.h"
+#include "cdecl.h"
+
+#define lex_error(...) do { \
+ cdecl__err(CDECL_ENOPARSE, __VA_ARGS__); \
return T_LEX_ERROR; \
} while(0)
#define dup_token() do { \
yylval->strval = malloc(yyleng+1); \
- if (!yylval->strval) \
- lex_error("failed to allocate memory"); \
+ if (!yylval->strval) { \
+ cdecl__err(CDECL_ENOMEM); \
+ return T_LEX_ERROR; \
+ } \
strcpy(yylval->strval, yytext); \
} while(0)
%}
errno = 0;
yylval->uintval = strtoumax(yytext, &end, 0);
if (errno == ERANGE)
- lex_error("integer constant out of range");
+ lex_error(_("integer constant out of range"));
if (*end)
- lex_error("invalid integer constant");
+ lex_error(_("invalid integer constant"));
return T_UINT;
}
[[:space:]]+
. {
- char buf[] = "syntax error, unexpected #";
- *strchr(buf, '#') = *yytext;
- lex_error(buf);
+ char buf[5] = { yytext[0] };
+ unsigned char c = buf[0];
+
+ if (!isprint(c) || c == '\\' || c == '\'') {
+ /* Encode nonprinting characters with C-style escapes */
+ buf[0] = '\\';
+ switch (c) {
+ case '\a': buf[1] = 'a'; break;
+ case '\b': buf[1] = 'b'; break;
+ case '\f': buf[1] = 'f'; break;
+ case '\n': buf[1] = 'n'; break;
+ case '\r': buf[1] = 'r'; break;
+ case '\t': buf[1] = 't'; break;
+ case '\v': buf[1] = 'v'; break;
+ case '\\': buf[1] = '\\'; break;
+ case '\'': buf[1] = '\''; break;
+ default:
+ buf[1] = '0' + ((c >> 6) & 3);
+ buf[2] = '0' + ((c >> 3) & 7);
+ buf[3] = '0' + ((c >> 0) & 7);
+ }
+ }
+
+ lex_error(_("syntax error, unexpected '%s'"), buf);
}