#include <assert.h>
#include <ctype.h>
#include <errno.h>
+#include <float.h>
#include <limits.h>
+#include <math.h>
#include <stdio.h>
#include <string.h>
/*----- Preliminary utilities ---------------------------------------------*/
+#ifdef isnan
+# define NANP(x) isnan(x)
+#else
+# define NANP(x) (!((x) == (x)))
+#endif
+
+#ifdef isinf
+# define INFP(x) isinf(x)
+#else
+# define INFP(x) ((x) > DBL_MAX || (x) < DBL_MIN)
+#endif
+
static int signed_to_buf(buf *b, long i)
{
kludge64 k;
return (wd/4);
}
-static void check_signed_range(long i,
- const struct tvec_irange *ir,
- struct tvec_state *tv)
+static int check_signed_range(long i,
+ const struct tvec_irange *ir,
+ struct tvec_state *tv)
{
- if (ir && (ir->min > i || i > ir->max))
+ if (ir && (ir->min > i || i > ir->max)) {
tvec_error(tv, "integer %ld out of range (must be in [%ld .. %ld])",
i, ir->min, ir->max);
+ return (-1);
+ }
+ return (0);
}
-static void check_unsigned_range(unsigned long u,
- const struct tvec_urange *ur,
- struct tvec_state *tv)
+static int check_unsigned_range(unsigned long u,
+ const struct tvec_urange *ur,
+ struct tvec_state *tv)
{
- if (ur && (ur->min > u || u > ur->max))
+ if (ur && (ur->min > u || u > ur->max)) {
tvec_error(tv, "integer %lu out of range (must be in [%lu .. %lu])",
u, ur->min, ur->max);
+ return (-1);
+ }
+ return (0);
}
-static void parse_signed(long *i_out, const char *p,
- const struct tvec_irange *ir,
- struct tvec_state *tv)
+static int parse_signed(long *i_out, const char *p,
+ const struct tvec_irange *ir,
+ struct tvec_state *tv)
{
char *q; const char *pp;
int olderr;
olderr = errno; errno = 0;
pp = p; if (*pp == '-' || *pp == '+') pp++;
- if (!ISDIGIT(*pp)) tvec_syntax(tv, *pp, "signed integer");
+ if (!ISDIGIT(*pp))
+ return (tvec_syntax(tv, *pp ? *pp : fgetc(tv->fp), "signed integer"));
i = strtol(p, &q, 0);
- if (*q && !ISSPACE(*q)) tvec_syntax(tv, *q, "end-of-line");
- if (errno) tvec_error(tv, "invalid integer `%s'", p);
- check_signed_range(i, ir, tv);
+ if (*q && !ISSPACE(*q)) return (tvec_syntax(tv, *q, "end-of-line"));
+ if (errno)
+ return (tvec_error(tv, "invalid integer `%s': %s", p, strerror(errno)));
+ if (check_signed_range(i, ir, tv)) return (-1);
errno = olderr; *i_out = i;
+ return (0);
}
-static void parse_unsigned(unsigned long *u_out, const char *p,
- const struct tvec_urange *ur,
- struct tvec_state *tv)
+static int parse_unsigned(unsigned long *u_out, const char *p,
+ const struct tvec_urange *ur,
+ struct tvec_state *tv)
{
char *q;
int olderr;
unsigned long u;
olderr = errno; errno = 0;
- if (!ISDIGIT(*p)) tvec_syntax(tv, *p, "unsigned integer");
+ if (!ISDIGIT(*p)) return (tvec_syntax(tv, *p, "unsigned integer"));
u = strtoul(p, &q, 0);
- if (*q && !ISSPACE(*q)) tvec_syntax(tv, *q, "end-of-line");
- if (errno) tvec_error(tv, "invalid integer `%s'", p);
- check_unsigned_range(u, ur, tv);
+ if (*q && !ISSPACE(*q)) return (tvec_syntax(tv, *q, "end-of-line"));
+ if (errno)
+ return (tvec_error(tv, "invalid integer `%s': %s", p, strerror(errno)));
+ if (check_unsigned_range(u, ur, tv)) return (-1);
errno = olderr; *u_out = u;
+ return (0);
+}
+
+static void format_floating(const struct gprintf_ops *gops, void *go,
+ double x)
+{
+ int prec;
+
+ if (NANP(x))
+ gprintf(gops, go, "#nan");
+ else if (INFP(x))
+ gprintf(gops, go, x > 0 ? "#+inf" : "#-inf");
+ else {
+ /* Ugh. C doesn't provide any function for just printing a
+ * floating-point number /correctly/, i.e., so that you can read the
+ * result back and recover the number you first thought of. There are
+ * complicated algorithms published for doing this, but I really don't
+ * want to get into that here. So we have this.
+ *
+ * The sign doesn't cause significant difficulty so we're going to ignore
+ * it for now. So suppose we're given a number %$x = f b^e$%, in
+ * base-%$b$% format, so %$f b^n$% and %$e$% are integers, with
+ * %$0 \le f < 1$%. We're going to convert it into the nearest integer
+ * of the form %$X = F B^E$%, with similar conditions, only with the
+ * additional requirement that %$X$% is normalized, i.e., that %$X = 0$%
+ * or %$F \ge B^{-N}$%.
+ *
+ * We're rounding to the nearest such %$X$%. If there is to be ambiguity
+ * in the conversion, then some %$x = f b^e$% and the next smallest
+ * representable number %$x' = x + b^{e-n}$% must both map to the same
+ * %$X$%, which means both %$x$% and %$x'$% must be nearer to %$X$% than
+ * any other number representable in the target system. The nest larger
+ * number is %$X' = X + B^{E-N}$%; the next smaller number will normally
+ * be %$W = X - B^{E-N}$%, but if %$F = 1/B$ then the next smaller number
+ * is actually %$X - B^{E-N-1}$%. We ignore this latter possibility in
+ * the pursuit of a conservative estimate (though actually it doesn't
+ * matter).
+ *
+ * If both %$x$% and %$x'$% map to %$X$% then we must have
+ * %$L = X - B^{E-N}/2 \le x$% and %$x + b^{e-n} \le R = X + B^{E-N}/2$%;
+ * so firstly %$f b^e = x \ge L = W + B^{E-N}/2 > W = (F - B^{-N}) B^E$%,
+ * and secondly %$b^{e-n} \le B^{E-N}$%. Since these inequalities are in
+ * opposite senses, we can divide, giving
+ *
+ * %$f b^e/b^{e-n} > (F - B^{-N}) B^E/B^{E-N}$% ,
+ *
+ * whence
+ *
+ * %$f b^n > (F - B^{-N}) B^N = F B^N - 1$% .
+ *
+ * Now %$f \le 1 - b^{-n}$%, and %$F \ge B^{-1}$%, so, for this to be
+ * possible, it must be the case that
+ *
+ * %$(1 - b^{-n}) b^n = b^n - 1 > B^{N-1} - 1$% .
+ *
+ * Then rearrange and take logarithms, obtaining
+ *
+ * %$(N - 1) \log B < n \log b$% ,
+ *
+ * and so
+ *
+ * %$N < n \log b/\log B + 1$% .
+ *
+ * Recall that this is a necessary condition for a collision to occur; we
+ * are therefore safe whenever
+ *
+ * %$N \ge n \log b/\log B + 1$% ;
+ *
+ * so, taking ceilings,
+ *
+ * %$N \ge \lceil n \log b/\log B \rceil + 1$% .
+ *
+ * So that's why we have this.
+ *
+ * I'm going to assume that @n = DBL_MANT_DIG@ is sufficiently small that
+ * we can calculate this without ending up on the wrong side of an
+ * integer boundary.
+ *
+ * In C11, we have @DBL_DECIMAL_DIG@, which should be the same value only
+ * as a constant. Except that modern compilers are more than clever
+ * enough to work out that this is a constant anyway.
+ *
+ * This is sometimes an overestimate: we'll print out meaningless digits
+ * that don't represent anything we actually know about the number in
+ * question. To fix that, we'd need a complicated algorithm like Steele
+ * and White's Dragon4, Gay's @dtoa@, or Burger and Dybvig's algorithm
+ * (note that Loitsch's Grisu2 is conservative, and Grisu3 hands off to
+ * something else in difficult situations).
+ */
+
+ prec = ceil(DBL_MANT_DIG*log(FLT_RADIX)/log(10)) + 1;
+ gprintf(gops, go, "%.*g", prec, x);
+ }
+}
+
+static int eqish_floating_p(double x, double y,
+ const struct tvec_floatinfo *fi)
+{
+ double xx, yy, t;
+
+ if (NANP(x)) return (NANP(y)); else if (NANP(y)) return (0);
+ if (INFP(x)) return (x == y); else if (INFP(y)) return (0);
+
+ switch (fi ? fi->f&TVFF_EQMASK : TVFF_EXACT) {
+ case TVFF_EXACT:
+ return (x == y);
+ case TVFF_ABSDELTA:
+ t = x - y; if (t < 0) t = -t; return (t < fi->delta);
+ case TVFF_RELDELTA:
+ xx = x >= 0 ? x : -x; yy = y >= 0 ? y : -y;
+ if (xx < yy) { t = x; x = y; y = t; }
+ return (1.0 - y/x < fi->delta);
+ default:
+ abort();
+ }
+}
+
+static int parse_floating(double *x_out, const char *p,
+ const struct tvec_floatinfo *fi,
+ struct tvec_state *tv)
+{
+ const char *pp; char *q;
+ dstr d = DSTR_INIT;
+ double x;
+ int olderr, rc;
+
+ if (STRCMP(p, ==, "#nan")) {
+#ifdef NAN
+ x = NAN; rc = 0;
+#else
+ tvec_error(tv, "NaN not supported on this system");
+ rc = -1; goto end;
+#endif
+ } else if (STRCMP(p, ==, "#inf") ||
+ STRCMP(p, ==, "#+inf") ||
+ STRCMP(p, ==, "+#inf")) {
+#ifdef NAN
+ x = INFINITY; rc = 0;
+#else
+ tvec_error(tv, "infinity not supported on this system");
+ rc = -1; goto end;
+#endif
+ } else if (STRCMP(p, ==, "#-inf") ||
+ STRCMP(p, ==, "-#inf")) {
+#ifdef NAN
+ x = -INFINITY; rc = 0;
+#else
+ tvec_error(tv, "infinity not supported on this system");
+ rc = -1; goto end;
+#endif
+ } else {
+ pp = p;
+ if (*pp == '+' || *pp == '-') pp++;
+ if (*pp == '.') pp++;
+ if (!ISDIGIT(*pp)) {
+ tvec_syntax(tv, *pp ? *pp : fgetc(tv->fp), "floating-point number");
+ rc = -1; goto end;
+ }
+ olderr = errno; errno = 0;
+ x = strtod(p, &q);
+ if (*q) {
+ tvec_syntax(tv, *q, "end-of-line");
+ rc = -1; goto end;
+ }
+ if (errno && (errno != ERANGE || (x > 0 ? -x : x) == HUGE_VAL)) {
+ tvec_error(tv, "invalid floating-point number `%s': %s",
+ p, strerror(errno));
+ rc = -1; goto end;
+ }
+ errno = olderr;
+ }
+
+ if (NANP(x) && fi && !(fi->f&TVFF_NANOK)) {
+ tvec_error(tv, "#nan not allowed here");
+ rc = -1; goto end;
+ }
+ if (fi && ((!(fi->f&TVFF_NOMIN) && x < fi->min) ||
+ (!(fi->f&TVFF_NOMAX) && x > fi->max))) {
+ dstr_puts(&d, "floating-point number ");
+ format_floating(&dstr_printops, &d, x);
+ dstr_puts(&d, " out of range (must be in ");
+ if (fi->f&TVFF_NOMIN)
+ dstr_puts(&d, "(#-inf");
+ else
+ { dstr_putc(&d, '['); format_floating(&dstr_printops, &d, fi->min); }
+ dstr_puts(&d, " .. ");
+ if (fi->f&TVFF_NOMAX)
+ dstr_puts(&d, "#+inf)");
+ else
+ { format_floating(&dstr_printops, &d, fi->max); dstr_putc(&d, ']'); }
+ dstr_putc(&d, ')'); dstr_putz(&d);
+ tvec_error(tv, "%s", d.buf); rc = -1; goto end;
+ }
+
+ *x_out = x; rc = 0;
+end:
+ dstr_destroy(&d);
+ return (rc);
}
static int convert_hex(char ch, int *v_out)
else return (-1);
}
-static void read_quoted_string(dstr *d, int quote, struct tvec_state *tv)
+static int read_escape(int *ch_out, struct tvec_state *tv)
{
- char expect[4];
int ch, i, esc;
unsigned f = 0;
#define f_brace 1u
- sprintf(expect, "`%c'", quote);
+ ch = getc(tv->fp);
+ switch (ch) {
+ case EOF: case '\n': tvec_syntax(tv, ch, "string escape");
+ case '\'': *ch_out = '\''; break;
+ case '\\': *ch_out = '\\'; break;
+ case '"': *ch_out = '"'; break;
+ case 'a': *ch_out = '\a'; break;
+ case 'b': *ch_out = '\b'; break;
+ case 'e': *ch_out = '\x1b'; break;
+ case 'f': *ch_out = '\f'; break;
+ case 'n': *ch_out = '\n'; break;
+ case 'r': *ch_out = '\r'; break;
+ case 't': *ch_out = '\t'; break;
+ case 'v': *ch_out = '\v'; break;
+
+ case 'x':
+ ch = getc(tv->fp);
+ if (ch == '{') { f |= f_brace; ch = getc(tv->fp); }
+ else f &= ~f_brace;
+ if (convert_hex(ch, &esc))
+ return (tvec_syntax(tv, ch, "hex digit"));
+ for (;;) {
+ ch = getc(tv->fp); if (convert_hex(ch, &i)) break;
+ esc = 8*esc + i;
+ if (esc > UCHAR_MAX)
+ return (tvec_error(tv,
+ "character code %d out of range", esc));
+ }
+ if (!(f&f_brace)) ungetc(ch, tv->fp);
+ else if (ch != '}') return (tvec_syntax(tv, ch, "`}'"));
+ *ch_out = esc;
+ break;
+
+ default:
+ if ('0' <= ch && ch < '8') {
+ i = 1; esc = ch - '0';
+ for (;;) {
+ ch = getc(tv->fp);
+ if ('0' > ch || ch >= '8') { ungetc(ch, tv->fp); break; }
+ esc = 8*esc + ch - '0';
+ i++; if (i >= 3) break;
+ }
+ if (esc > UCHAR_MAX)
+ return (tvec_error(tv,
+ "character code %d out of range", esc));
+ *ch_out = esc;
+ } else
+ return (tvec_syntax(tv, ch, "string escape"));
+ }
+
+ return (0);
+
+#undef f_brace
+}
+
+static int read_quoted_string(dstr *d, int quote, struct tvec_state *tv)
+{
+ int ch;
for (;;) {
ch = getc(tv->fp);
- reinsert:
switch (ch) {
case EOF: case '\n':
- tvec_syntax(tv, ch, expect);
-
+ return (tvec_syntax(tv, ch, "`%c'", quote));
case '\\':
if (quote == '\'') goto ordinary;
- ch = getc(tv->fp);
- switch (ch) {
- case EOF: tvec_syntax(tv, ch, expect);
- case '\n': tv->lno++; break;
- case '\'': DPUTC(d, '\''); break;
- case '\\': DPUTC(d, '\\'); break;
- case '"': DPUTC(d, '"'); break;
- case 'a': DPUTC(d, '\a'); break;
- case 'b': DPUTC(d, '\b'); break;
- case 'e': DPUTC(d, '\x1b'); break;
- case 'f': DPUTC(d, '\f'); break;
- case 'n': DPUTC(d, '\n'); break;
- case 'r': DPUTC(d, '\r'); break;
- case 't': DPUTC(d, '\t'); break;
- case 'v': DPUTC(d, '\v'); break;
-
- case 'x':
- ch = getc(tv->fp);
- if (ch == '{') { f |= f_brace; ch = getc(tv->fp); }
- else f &= ~f_brace;
- if (convert_hex(ch, &esc)) tvec_syntax(tv, ch, "hex digit");
- for (;;) {
- ch = getc(tv->fp); if (convert_hex(ch, &i)) break;
- esc = 8*esc + i;
- if (esc > UCHAR_MAX)
- tvec_error(tv, "character code %d out of range", esc);
- }
- DPUTC(d, esc);
- if (!(f&f_brace)) goto reinsert;
- else if (ch != '}') tvec_syntax(tv, ch, "`}'");
- break;
-
- default:
- if ('0' <= ch && ch < '8') {
- i = 1; esc = ch - '0';
- for (;;) {
- ch = getc(tv->fp);
- if (i > 3 || '0' > ch || ch >= '8') break;
- esc = 8*esc + ch - '0'; i++;
- }
- if (esc > UCHAR_MAX)
- tvec_error(tv, "character code %d out of range", esc);
- DPUTC(d, esc);
- goto reinsert;
- }
- tvec_syntax(tv, ch, "string escape");
- break;
- }
- break;
-
+ ch = getc(tv->fp); if (ch == '\n') { tv->lno++; break; }
+ ungetc(ch, tv->fp); if (read_escape(&ch, tv)) return (-1);
+ goto ordinary;
default:
if (ch == quote) goto end;
ordinary:
end:
DPUTZ(d);
+ return (0);
+}
-#undef f_brace
+#define FCF_BRACE 1u
+static void format_charesc(int ch, unsigned f,
+ const struct gprintf_ops *gops, void *go)
+{
+ switch (ch) {
+ case '\a': gprintf(gops, go, "\\a"); break;
+ case '\b': gprintf(gops, go, "\\b"); break;
+ case '\x1b': gprintf(gops, go, "\\e"); break;
+ case '\f': gprintf(gops, go, "\\f"); break;
+ case '\r': gprintf(gops, go, "\\r"); break;
+ case '\n': gprintf(gops, go, "\\n"); break;
+ case '\t': gprintf(gops, go, "\\t"); break;
+ case '\v': gprintf(gops, go, "\\v"); break;
+ case '\'': gprintf(gops, go, "\\'"); break;
+ case '"': gprintf(gops, go, "\\\""); break;
+ default:
+ if (f&FCF_BRACE)
+ gprintf(gops, go, "\\x{%0*x}", hex_width(UCHAR_MAX), ch);
+ else
+ gprintf(gops, go, "\\x%0*x", hex_width(UCHAR_MAX), ch);
+ break;
+ }
+}
+
+static void format_char(int ch, const struct gprintf_ops *gops, void *go)
+{
+ if (ch == EOF)
+ gprintf(gops, go, "#eof");
+ else if (isprint(ch) && ch != '\'')
+ gprintf(gops, go, "'%c'", ch);
+ else {
+ gprintf(gops, go, "'");
+ format_charesc(ch, 0, gops, go);
+ gprintf(gops, go, "'");
+ }
}
enum { TVCODE_BARE, TVCODE_HEX, TVCODE_BASE64, TVCODE_BASE32 };
ch = getc(tv->fp);
switch (ch) {
case EOF:
- goto bad;
+ tvec_syntax(tv, ch, "bareword");
+ rc = -1; goto end;
case '\n':
if (s == ESCAPE) { tv->lno++; goto addch; }
if (s == WORD) pos = d->len;
- ungetc(ch, tv->fp); if (tvec_nexttoken(tv)) { rc = -1; goto done; }
+ ungetc(ch, tv->fp); if (tvec_nexttoken(tv)) { rc = -1; goto end; }
DPUTC(d, ' '); s = SPACE;
break;
case '"': case '\'': case '!':
- if (s == SPACE) { ungetc(ch, tv->fp); rc = 0; goto done; }
+ if (s == SPACE) { ungetc(ch, tv->fp); goto done; }
goto addch;
case '\\':
s = ESCAPE;
done:
if (s == SPACE) d->len = pos;
- DPUTZ(d); return (rc);
-
-bad:
- tvec_syntax(tv, ch, "bareword");
+ DPUTZ(d); rc = 0;
+end:
+ return (rc);
}
static void set_up_encoding(const codec_class **ccl_out, unsigned *f_out,
}
}
-static void read_compound_string(void **p_inout, size_t *sz_inout,
- unsigned code, struct tvec_state *tv)
+static int read_compound_string(void **p_inout, size_t *sz_inout,
+ unsigned code, struct tvec_state *tv)
{
const codec_class *ccl; unsigned f;
codec *cdc;
dstr d = DSTR_INIT, w = DSTR_INIT;
char *p;
- int ch, err;
+ int ch, err, rc;
set_up_encoding(&ccl, &f, code);
if (tvec_nexttoken(tv)) tvec_syntax(tv, fgetc(tv->fp), "string");
do {
ch = getc(tv->fp);
if (ch == '"' || ch == '\'')
- read_quoted_string(&d, ch, tv);
+ { if (read_quoted_string(&d, ch, tv)) { rc = -1; goto end; } }
else if (ch == '!') {
ungetc(ch, tv->fp);
DRESET(&w); tvec_readword(tv, &w, ";", "`!'-keyword");
else if (STRCMP(w.buf, ==, "!hex")) code = TVCODE_HEX;
else if (STRCMP(w.buf, ==, "!base32")) code = TVCODE_BASE32;
else if (STRCMP(w.buf, ==, "!base64")) code = TVCODE_BASE64;
- else tvec_error(tv, "unknown string keyword `%s'", w.buf);
+ else {
+ tvec_error(tv, "unknown string keyword `%s'", w.buf);
+ rc = -1; goto end;
+ }
set_up_encoding(&ccl, &f, code);
} else if (ccl) {
ungetc(ch, tv->fp);
DRESET(&w);
- tvec_readword(tv, &w, ";", "%s-encoded fragment", ccl->name);
+ if (tvec_readword(tv, &w, ";", "%s-encoded fragment", ccl->name))
+ { rc = -1; goto end; }
cdc = ccl->decoder(f);
err = cdc->ops->code(cdc, w.buf, w.len, &d);
if (!err) err = cdc->ops->code(cdc, 0, 0, &d);
- if (err)
+ if (err) {
tvec_error(tv, "invalid %s fragment `%s': %s",
ccl->name, w.buf, codec_strerror(err));
+ rc = -1; goto end;
+ }
cdc->ops->destroy(cdc);
} else switch (code) {
case TVCODE_BARE:
if (*sz_inout <= d.len)
{ xfree(*p_inout); *p_inout = xmalloc(d.len + 1); }
p = *p_inout; memcpy(p, d.buf, d.len); p[d.len] = 0; *sz_inout = d.len;
+ rc = 0;
+end:
dstr_destroy(&d); dstr_destroy(&w);
+ return (rc);
}
/*----- Skeleton ----------------------------------------------------------*/
static void release_...(union tvec_regval *rv, const struct tvec_regdef *rd)
static int eq_...(const union tvec_regval *rv0, const union tvec_regval *rv1,
const struct tvec_regdef *rd)
-static size_t measure_...(const union tvec_regval *rv,
- const struct tvec_regdef *rd)
static int tobuf_...(buf *b, const union tvec_regval *rv,
const struct tvec_regdef *rd)
static int frombuf_...(buf *b, union tvec_regval *rv,
const struct tvec_regdef *rd)
-static void parse_...(union tvec_regval *rv, const struct tvec_regdef *rd,
- struct tvec_state *tv)
+static int parse_...(union tvec_regval *rv, const struct tvec_regdef *rd,
+ struct tvec_state *tv)
static void dump_...(const union tvec_regval *rv,
const struct tvec_regdef *rd,
struct tvec_state *tv, unsigned style)
const struct tvec_regty tvty_... = {
- init_..., release_..., eq_..., measure_...,
+ init_..., release_..., eq_...,
tobuf_..., frombuf_...,
parse_..., dump_...
};
const struct tvec_regdef *rd)
{ return (rv0->u == rv1->u); }
-static size_t measure_int(const union tvec_regval *rv,
- const struct tvec_regdef *rd)
- { return (8); }
-
static int tobuf_int(buf *b, const union tvec_regval *rv,
const struct tvec_regdef *rd)
{ return (signed_to_buf(b, rv->i)); }
static int frombuf_int(buf *b, union tvec_regval *rv,
const struct tvec_regdef *rd)
- { return signed_from_buf(b, &rv->i); }
+ { return (signed_from_buf(b, &rv->i)); }
static int frombuf_uint(buf *b, union tvec_regval *rv,
const struct tvec_regdef *rd)
{ return (unsigned_from_buf(b, &rv->u)); }
-static void parse_int(union tvec_regval *rv, const struct tvec_regdef *rd,
- struct tvec_state *tv)
+static int parse_int(union tvec_regval *rv, const struct tvec_regdef *rd,
+ struct tvec_state *tv)
{
dstr d = DSTR_INIT;
-
- tvec_readword(tv, &d, ";", "signed integer");
- parse_signed(&rv->i, d.buf, rd->arg.p, tv);
- tvec_flushtoeol(tv, 0);
+ int rc;
+
+ if (tvec_readword(tv, &d, ";", "signed integer"))
+ { rc = -1; goto end; }
+ if (parse_signed(&rv->i, d.buf, rd->arg.p, tv))
+ { rc = -1; goto end; }
+ if (tvec_flushtoeol(tv, 0))
+ { rc = -1; goto end; }
+ rc = 0;
+end:
dstr_destroy(&d);
+ return (rc);
}
-static void parse_uint(union tvec_regval *rv, const struct tvec_regdef *rd,
- struct tvec_state *tv)
+static int parse_uint(union tvec_regval *rv, const struct tvec_regdef *rd,
+ struct tvec_state *tv)
{
dstr d = DSTR_INIT;
-
- tvec_readword(tv, &d, ";", "unsigned integer");
- parse_unsigned(&rv->u, d.buf, rd->arg.p, tv);
- tvec_flushtoeol(tv, 0);
+ int rc;
+
+ if (tvec_readword(tv, &d, ";", "unsigned integer"))
+ { rc = -1; goto end; }
+ if (parse_unsigned(&rv->u, d.buf, rd->arg.p, tv))
+ { rc = -1; goto end; }
+ if (tvec_flushtoeol(tv, 0))
+ { rc = -1; goto end; }
+ rc = 0;
+end:
dstr_destroy(&d);
+ return (rc);
}
static void dump_int(const union tvec_regval *rv,
const struct tvec_regdef *rd,
- struct tvec_state *tv, unsigned style)
+ unsigned style,
+ const struct gprintf_ops *gops, void *go)
{
unsigned long u;
- tvec_write(tv, "%ld", rv->i);
+ gprintf(gops, go, "%ld", rv->i);
if (!(style&TVSF_COMPACT)) {
if (rv->i >= 0) u = rv->i;
else u = -(unsigned long)rv->i;
- tvec_write(tv, " ; = %s0x%0*lx", rv->i < 0 ? "-" : "", hex_width(u), u);
+ gprintf(gops, go, " ; = %s0x%0*lx",
+ rv->i < 0 ? "-" : "", hex_width(u), u);
+ if (rv->i == EOF || (0 <= rv->i && rv->i < UCHAR_MAX))
+ { gprintf(gops, go, " = "); format_char(rv->i, gops, go); }
}
}
static void dump_uint(const union tvec_regval *rv,
const struct tvec_regdef *rd,
- struct tvec_state *tv, unsigned style)
+ unsigned style,
+ const struct gprintf_ops *gops, void *go)
{
- tvec_write(tv, "%lu", rv->u);
- if (!(style&TVSF_COMPACT))
- tvec_write(tv, " ; = 0x%0*lx", hex_width(rv->u), rv->u);
+ gprintf(gops, go, "%lu", rv->u);
+ if (!(style&TVSF_COMPACT)) {
+ gprintf(gops, go, " ; = 0x%0*lx", hex_width(rv->u), rv->u);
+ if (rv->u < UCHAR_MAX)
+ { gprintf(gops, go, " = "); format_char(rv->u, gops, go); }
+ }
}
const struct tvec_regty tvty_int = {
- init_int, release_int, eq_int, measure_int,
+ init_int, release_int, eq_int,
tobuf_int, frombuf_int,
parse_int, dump_int
};
tvrange_i32 = { -2147483648, 2147483647 };
const struct tvec_regty tvty_uint = {
- init_uint, release_int, eq_uint, measure_int,
+ init_uint, release_int, eq_uint,
tobuf_uint, frombuf_uint,
parse_uint, dump_uint
};
return (tvec_claimeq(tv, &tvty_uint, 0, file, lno, expr));
}
+/*----- Main code ---------------------------------------------------------*/
+
+static void init_float(union tvec_regval *rv, const struct tvec_regdef *rd)
+ { rv->f = 0.0; }
+static void release_float(union tvec_regval *rv,
+ const struct tvec_regdef *rd)
+ { ; }
+
+static int eq_float(const union tvec_regval *rv0,
+ const union tvec_regval *rv1,
+ const struct tvec_regdef *rd)
+ { return (eqish_floating_p(rv0->f, rv1->f, rd->arg.p)); }
+
+static int tobuf_float(buf *b, const union tvec_regval *rv,
+ const struct tvec_regdef *rd)
+ { return (buf_putf64l(b, rv->f)); }
+static int frombuf_float(buf *b, union tvec_regval *rv,
+ const struct tvec_regdef *rd)
+ { return (buf_getf64l(b, &rv->f)); }
+
+static int parse_float(union tvec_regval *rv, const struct tvec_regdef *rd,
+ struct tvec_state *tv)
+{
+ dstr d = DSTR_INIT;
+ int rc;
+
+ if (tvec_readword(tv, &d, ";", "floating-point number"))
+ { rc = -1; goto end; }
+ if (parse_floating(&rv->f, d.buf, rd->arg.p, tv))
+ { rc = -1; goto end; }
+ if (tvec_flushtoeol(tv, 0))
+ { rc = -1; goto end; }
+ rc = 0;
+end:
+ dstr_destroy(&d);
+ return (rc);
+}
+
+static void dump_float(const union tvec_regval *rv,
+ const struct tvec_regdef *rd,
+ unsigned style,
+ const struct gprintf_ops *gops, void *go)
+ { format_floating(gops, go, rv->f); }
+
+const struct tvec_regty tvty_float = {
+ init_float, release_float, eq_float,
+ tobuf_float, frombuf_float,
+ parse_float, dump_float
+};
+
+int tvec_claimeqish_float(struct tvec_state *tv,
+ double f0, double f1, unsigned f, double delta,
+ const char *file, unsigned lno,
+ const char *expr)
+{
+ struct tvec_floatinfo fi;
+ union tvec_misc arg;
+
+ fi.f = f; fi.min = fi.max = 0.0; fi.delta = delta; arg.p = &fi;
+ tv->in[0].v.f = f0; tv->in[1].v.f = f1;
+ return (tvec_claimeq(tv, &tvty_float, &arg, file, lno, expr));
+}
+int tvec_claimeq_float(struct tvec_state *tv,
+ double f0, double f1,
+ const char *file, unsigned lno,
+ const char *expr)
+{
+ return (tvec_claimeqish_float(tv, f0, f1, TVFF_EXACT, 0.0,
+ file, lno, expr));
+}
+
/*----- Enumerations ------------------------------------------------------*/
static void init_enum(union tvec_regval *rv, const struct tvec_regdef *rd)
const struct tvec_regdef *rd)
{
const struct tvec_enuminfo *ei = rd->arg.p;
+ const struct tvec_fenuminfo *fei;
switch (ei->mv) {
#define CASE(tag, ty, slot) \
- case TVMISC_##tag: return (rv0->slot == rv1->slot);
+ case TVMISC_##tag: PREP_##tag return (HANDLE_##tag);
+#define PREP_INT
+#define HANDLE_INT rv0->i == rv1->i
+#define PREP_UINT
+#define HANDLE_UINT rv0->u == rv1->u
+#define PREP_FLT fei = (const struct tvec_fenuminfo *)ei;
+#define HANDLE_FLT eqish_floating_p(rv0->f, rv1->f, fei->fi)
+#define PREP_PTR
+#define HANDLE_PTR rv0->p == rv1->p
TVEC_MISCSLOTS(CASE)
#undef CASE
+#undef PREP_INT
+#undef HANDLE_INT
+#undef PREP_UINT
+#undef HANDLE_UINT
+#undef PREP_FLT
+#undef HANDLE_FLT
+#undef PREP_PTR
+#undef HANDLE_PTR
default: abort();
}
}
const struct tvec_regdef *rd)
{
const struct tvec_enuminfo *ei = rd->arg.p;
+ const struct tvec_penuminfo *pei;
+ const struct tvec_passoc *pa;
+ long i;
switch (ei->mv) {
#define CASE(tag, ty, slot) \
- case TVMISC_##tag: return (HANDLE_##tag);
-#define HANDLE_INT signed_to_buf(b, rv->i)
-#define HANDLE_UINT unsigned_to_buf(b, rv->u)
-#define HANDLE_PTR -1
+ case TVMISC_##tag: HANDLE_##tag
+#define HANDLE_INT return (signed_to_buf(b, rv->i));
+#define HANDLE_UINT return (unsigned_to_buf(b, rv->u));
+#define HANDLE_FLT return (buf_putf64l(b, rv->f));
+#define HANDLE_PTR \
+ pei = (const struct tvec_penuminfo *)ei; \
+ for (pa = pei->av, i = 0; pa->tag; pa++, i++) \
+ if (pa->p == rv->p) goto found; \
+ if (!rv->p) i = -1; \
+ else return (-1); \
+ found: \
+ return (signed_to_buf(b, i));
TVEC_MISCSLOTS(CASE)
#undef CASE
#undef HANDLE_INT
#undef HANDLE_UINT
+#undef HANDLE_FLT
#undef HANDLE_PTR
default: abort();
}
const struct tvec_regdef *rd)
{
const struct tvec_enuminfo *ei = rd->arg.p;
+ const struct tvec_penuminfo *pei;
+ const struct tvec_passoc *pa;
+ long i, n;
switch (ei->mv) {
#define CASE(tag, ty, slot) \
- case TVMISC_##tag: return (HANDLE_##tag);
-#define HANDLE_INT signed_from_buf(b, &rv->i)
-#define HANDLE_UINT unsigned_from_buf(b, &rv->u)
-#define HANDLE_PTR -1
+ case TVMISC_##tag: HANDLE_##tag
+#define HANDLE_INT return (signed_from_buf(b, &rv->i));
+#define HANDLE_UINT return (unsigned_from_buf(b, &rv->u));
+#define HANDLE_FLT return (buf_getf64l(b, &rv->f));
+#define HANDLE_PTR \
+ pei = (const struct tvec_penuminfo *)ei; \
+ for (pa = pei->av, n = 0; pa->tag; pa++, n++); \
+ if (signed_from_buf(b, &i)) return (-1); \
+ if (0 <= i && i < n) rv->p = (/*unconst*/ void *)pei->av[i].p; \
+ else if (i == -1) rv->p = 0; \
+ else return (-1); \
+ return (0);
TVEC_MISCSLOTS(CASE)
#undef CASE
#undef HANDLE_INT
#undef HANDLE_UINT
+#undef HANDLE_FLT
#undef HANDLE_PTR
default: abort();
}
}
-static void parse_enum(union tvec_regval *rv, const struct tvec_regdef *rd,
- struct tvec_state *tv)
+static int parse_enum(union tvec_regval *rv, const struct tvec_regdef *rd,
+ struct tvec_state *tv)
{
const struct tvec_enuminfo *ei = rd->arg.p;
#define DECLS(tag, ty, slot) \
+ const struct tvec_##slot##enuminfo *slot##ei; \
const struct tvec_##slot##assoc *slot##a;
TVEC_MISCSLOTS(DECLS)
#undef DECLS
dstr d = DSTR_INIT;
+ int rc;
- tvec_readword(tv, &d, ";", "enumeration tag or literal integer");
+ if (tvec_readword(tv, &d, ";", "enumeration tag or literal integer"))
+ { rc = -1; goto end; }
switch (ei->mv) {
+
#define CASE(tag_, ty, slot) \
case TVMISC_##tag_: \
- for (slot##a = ei->u.slot.av; slot##a->tag; slot##a++) \
+ slot##ei = (const struct tvec_##slot##enuminfo *)ei; \
+ for (slot##a = slot##ei->av; slot##a->tag; slot##a++) \
if (STRCMP(d.buf, ==, slot##a->tag)) \
- { rv->slot = FETCH_##tag_; goto end; }
+ { rv->slot = FETCH_##tag_; goto done; } \
+ HANDLE_##tag_ goto done;
+
#define FETCH_INT (ia->i)
+#define HANDLE_INT \
+ if (parse_signed(&rv->i, d.buf, iei->ir, tv)) \
+ { rc = -1; goto end; }
+
#define FETCH_UINT (ua->u)
+#define HANDLE_UINT \
+ if (parse_unsigned(&rv->u, d.buf, uei->ur, tv)) \
+ { rc = -1; goto end; }
+
+#define FETCH_FLT (fa->f)
+#define HANDLE_FLT \
+ if (parse_floating(&rv->f, d.buf, fei->fi, tv)) \
+ { rc = -1; goto end; }
+
#define FETCH_PTR ((/*unconst*/ void *)(pa->p))
- TVEC_MISCSLOTS(CASE)
-#undef CASE
-#undef FETCH_INT
-#undef FETCH_UINT
-#undef FETCH_PTR
- }
+#define HANDLE_PTR \
+ if (STRCMP(d.buf, ==, "#nil")) rv->p = 0; \
+ else goto tagonly;
- switch (ei->mv) {
-#define CASE(tag, ty, slot) \
- case TVMISC_##tag: HANDLE_##tag goto end;
-#define HANDLE_INT parse_signed(&rv->i, d.buf, ei->u.i.ir, tv);
-#define HANDLE_UINT parse_unsigned(&rv->u, d.buf, ei->u.u.ur, tv);
-#define HANDLE_PTR if (STRCMP(d.buf, ==, "#nil")) rv->p = 0; \
- else goto tagonly;
TVEC_MISCSLOTS(CASE)
+
#undef CASE
+#undef FETCH_INT
#undef HANDLE_INT
+#undef FETCH_UINT
#undef HANDLE_UINT
+#undef FETCH_FLT
+#undef HANDLE_FLT
+#undef FETCH_PTR
#undef HANDLE_PTR
- default: tagonly:
+
+ tagonly:
tvec_error(tv, "unknown `%s' value `%s'", ei->name, d.buf);
+ rc = -1; goto end;
}
+done:
+ if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
+ rc = 0;
end:
- tvec_flushtoeol(tv, 0);
dstr_destroy(&d);
+ return (rc);
}
static void dump_enum(const union tvec_regval *rv,
const struct tvec_regdef *rd,
- struct tvec_state *tv, unsigned style)
+ unsigned style,
+ const struct gprintf_ops *gops, void *go)
{
const struct tvec_enuminfo *ei = rd->arg.p;
#define DECLS(tag, ty, slot) \
+ const struct tvec_##slot##enuminfo *slot##ei; \
const struct tvec_##slot##assoc *slot##a;
TVEC_MISCSLOTS(DECLS)
#undef DECLS
switch (ei->mv) {
#define CASE(tag_, ty, slot) \
case TVMISC_##tag_: \
- for (slot##a = ei->u.slot.av; slot##a->tag; slot##a++) \
+ slot##ei = (const struct tvec_##slot##enuminfo *)ei; \
+ for (slot##a = slot##ei->av; slot##a->tag; slot##a++) \
if (rv->slot == slot##a->slot) \
{ tag = slot##a->tag; goto found; } \
break;
#undef CASE
default: abort();
}
- goto print_int;
+ goto print_raw;
found:
f |= f_known;
- tvec_write(tv, "%s", tag);
+ gprintf(gops, go, "%s", tag);
if (style&TVSF_COMPACT) return;
- tvec_write(tv, " ; = ");
+ gprintf(gops, go, " ; = ");
-print_int:
+print_raw:
switch (ei->mv) {
#define CASE(tag, ty, slot) \
case TVMISC_##tag: HANDLE_##tag break;
-#define HANDLE_INT tvec_write(tv, "%ld", rv->i);
-#define HANDLE_UINT tvec_write(tv, "%lu", rv->u);
-#define HANDLE_PTR if (!rv->p) tvec_write(tv, "#nil"); \
- else tvec_write(tv, "#<%s %p>", ei->name, rv->p);
+#define HANDLE_INT gprintf(gops, go, "%ld", rv->i);
+#define HANDLE_UINT gprintf(gops, go, "%lu", rv->u);
+#define HANDLE_FLT format_floating(gops, go, rv->f);
+#define HANDLE_PTR if (!rv->p) gprintf(gops, go, "#nil"); \
+ else gprintf(gops, go, "#<%s %p>", ei->name, rv->p);
TVEC_MISCSLOTS(CASE)
#undef CASE
#undef HANDLE_INT
#undef HANDLE_UINT
+#undef HANDLE_FLT
#undef HANDLE_PTR
}
+ if (style&TVSF_COMPACT) return;
switch (ei->mv) {
case TVMISC_INT:
- if (!(f&f_known)) tvec_write(tv, " ;");
+ if (!(f&f_known)) gprintf(gops, go, " ;");
if (rv->i >= 0) u = rv->i;
else u = -(unsigned long)rv->i;
- tvec_write(tv, " = %s0x%0*lx", rv->i < 0 ? "-" : "", hex_width(u), u);
+ gprintf(gops, go, " = %s0x%0*lx",
+ rv->i < 0 ? "-" : "", hex_width(u), u);
+ if (rv->i == EOF || (0 <= rv->i && rv->i < UCHAR_MAX))
+ { gprintf(gops, go, " = "); format_char(rv->i, gops, go); }
break;
case TVMISC_UINT:
- if (!(f&f_known)) tvec_write(tv, " ;");
- tvec_write(tv, " = 0x%0*lx", hex_width(rv->u), rv->u);
+ if (!(f&f_known)) gprintf(gops, go, " ;");
+ gprintf(gops, go, " = 0x%0*lx", hex_width(rv->u), rv->u);
+ if (rv->u < UCHAR_MAX)
+ { gprintf(gops, go, " = "); format_char(rv->u, gops, go); }
break;
}
}
const struct tvec_regty tvty_enum = {
- init_enum, release_int, eq_enum, measure_int,
+ init_enum, release_int, eq_enum,
tobuf_enum, frombuf_enum,
parse_enum, dump_enum
};
+static const struct tvec_iassoc bool_assoc[] = {
+ { "nil", 0 },
+ { "false", 0 },
+ { "f", 0 },
+ { "no", 0 },
+ { "n", 0 },
+ { "off", 0 },
+
+ { "t", 1 },
+ { "true", 1 },
+ { "yes", 1 },
+ { "y", 1 },
+ { "on", 1 },
+
+ { 0, 0 }
+};
+
+const struct tvec_ienuminfo tvenum_bool =
+ { { "bool", TVMISC_INT }, bool_assoc, &tvrange_int };
+
#define DEFCLAIM(tag, ty, slot) \
- int tvec_claimeq_##slot##enum(struct tvec_state *tv, \
- const struct tvec_enuminfo *ei, \
- ty e0, ty e1, \
- const char *file, unsigned lno, \
- const char *expr) \
+ int tvec_claimeq_##slot##enum \
+ (struct tvec_state *tv, \
+ const struct tvec_##slot##enuminfo *ei, ty e0, ty e1, \
+ const char *file, unsigned lno, const char *expr) \
{ \
union tvec_misc arg; \
\
- assert(ei->mv == TVMISC_##tag); \
+ assert(ei->_ei.mv == TVMISC_##tag); \
arg.p = ei; \
tv->in[0].v.slot = GET_##tag(e0); \
tv->out[0].v.slot = GET_##tag(e1); \
}
#define GET_INT(e) (e)
#define GET_UINT(e) (e)
+#define GET_FLT(e) (e)
#define GET_PTR(e) ((/*unconst*/ void *)(e))
TVEC_MISCSLOTS(DEFCLAIM)
#undef DEFCLAIM
#undef GET_INT
#undef GET_UINT
+#undef GET_FLT
#undef GET_PTR
/*----- Flag types --------------------------------------------------------*/
-static void parse_flags(union tvec_regval *rv, const struct tvec_regdef *rd,
- struct tvec_state *tv)
+static int parse_flags(union tvec_regval *rv, const struct tvec_regdef *rd,
+ struct tvec_state *tv)
{
const struct tvec_flaginfo *fi = rd->arg.p;
const struct tvec_flag *f;
unsigned long m = 0, v = 0, t;
dstr d = DSTR_INIT;
- int ch;
+ int ch, rc;
for (;;) {
- DRESET(&d); tvec_readword(tv, &d, "|;", "flag name or integer");
+ DRESET(&d);
+ if (tvec_readword(tv, &d, "|;", "flag name or integer"))
+ { rc = -1; goto end; }
for (f = fi->fv; f->tag; f++)
if (STRCMP(f->tag, ==, d.buf)) {
- if (m&f->m) tvec_error(tv, "colliding flag setting");
- else { m |= f->m; v |= f->v; goto next; }
+ if (m&f->m)
+ { tvec_error(tv, "colliding flag setting"); rc = -1; goto end; }
+ else
+ { m |= f->m; v |= f->v; goto next; }
}
- parse_unsigned(&t, d.buf, fi->range, tv); v |= t;
+ if (parse_unsigned(&t, d.buf, fi->range, tv))
+ { rc = -1; goto end; }
+ v |= t;
next:
if (tvec_nexttoken(tv)) break;
- ch = getc(tv->fp); if (ch != '|') tvec_syntax(tv, ch, "`|'");
- if (tvec_nexttoken(tv)) tvec_syntax(tv, '\n', "flag name or integer");
+ ch = getc(tv->fp);
+ if (ch != '|') { tvec_syntax(tv, ch, "`|'"); rc = -1; goto end; }
+ if (tvec_nexttoken(tv))
+ { tvec_syntax(tv, '\n', "flag name or integer"); rc = -1; goto end; }
}
rv->u = v;
+ rc = 0;
+end:
+ dstr_destroy(&d);
+ return (rc);
}
static void dump_flags(const union tvec_regval *rv,
const struct tvec_regdef *rd,
- struct tvec_state *tv, unsigned style)
+ unsigned style,
+ const struct gprintf_ops *gops, void *go)
{
const struct tvec_flaginfo *fi = rd->arg.p;
const struct tvec_flag *f;
for (f = fi->fv, sep = ""; f->tag; f++)
if ((m&f->m) && (v&f->m) == f->v) {
- tvec_write(tv, "%s%s", sep, f->tag); m &= ~f->m;
+ gprintf(gops, go, "%s%s", sep, f->tag); m &= ~f->m;
sep = style&TVSF_COMPACT ? "|" : " | ";
}
- if (v&m) tvec_write(tv, "%s0x%0*lx", sep, hex_width(v), v&m);
+ if (v&m) gprintf(gops, go, "%s0x%0*lx", sep, hex_width(v), v&m);
if (!(style&TVSF_COMPACT))
- tvec_write(tv, " ; = 0x%0*lx", hex_width(rv->u), rv->u);
+ gprintf(gops, go, " ; = 0x%0*lx", hex_width(rv->u), rv->u);
}
const struct tvec_regty tvty_flags = {
- init_uint, release_int, eq_uint, measure_int,
+ init_uint, release_int, eq_uint,
tobuf_uint, frombuf_uint,
parse_flags, dump_flags
};
return (tvec_claimeq(tv, &tvty_flags, &arg, file, lno, expr));
}
+/*----- Characters --------------------------------------------------------*/
+
+static int tobuf_char(buf *b, const union tvec_regval *rv,
+ const struct tvec_regdef *rd)
+{
+ uint32 u;
+ if (0 <= rv->i && rv->i <= UCHAR_MAX) u = rv->i;
+ else if (rv->i == EOF) u = MASK32;
+ else return (-1);
+ return (buf_putu32l(b, u));
+}
+
+static int frombuf_char(buf *b, union tvec_regval *rv,
+ const struct tvec_regdef *rd)
+{
+ uint32 u;
+
+ if (buf_getu32l(b, &u)) return (-1);
+ if (0 <= u && u <= UCHAR_MAX) rv->i = u;
+ else if (u == MASK32) rv->i = EOF;
+ else return (-1);
+ return (0);
+}
+
+static int parse_char(union tvec_regval *rv, const struct tvec_regdef *rd,
+ struct tvec_state *tv)
+{
+ dstr d = DSTR_INIT;
+ int ch, rc;
+ unsigned f = 0;
+#define f_quote 1u
+
+ ch = getc(tv->fp);
+ if (ch == '#') {
+ ungetc(ch, tv->fp);
+ if (tvec_readword(tv, &d, ";", "character name")) { rc = -1; goto end; }
+ if (STRCMP(d.buf, ==, "#eof"))
+ rv->i = EOF;
+ else {
+ rc = tvec_error(tv, "unknown character name `%s'", d.buf);
+ goto end;
+ }
+ rc = 0; goto end;
+ }
+
+ if (ch == '\'') { f |= f_quote; ch = getc(tv->fp); }
+ switch (ch) {
+ case '\'':
+ if (!(f&f_quote)) goto plain;
+ case EOF: case '\n':
+ rc = tvec_syntax(tv, ch, "character"); goto end;
+ case '\\':
+ if (read_escape(&ch, tv)) return (-1);
+ default: plain:
+ rv->i = ch; break;
+ }
+ if (f&f_quote) {
+ ch = getc(tv->fp);
+ if (ch != '\'') { rc = tvec_syntax(tv, ch, "`''"); goto end; }
+ }
+ if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
+ rc = 0;
+end:
+ dstr_destroy(&d);
+ return (rc);
+
+#undef f_quote
+}
+
+static void dump_char(const union tvec_regval *rv,
+ const struct tvec_regdef *rd,
+ unsigned style,
+ const struct gprintf_ops *gops, void *go)
+{
+ unsigned u;
+
+ if ((style&TVSF_COMPACT) && isprint(rv->i) && rv->i != '\'')
+ gprintf(gops, go, "%c", (int)rv->i);
+ else
+ format_char(rv->i, gops, go);
+
+ if (!(style&TVSF_COMPACT)) {
+ u = rv->i < 0 ? -rv->i : rv->i;
+ gprintf(gops, go, " ; = %d = %s0x%0*x",
+ (int)rv->i, rv->i < 0 ? "-" : "", hex_width(u), u);
+ }
+}
+
+const struct tvec_regty tvty_char = {
+ init_int, release_int, eq_int,
+ tobuf_char, frombuf_char,
+ parse_char, dump_char
+};
+
+int tvec_claimeq_char(struct tvec_state *tv, int c0, int c1,
+ const char *file, unsigned lno, const char *expr)
+{
+ tv->in[0].v.i = c0; tv->out[0].v.i = c1;
+ return (tvec_claimeq(tv, &tvty_char, 0, file, lno, expr));
+}
+
/*----- Text and byte strings ---------------------------------------------*/
void tvec_allocstring(union tvec_regval *rv, size_t sz)
MEMCMP(rv0->bytes.p, ==, rv1->bytes.p, rv1->bytes.sz)));
}
-static size_t measure_string(const union tvec_regval *rv,
- const struct tvec_regdef *rd)
- { return (rv->str.sz + 4); }
-
-static size_t measure_bytes(const union tvec_regval *rv,
- const struct tvec_regdef *rd)
- { return (rv->bytes.sz + 4); }
-
static int tobuf_string(buf *b, const union tvec_regval *rv,
const struct tvec_regdef *rd)
{ return (buf_putmem32l(b, rv->str.p, rv->str.sz)); }
return (0);
}
-static void check_string_length(size_t sz, const struct tvec_urange *ur,
- struct tvec_state *tv)
+static int check_string_length(size_t sz, const struct tvec_urange *ur,
+ struct tvec_state *tv)
{
if (ur && (ur->min > sz || sz > ur->max))
- tvec_error(tv, "invalid string length %lu; must be in [%lu..%lu]",
- (unsigned long)sz, ur->min, ur->max);
+ return (tvec_error(tv,
+ "invalid string length %lu; must be in [%lu..%lu]",
+ (unsigned long)sz, ur->min, ur->max));
+ return (0);
}
-static void parse_string(union tvec_regval *rv, const struct tvec_regdef *rd,
- struct tvec_state *tv)
+static int parse_string(union tvec_regval *rv, const struct tvec_regdef *rd,
+ struct tvec_state *tv)
{
void *p = rv->str.p;
- read_compound_string(&p, &rv->str.sz, TVCODE_BARE, tv); rv->str.p = p;
- check_string_length(rv->str.sz, rd->arg.p, tv);
+ if (read_compound_string(&p, &rv->str.sz, TVCODE_BARE, tv)) return (-1);
+ rv->str.p = p;
+ if (check_string_length(rv->str.sz, rd->arg.p, tv)) return (-1);
+ return (0);
}
-static void parse_bytes(union tvec_regval *rv, const struct tvec_regdef *rd,
- struct tvec_state *tv)
+static int parse_bytes(union tvec_regval *rv, const struct tvec_regdef *rd,
+ struct tvec_state *tv)
{
void *p = rv->bytes.p;
- read_compound_string(&p, &rv->bytes.sz, TVCODE_HEX, tv); rv->bytes.p = p;
- check_string_length(rv->bytes.sz, rd->arg.p, tv);
+ if (read_compound_string(&p, &rv->bytes.sz, TVCODE_HEX, tv)) return (-1);
+ rv->bytes.p = p;
+ if (check_string_length(rv->bytes.sz, rd->arg.p, tv)) return (-1);
+ return (0);
}
static void dump_string(const union tvec_regval *rv,
const struct tvec_regdef *rd,
- struct tvec_state *tv, unsigned style)
+ unsigned style,
+ const struct gprintf_ops *gops, void *go)
{
const unsigned char *p, *q, *l;
- int ch;
unsigned f = 0;
#define f_nonword 1u
#define f_newline 2u
- if (!rv->str.sz) { tvec_write(tv, "\"\""); return; }
+ if (!rv->str.sz) { gprintf(gops, go, "\"\""); return; }
p = (const unsigned char *)rv->str.p; l = p + rv->str.sz;
if (*p == '!' || *p == ';' || *p == '"' || *p == '\'') goto quote;
for (q = p; q < l; q++)
if (*q == '\n' && q != l - 1) f |= f_newline;
else if (!*q || !isgraph(*q) || *q == '\\') f |= f_nonword;
- if (f&f_newline) { tvec_write(tv, "\n\t"); goto quote; }
+ if (f&f_newline) { gprintf(gops, go, "\n\t"); goto quote; }
else if (f&f_nonword) goto quote;
- tv->output->ops->write(tv->output, (const char *)p, rv->str.sz); return;
+ gops->putm(go, (const char *)p, rv->str.sz); return;
quote:
- tvec_write(tv, "\"");
+ gprintf(gops, go, "\"");
for (q = p; q < l; q++)
- switch (*q) {
- case '"': case '\\': ch = *q; goto escape;
- case '\a': ch = 'a'; goto escape;
- case '\b': ch = 'b'; goto escape;
- case '\x1b': ch = 'e'; goto escape;
- case '\f': ch = 'f'; goto escape;
- case '\r': ch = 'r'; goto escape;
- case '\t': ch = 't'; goto escape;
- case '\v': ch = 'v'; goto escape;
- escape:
- if (p < q)
- tv->output->ops->write(tv->output, (const char *)p, q - p);
- tvec_write(tv, "\\%c", ch); p = q + 1;
- break;
-
- case '\n':
- if (p < q)
- tv->output->ops->write(tv->output, (const char *)p, q - p);
- tvec_write(tv, "\\n"); p = q + 1;
- if (!(style&TVSF_COMPACT) && q < l) tvec_write(tv, "\"\t\"");
- break;
-
- default:
- if (isprint(*q)) break;
- if (p < q)
- tv->output->ops->write(tv->output, (const char *)p, q - p);
- tvec_write(tv, "\\x{%0*x}", hex_width(UCHAR_MAX), *q); p = q + 1;
- break;
+ if (!isprint(*q) || *q == '"') {
+ if (p < q) gops->putm(go, (const char *)p, q - p);
+ if (*q == '\n' && !(style&TVSF_COMPACT))gprintf(gops, go, "\\n\"\t\"");
+ else format_charesc(*q, FCF_BRACE, gops, go);
}
- if (p < q) tv->output->ops->write(tv->output, (const char *)p, q - p);
- tvec_write(tv, "\"");
+ if (p < q) gops->putm(go, (const char *)p, q - p);
+ gprintf(gops, go, "\"");
#undef f_nonword
#undef f_newline
static void dump_bytes(const union tvec_regval *rv,
const struct tvec_regdef *rd,
- struct tvec_state *tv, unsigned style)
+ unsigned style,
+ const struct gprintf_ops *gops, void *go)
{
const unsigned char *p = rv->bytes.p, *l = p + rv->bytes.sz;
size_t off, sz = rv->bytes.sz;
int wd;
if (!sz) {
- tvec_write(tv, style&TVSF_COMPACT ? "\"\"" : "\"\" ; empty");
+ gprintf(gops, go, style&TVSF_COMPACT ? "\"\"" : "\"\" ; empty");
return;
}
if (style&TVSF_COMPACT) {
- while (p < l) tvec_write(tv, "%02x", *p++);
+ while (p < l) gprintf(gops, go, "%02x", *p++);
return;
}
- if (sz > 16) tvec_write(tv, "\n\t");
+ if (sz > 16) gprintf(gops, go, "\n\t");
off = 0; wd = hex_width(sz);
while (p < l) {
else n = 16;
for (i = 0; i < 16; i++) {
- if (i < n) tvec_write(tv, "%02x", p[i]);
- else tvec_write(tv, " ");
- if (i%4 == 3) tvec_write(tv, " ");
+ if (i < n) gprintf(gops, go, "%02x", p[i]);
+ else gprintf(gops, go, " ");
+ if (i%4 == 3) gprintf(gops, go, " ");
}
- tvec_write(tv, " ; ");
- if (sz > 16) tvec_write(tv, "[%0*lx] ", wd, (unsigned long)off);
+ gprintf(gops, go, " ; ");
+ if (sz > 16) gprintf(gops, go, "[%0*lx] ", wd, (unsigned long)off);
for (i = 0; i < n; i++)
- tvec_write(tv, "%c", isprint(p[i]) ? p[i] : '.');
+ gprintf(gops, go, "%c", isprint(p[i]) ? p[i] : '.');
p += n; off += n;
- if (p < l) tvec_write(tv, "\n\t");
+ if (p < l) gprintf(gops, go, "\n\t");
}
}
const struct tvec_regty tvty_string = {
- init_string, release_string, eq_string, measure_string,
+ init_string, release_string, eq_string,
tobuf_string, frombuf_string,
parse_string, dump_string
};
const struct tvec_regty tvty_bytes = {
- init_bytes, release_bytes, eq_bytes, measure_bytes,
+ init_bytes, release_bytes, eq_bytes,
tobuf_bytes, frombuf_bytes,
parse_bytes, dump_bytes
};
static const char units[] = "kMGTPEZY";
-static void parse_buffer(union tvec_regval *rv,
- const struct tvec_regdef *rd,
- struct tvec_state *tv)
+static int parse_buffer(union tvec_regval *rv,
+ const struct tvec_regdef *rd,
+ struct tvec_state *tv)
{
dstr d = DSTR_INIT;
char *q; const char *unit;
int olderr;
size_t pos;
unsigned long u, t;
+ int rc;
unsigned f = 0;
#define f_range 1u
- tvec_readword(tv, &d, ";", "buffer length");
+ if (tvec_readword(tv, &d, ";", "buffer length")) { rc = -1; goto end; }
olderr = errno; errno = 0;
u = strtoul(d.buf, &q, 0);
if (errno) goto bad;
}
}
if (*q && *q != ';') goto bad;
- check_string_length(u, rd->arg.p, tv);
+ if (check_string_length(u, rd->arg.p, tv)) { rc = -1; goto end; }
- tvec_flushtoeol(tv, 0);
- tvec_allocbytes(rv, u); memset(rv->bytes.p, 0, u);
- DDESTROY(&d); return;
+ if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
+ tvec_allocbytes(rv, u); memset(rv->bytes.p, '?', u);
+ rc = 0;
+end:
+ DDESTROY(&d); return (rc);
bad:
tvec_error(tv, "invalid buffer length `%s'", d.buf);
+ rc = -1; goto end;
rangerr:
tvec_error(tv, "buffer length `%s' out of range", d.buf);
+ rc = -1; goto end;
#undef f_range
}
static void dump_buffer(const union tvec_regval *rv,
const struct tvec_regdef *rd,
- struct tvec_state *tv, unsigned style)
+ unsigned style,
+ const struct gprintf_ops *gops, void *go)
{
const char *unit;
unsigned long u = rv->bytes.sz;
if (!u || u%1024)
- tvec_write(tv, "%lu B", u);
+ gprintf(gops, go, "%lu B", u);
else {
for (unit = units, u /= 1024; !(u%1024) && unit[1]; u /= 1024, unit++);
- tvec_write(tv, "%lu %cB", u, *unit);
+ gprintf(gops, go, "%lu %cB", u, *unit);
}
}
const struct tvec_regty tvty_buffer = {
- init_bytes, release_bytes, eq_buffer, measure_int,
+ init_bytes, release_bytes, eq_buffer,
tobuf_buffer, frombuf_buffer,
parse_buffer, dump_buffer
};