X-Git-Url: https://git.distorted.org.uk/~mdw/mLib/blobdiff_plain/b64eb60f6c1fdb12f3922e04913e137199838807..c5e0e40378b7e209521d2e9a52f055575a948313:/test/tvec-types.c diff --git a/test/tvec-types.c b/test/tvec-types.c index a5d57db..8be8ae2 100644 --- a/test/tvec-types.c +++ b/test/tvec-types.c @@ -30,7 +30,9 @@ #include #include #include +#include #include +#include #include #include @@ -44,6 +46,18 @@ /*----- 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; @@ -92,27 +106,33 @@ static int hex_width(unsigned long u) 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; @@ -120,29 +140,231 @@ static void parse_signed(long *i_out, const char *p, 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) @@ -153,74 +375,81 @@ 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: @@ -231,8 +460,44 @@ static void read_quoted_string(dstr *d, int quote, struct tvec_state *tv) 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 }; @@ -247,15 +512,16 @@ static int collect_bare(dstr *d, struct tvec_state *tv) 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; @@ -273,10 +539,9 @@ static int collect_bare(dstr *d, struct tvec_state *tv) 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, @@ -300,21 +565,21 @@ 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"); @@ -322,18 +587,24 @@ static void read_compound_string(void **p_inout, size_t *sz_inout, 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: @@ -349,7 +620,10 @@ done: 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 ----------------------------------------------------------*/ @@ -358,20 +632,18 @@ static void init_...(union tvec_regval *rv, const struct tvec_regdef *rd) 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_... }; @@ -396,10 +668,6 @@ static int eq_uint(const union tvec_regval *rv0, 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)); } @@ -410,59 +678,81 @@ static int tobuf_uint(buf *b, const union tvec_regval *rv, 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 }; @@ -477,7 +767,7 @@ const struct tvec_irange 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 }; @@ -507,6 +797,77 @@ int tvec_claimeq_uint(struct tvec_state *tv, 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) @@ -527,12 +888,29 @@ static int eq_enum(const union tvec_regval *rv0, 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(); } } @@ -541,17 +919,29 @@ static int tobuf_enum(buf *b, const union tvec_regval *rv, 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(); } @@ -562,76 +952,111 @@ static int frombuf_enum(buf *b, union tvec_regval *rv, 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 @@ -643,7 +1068,8 @@ static void dump_enum(const union tvec_regval *rv, 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; @@ -651,59 +1077,86 @@ static void dump_enum(const union tvec_regval *rv, #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); \ @@ -711,45 +1164,60 @@ const struct tvec_regty tvty_enum = { } #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; @@ -758,18 +1226,18 @@ static void dump_flags(const union tvec_regval *rv, 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 }; @@ -785,6 +1253,107 @@ int tvec_claimeq_flags(struct tvec_state *tv, 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) @@ -831,14 +1400,6 @@ static int eq_bytes(const union tvec_regval *rv0, 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)); } @@ -869,87 +1430,69 @@ static int frombuf_bytes(buf *b, union tvec_regval *rv, 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 @@ -957,7 +1500,8 @@ quote: 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; @@ -965,16 +1509,16 @@ static void dump_bytes(const union tvec_regval *rv, 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) { @@ -982,27 +1526,27 @@ static void dump_bytes(const union tvec_regval *rv, 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 }; @@ -1064,19 +1608,20 @@ static int frombuf_buffer(buf *b, union tvec_regval *rv, 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; @@ -1097,38 +1642,43 @@ static void parse_buffer(union tvec_regval *rv, } } 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 };