#include <assert.h>
#include <ctype.h>
+#include <errno.h>
#include <string.h>
#include "alloc.h"
/*----- Output ------------------------------------------------------------*/
-void tvec_error(struct tvec_state *tv, const char *msg, ...)
- { va_list ap; va_start(ap, msg); tvec_error_v(tv, msg, &ap); }
-void tvec_error_v(struct tvec_state *tv, const char *msg, va_list *ap)
- { tv->output->ops->error(tv->output, msg, ap); exit(2); }
+int tvec_error(struct tvec_state *tv, const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg); tvec_error_v(tv, msg, &ap); va_end(ap);
+ tv->f |= TVSF_ERROR; return (-1);
+}
+int tvec_error_v(struct tvec_state *tv, const char *msg, va_list *ap)
+ { tv->output->ops->error(tv->output, msg, ap); return (-1); }
void tvec_notice(struct tvec_state *tv, const char *msg, ...)
{
void tvec_notice_v(struct tvec_state *tv, const char *msg, va_list *ap)
{ tv->output->ops->notice(tv->output, msg, ap); }
-void tvec_syntax(struct tvec_state *tv, int ch, const char *expect, ...)
+int tvec_syntax(struct tvec_state *tv, int ch, const char *expect, ...)
{
va_list ap;
+
va_start(ap, expect); tvec_syntax_v(tv, ch, expect, &ap); va_end(ap);
+ return (-1);
}
-void tvec_syntax_v(struct tvec_state *tv, int ch,
+int tvec_syntax_v(struct tvec_state *tv, int ch,
const char *expect, va_list *ap)
{
dstr d = DSTR_INIT;
switch (ch) {
case EOF: strcpy(found, "<eof>"); break;
- case '\n': strcpy(found, "<eol>"); break;
+ case '\n': strcpy(found, "<eol>"); ungetc(ch, tv->fp); break;
default:
if (isprint(ch)) sprintf(found, "`%c'", ch);
else sprintf(found, "<#x%02x>", ch);
}
dstr_vputf(&d, expect, ap);
tvec_error(tv, "syntax error: expected %s but found %s", expect, found);
+ return (-1);
}
void tvec_skipgroup(struct tvec_state *tv, const char *excuse, ...)
return (0);
}
-void tvec_bench(struct tvec_state *tv)
+int tvec_bench(struct tvec_state *tv)
{
const struct tvec_bench *tvb = tv->test->arg.p;
struct bench_state *b;
end_1:
if (r.ctx) xfree(r.ctx);
end_0:
- return;
+ return (0);
}
/*----- Main machinery ----------------------------------------------------*/
}
}
-void tvec_flushtoeol(struct tvec_state *tv, unsigned f)
+int tvec_flushtoeol(struct tvec_state *tv, unsigned f)
{
- int ch;
+ int ch, rc = 0;
for (;;) {
ch = getc(tv->fp);
switch (ch) {
- case '\n': tv->lno++; return;
- case EOF: return;
+ case '\n': tv->lno++; return (rc);
+ case EOF: return (rc);
case ';': f |= TVFF_ALLOWANY; break;
default:
- if (!(f&TVFF_ALLOWANY) && !isspace(ch))
+ if (!(f&TVFF_ALLOWANY) && !isspace(ch)) {
tvec_syntax(tv, ch, "end-of-line");
+ rc = -1; f |= TVFF_ALLOWANY;
+ }
break;
}
}
ch = getc(tv->fp);
if (ch == '\n' || ch == EOF || ch == ';' ||
(delims && strchr(delims, ch))) {
- if (expect) tvec_syntax(tv, ch, expect, ap);
+ if (expect) return (tvec_syntax(tv, ch, expect, ap));
else { ungetc(ch, tv->fp); return (-1); }
}
if (d->len) DPUTC(d, ' ');
#undef f_mismatch
}
-void tvec_runtest(struct tvec_state *tv)
+int tvec_runtest(struct tvec_state *tv)
{
tv->test->fn(tv->in, tv->out, (/*unconst*/ void *)tv->test->arg.p);
- tvec_check(tv, 0);
+ tvec_check(tv, 0); return (0);
}
static void begin_test(struct tvec_state *tv)
for (rd = tv->test->regs; rd->name; rd++) {
if (TVEC_REG(tv, in, rd->i)->f&TVRF_LIVE)
{ if (rd->i < tv->nrout) TVEC_REG(tv, out, rd->i)->f |= TVRF_LIVE; }
- else if (!(rd->f&TVRF_OPT))
+ else if (!(rd->f&TVRF_OPT)) {
tvec_error(tv, "required register `%s' not set in test `%s'",
rd->name, tv->test->name);
+ goto end;
+ }
}
if (!(tv->f&TVSF_SKIP))
{ begin_test(tv); tv->test->run(tv); tvec_endtest(tv); }
+end:
tv->f &= ~TVSF_OPEN; release_registers(tv); init_registers(tv);
}
release_registers(tv); tv->test = 0;
}
-void tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
+int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
{
dstr d = DSTR_INIT;
const struct tvec_test *test;
const struct tvec_regdef *rd;
struct tvec_reg *r;
int ch;
+ int rc = 0;
tv->infile = infile; tv->lno = 1; tv->fp = fp;
goto end;
case '[':
+ end_test_group(tv);
tvec_skipspc(tv);
DRESET(&d); tvec_readword(tv, &d, "];", "group name");
+ tvec_skipspc(tv);
+ ch = getc(tv->fp); if (ch != ']') tvec_syntax(tv, ch, "`]'");
for (test = tv->tests; test->name; test++)
if (STRCMP(d.buf, ==, test->name)) goto found_test;
- tvec_error(tv, "unknown test group `%s'", d.buf);
+ tvec_error(tv, "unknown test group `%s'", d.buf); goto flush_line;
found_test:
- tvec_skipspc(tv);
- ch = getc(tv->fp); if (ch != ']') tvec_syntax(tv, ch, "`]'");
- tvec_flushtoeol(tv, 0);
- end_test_group(tv);
- tv->test = test;
- begin_test_group(tv);
+ tvec_flushtoeol(tv, 0); tv->test = test; begin_test_group(tv);
break;
case '\n':
if (isspace(ch)) {
tvec_skipspc(tv);
ch = getc(tv->fp);
- if (ch == EOF)
- goto end;
- else if (ch == ';')
- { tvec_flushtoeol(tv, TVFF_ALLOWANY); continue; }
- else
- { tvec_flushtoeol(tv, 0); check(tv); }
+ if (ch == EOF) goto end;
+ else if (ch == ';') tvec_flushtoeol(tv, TVFF_ALLOWANY);
+ else if (tvec_flushtoeol(tv, 0)) rc = -1;
+ else check(tv);
} else if (ch == ';')
- { tvec_flushtoeol(tv, TVFF_ALLOWANY); continue; }
+ tvec_flushtoeol(tv, TVFF_ALLOWANY);
else {
ungetc(ch, tv->fp);
- DRESET(&d); tvec_readword(tv, &d, "=:;", "register name");
+ DRESET(&d);
+ if (tvec_readword(tv, &d, "=:;", "register name")) goto flush_line;
tvec_skipspc(tv); ch = getc(tv->fp);
- if (ch != '=' && ch != ':') tvec_syntax(tv, ch, "`=' or `:'");
+ if (ch != '=' && ch != ':')
+ { tvec_syntax(tv, ch, "`=' or `:'"); goto flush_line; }
tvec_skipspc(tv);
if (d.buf[0] == '@') {
if (STRCMP(d.buf, ==, "@status")) {
{ tv->test_lno = tv->lno; tv->f |= TVSF_OPEN; }
ch = getc(tv->fp);
if (ch == EOF || ch == '\n' || ch == ';')
- tvec_syntax(tv, ch, "status character");
- if (ch == '\\') {
+ { tvec_syntax(tv, ch, "status character"); goto flush_line; }
+ else if (ch == '\\') {
ch = getc(tv->fp);
- if (ch == EOF || ch == '\n')
+ if (ch == EOF || ch == '\n') {
tvec_syntax(tv, ch, "escaped status character");
+ goto flush_line;
+ }
}
tv->expst = ch;
tvec_flushtoeol(tv, 0);
- } else
+ } else {
tvec_error(tv, "unknown special register `%s'", d.buf);
+ goto flush_line;
+ }
} else {
- if (!tv->test) tvec_error(tv, "no current test");
+ if (!tv->test)
+ { tvec_error(tv, "no current test"); goto flush_line; }
for (rd = tv->test->regs; rd->name; rd++)
if (STRCMP(rd->name, ==, d.buf)) goto found_reg;
tvec_error(tv, "unknown register `%s' for test `%s'",
d.buf, tv->test->name);
+ goto flush_line;
found_reg:
if (!(tv->f&TVSF_OPEN))
{ tv->test_lno = tv->lno; tv->f |= TVSF_OPEN; }
tvec_skipspc(tv);
r = TVEC_REG(tv, in, rd->i);
- if (r->f&TVRF_LIVE)
+ if (r->f&TVRF_LIVE) {
tvec_error(tv, "register `%s' already set", rd->name);
- rd->ty->parse(&r->v, rd, tv); r->f |= TVRF_LIVE;
+ goto flush_line;
+ }
+ if (rd->ty->parse(&r->v, rd, tv)) goto flush_line;
+ r->f |= TVRF_LIVE;
}
}
break;
}
+ continue;
+
+ flush_line:
+ tvec_flushtoeol(tv, TVFF_ALLOWANY); rc = -1;
}
+ if (ferror(tv->fp))
+ { tvec_error(tv, "error reading input: %s", strerror(errno)); rc = -1; }
end:
end_test_group(tv);
tv->infile = 0; tv->fp = 0;
dstr_destroy(&d);
+ return (rc);
}
/*----- Ad-hoc testing ----------------------------------------------------*/
static const struct tvec_regdef no_regs = { 0, 0, 0, 0, { 0 } };
-static void fakerun(struct tvec_state *tv)
+static int fakerun(struct tvec_state *tv)
{ assert(!"fake run function"); }
static void fakefn(const struct tvec_reg *in, struct tvec_reg *out, void *p)
{ assert(!"fake test function"); }