set_err(msg, &state->err);
}
+/*
+ * In the NLS-disabled case, all format strings are of the form
+ *
+ * "blah blah %s"
+ *
+ * so we exploit this to implement a simple snprintf workalike using the
+ * libcdecl output helpers directly.
+ *
+ * In the NLS-enabled case, we have to use snprintf as format strings may
+ * be translated. GNU libintl ensures a suitable version is available.
+ */
+static size_t
+fmt_err(struct err_state *state, const char *fmt, const char *arg)
+{
+#if ENABLE_NLS
+ snprintf(state->str, state->nstr, fmt, arg);
+ return strlen(fmt) + strlen(arg);
+#else
+ struct output_state dst = { state->str, state->nstr };
+ size_t rc;
+
+ rc = cdecl__strlcpy(dst.dst, fmt, dst.dstlen);
+ cdecl__advance(&dst, rc-2);
+ cdecl__emit(&dst, arg);
+
+ return dst.accum;
+#endif
+}
+
/*
* Sets the library error to code; fmt is a printf-style string that may use
* up to one %s directive, to refer to arg.
void cdecl__err(unsigned code, const char *fmt, const char *arg)
{
struct err_state *state;
- int rc, try = 0;
+ unsigned try = 0;
+ size_t rc;
state = get_err_state();
if (!state)
return;
retry:
- rc = snprintf(state->str, state->nstr, fmt, arg);
- if (rc > 0 && rc >= state->nstr) {
+ rc = fmt_err(state, fmt, arg);
+ if (rc >= state->nstr) {
assert(try++ == 0 && rc < SIZE_MAX / 4);
+
state = alloc_err_state(state, (size_t)(rc+1u) * 3 / 2);
if (!state)
return;
#include "errmsg.h"
#include "tap.h"
+const char *cdecl__token_name(unsigned token)
+{
+ assert(0);
+}
+
static char *fmt_char(int c, char *buf)
{
int escape = 0;
return buf;
}
+static void check_code(const struct cdecl_error *err, unsigned expect)
+{
+ if (!tap_result(err->code == expect, "returned error code")) {
+ tap_diag("Failed, unexpected result");
+ tap_diag(" Received: %u", err->code);
+ tap_diag(" Expected: %u", expect);
+ }
+}
+
static void check_fixed_string(size_t len)
{
const struct cdecl_error *err;
memset(work2, 'X', len - 1);
tap_diag("cdecl__err w/ %lu-byte string", (unsigned long)len);
- cdecl__err(1234, work1, "");
+ cdecl__err(1234, work1, "XX");
memset(work1, 0, len);
err = cdecl_get_error();
- if (!tap_result(err->code == 1234, "returned error code")) {
- tap_diag("Failed, unexpected result");
- tap_diag(" Received: %u", err->code);
- tap_diag(" Expected: 1234");
- }
+ check_code(err, 1234);
errlen = strlen(err->str);
if (!tap_result(errlen == len-1, "returned string length")) {
free(work1);
}
+static void check_format_string(const char *fmt, const char *arg)
+{
+ size_t sz = strlen(fmt) + strlen(arg);
+ const struct cdecl_error *err;
+ char *work;
+
+ work = malloc(sz + 1);
+ if (!work)
+ abort();
+ sprintf(work, fmt, arg);
+
+ cdecl__err(5432, fmt, arg);
+ err = cdecl_get_error();
+
+ tap_diag("cdecl__err(\"%s\", \"%s\")", fmt, arg);
+ check_code(err, 5432);
+
+ if (!tap_result(!strcmp(err->str, work), "returned string")) {
+ tap_diag("Failed, unexpected result");
+ tap_diag(" Received: %.*s", (int)sz, err->str);
+ tap_diag(" Expected: %s", work);
+ }
+
+ free(work);
+}
+
static void check_enomem()
{
const char expmsg[] = "failed to allocate memory";
cdecl__errmsg(CDECL__ENOMEM);
err = cdecl_get_error();
- if (!tap_result(err->code == CDECL_ENOMEM, "returned error code")) {
- tap_diag("Failed, unexpected result");
- tap_diag(" Received: %u", err->code);
- tap_diag(" Expected: %d", CDECL_ENOMEM);
- }
-
+ check_code(err, CDECL_ENOMEM);
if (!tap_result(!strcmp(err->str, "failed to allocate memory"),
"returned string"))
{
}
}
+#define IFNLS(a, b) (ENABLE_NLS ? (a) : (b))
+
int main(void)
{
- tap_plan(3*3 + 2);
+ tap_plan(3*3 + 2 + 2);
check_fixed_string(50);
check_fixed_string(500);
check_fixed_string(5000);
+ /*
+ * When NLS is disabled, for implementation simplicity only format
+ * strings ending with %s are supported as this is sufficient.
+ * Otherwise, %s may appear anywhere.
+ */
+ check_format_string(IFNLS("hello %s world", "hello world %s"), "za");
+
check_enomem();
tap_done();