/* * Helper application to test internal library error reporting. * Copyright © 2023-2024 Nick Bowler * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "cdecl-internal.h" #include "cdecl.h" #include "errmsg.h" #include "tap.h" /* * Function called from output.c but not needed for error messaging. */ const char *cdecl__token_name(unsigned token) { tap_bail_out("stub cdecl__token_name called"); } static char *fmt_char(int c, char *buf) { int escape = 0; switch (c) { case '\a': escape = 'a'; break; case '\b': escape = 'b'; break; case '\t': escape = 't'; break; case '\n': escape = 'n'; break; case '\v': escape = 'v'; break; case '\f': escape = 'f'; break; case '\r': escape = 'r'; break; case '\\': escape = '\\'; break; case '\'': escape = '\''; break; } if (escape) sprintf(buf, "'\\%c'", escape); else if (isprint((unsigned char)c)) sprintf(buf, "'%c'", c); else sprintf(buf, "'\\%.3o'", c & 0777u); 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; char *work1, *work2; size_t errlen; assert(len > 0); work1 = calloc(2, len); if (!work1) abort(); work2 = work1 + len; memset(work1, 'X', len - 1); memset(work2, 'X', len - 1); tap_diag("cdecl__err w/ %lu-byte string", (unsigned long)len); cdecl__err(work1, "XX"); memset(work1, 0, len); err = cdecl_get_error(); check_code(err, CDECL_ENOPARSE); errlen = strlen(err->str); if (!tap_result(errlen == len-1, "returned string length")) { tap_diag("Failed, unexpected result"); tap_diag(" Received: %lu", (unsigned long)errlen); tap_diag(" Expected: %lu", (unsigned long)len); } if (!tap_result(!memcmp(err->str, work2, len), "returned string")) { char buf[8]; size_t n; n = strspn(err->str, "X"); if (n >= len) n = len; tap_diag("Failed, first incorrect character at offset %lu", (unsigned long)n); tap_diag(" Received: %s", fmt_char(err->str[n], buf)); tap_diag(" Expected: %s", fmt_char(work2[n], buf)); } 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(fmt, arg); err = cdecl_get_error(); tap_diag("cdecl__err(\"%s\", \"%s\")", fmt, arg); check_code(err, CDECL_ENOPARSE); 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"; const struct cdecl_error *err; tap_diag("cdecl__errmsg(CDECL__ENOMEM)"); cdecl__errmsg(CDECL__ENOMEM); err = cdecl_get_error(); check_code(err, CDECL_ENOMEM); if (!tap_result(!strcmp(err->str, "failed to allocate memory"), "returned string")) { unsigned i; for (i = 0; i < sizeof expmsg; i++) { if (err->str[i] != expmsg[i]) break; } tap_diag("Failed, first incorrect character at offset %u", i); tap_diag(" Received: %.*s", (int)sizeof expmsg, err->str); tap_diag(" Expected: %s", expmsg); } } #define IFNLS(a, b) (ENABLE_NLS ? (a) : (b)) int main(void) { 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(); }