/*
* 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"
const char *cdecl__token_name(unsigned token)
{
assert(0);
}
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();
}