3 * Types for the test-vector framework
5 * (c) 2023 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
12 * mLib is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU Library General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or (at
15 * your option) any later version.
17 * mLib is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
20 * License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
28 /*----- Header files ------------------------------------------------------*/
47 /*----- Preliminary utilities ---------------------------------------------*/
50 # define NANP(x) isnan(x)
52 # define NANP(x) (!((x) == (x)))
56 # define INFP(x) isinf(x)
58 # define INFP(x) ((x) > DBL_MAX || (x) < DBL_MIN)
62 # define NEGP(x) signbit(x)
64 # define NEGP(x) ((x) < 0.0)
67 static void trivial_release(union tvec_regval
*rv
,
68 const struct tvec_regdef
*rd
)
71 static int signed_to_buf(buf
*b
, long i
)
77 if (i
>= 0) ASSIGN64(k
, u
);
78 else { ASSIGN64(k
, ~u
); CPL64(k
, k
); }
79 return (buf_putk64l(b
, k
));
82 static int signed_from_buf(buf
*b
, long *i_out
)
84 kludge64 k
, lmax
, not_lmin
;
86 ASSIGN64(lmax
, LONG_MAX
); ASSIGN64(not_lmin
, ~(unsigned long)LONG_MIN
);
87 if (buf_getk64l(b
, &k
)) return (-1);
88 if (CMP64(k
, <=, lmax
)) *i_out
= (long)GET64(unsigned long, k
);
91 if (CMP64(k
, <=, not_lmin
)) *i_out
= -(long)GET64(unsigned long, k
) - 1;
97 static int unsigned_to_buf(buf
*b
, unsigned long u
)
98 { kludge64 k
; ASSIGN64(k
, u
); return (buf_putk64l(b
, k
)); }
100 static int unsigned_from_buf(buf
*b
, unsigned long *u_out
)
104 ASSIGN64(ulmax
, ULONG_MAX
);
105 if (buf_getk64l(b
, &k
)) return (-1);
106 if (CMP64(k
, >, ulmax
)) return (-1);
107 *u_out
= GET64(unsigned long, k
); return (0);
110 static int hex_width(unsigned long u
)
115 for (t
= u
>> 4, wd
= 4; t
>>= wd
, wd
*= 2, t
; );
119 static int check_signed_range(long i
,
120 const struct tvec_irange
*ir
,
121 struct tvec_state
*tv
)
123 if (ir
&& (ir
->min
> i
|| i
> ir
->max
)) {
124 tvec_error(tv
, "integer %ld out of range (must be in [%ld .. %ld])",
125 i
, ir
->min
, ir
->max
);
131 static int check_unsigned_range(unsigned long u
,
132 const struct tvec_urange
*ur
,
133 struct tvec_state
*tv
)
135 if (ur
&& (ur
->min
> u
|| u
> ur
->max
)) {
136 tvec_error(tv
, "integer %lu out of range (must be in [%lu .. %lu])",
137 u
, ur
->min
, ur
->max
);
143 static int chtodig(int ch
)
145 if ('0' <= ch
&& ch
<= '9') return (ch
- '0');
146 else if ('a' <= ch
&& ch
<= 'z') return (ch
- 'a' + 10);
147 else if ('A' <= ch
&& ch
<= 'Z') return (ch
- 'A' + 10);
151 static int parse_unsigned_integer(unsigned long *u_out
, const char **q_out
,
158 #define f_implicit 1u
162 if (p
[0] != '0' || !p
[1]) {
163 d
= chtodig(*p
); if (0 > d
|| d
>= 10) return (-1);
164 r
= 10; u
= d
; p
++; f
|= f_implicit
| f_digit
;
166 u
= 0; d
= chtodig(p
[2]);
167 if (d
< 0) { r
= 10; f
|= f_implicit
| f_digit
; p
++; }
168 else if ((p
[1] == 'x' || p
[1] == 'X') && d
< 16) { r
= 16; p
+= 2; }
169 else if ((p
[1] == 'o' || p
[1] == 'O') && d
< 8) { r
= 8; p
+= 2; }
170 else if ((p
[1] == 'b' || p
[1] == 'B') && d
< 2) { r
= 2; p
+= 2; }
171 else { r
= 10; f
|= f_digit
; p
++; }
178 if (f
&f_uscore
) break;
179 p
++; f
= (f
&~f_implicit
) | f_uscore
;
181 else if (ch
== 'r' || ch
== 'R') {
182 if (!(f
&f_implicit
) || !u
|| u
>= 36) break;
183 d
= chtodig(p
[1]); if (0 > d
|| d
>= u
) break;
184 r
= u
; u
= d
; f
= (f
&~f_implicit
) | f_digit
; p
+= 2; q
= p
;
187 if (d
< 0 || d
>= r
) break;
188 if (u
> ULONG_MAX
/r
) return (-1);
189 u
*= r
; if (u
> ULONG_MAX
- d
) return (-1);
190 u
+= d
; f
= (f
&~f_uscore
) | f_digit
; p
++; q
= p
;
194 if (!(f
&f_digit
)) return (-1);
195 *u_out
= u
; *q_out
= q
; return (0);
202 static int parse_signed_integer(long *i_out
, const char **q_out
,
210 else if (*p
== '-') { f
|= f_neg
; p
++; }
212 if (parse_unsigned_integer(&u
, q_out
, p
)) return (-1);
215 if (u
> LONG_MAX
) return (-1);
218 if (u
&& u
- 1 > -(LONG_MIN
+ 1)) return (-1);
219 *i_out
= u ?
-(long)(u
- 1) - 1 : 0;
227 static int parse_signed(long *i_out
, const char *p
,
228 const struct tvec_irange
*ir
,
229 struct tvec_state
*tv
)
234 if (parse_signed_integer(&i
, &q
, p
))
235 return (tvec_error(tv
, "invalid signed integer `%s'", p
));
236 if (*q
) return (tvec_syntax(tv
, *q
, "end-of-line"));
237 if (check_signed_range(i
, ir
, tv
)) return (-1);
238 *i_out
= i
; return (0);
241 static int parse_unsigned(unsigned long *u_out
, const char *p
,
242 const struct tvec_urange
*ur
,
243 struct tvec_state
*tv
)
248 if (parse_unsigned_integer(&u
, &q
, p
))
249 return (tvec_error(tv
, "invalid unsigned integer `%s'", p
));
250 if (*q
) return (tvec_syntax(tv
, *q
, "end-of-line"));
251 if (check_unsigned_range(u
, ur
, tv
)) return (-1);
252 *u_out
= u
; return (0);
255 static void format_signed_hex(const struct gprintf_ops
*gops
, void *go
,
258 unsigned long u
= i
>= 0 ? i
: -(unsigned long)i
;
259 gprintf(gops
, go
, "%s0x%0*lx", i
< 0 ?
"-" : "", hex_width(u
), u
);
262 static void format_unsigned_hex(const struct gprintf_ops
*gops
, void *go
,
264 { gprintf(gops
, go
, "0x%0*lx", hex_width(u
), u
); }
266 static void format_floating(const struct gprintf_ops
*gops
, void *go
,
272 gprintf(gops
, go
, "#nan");
274 gprintf(gops
, go
, x
> 0 ?
"#+inf" : "#-inf");
276 /* Ugh. C doesn't provide any function for just printing a
277 * floating-point number /correctly/, i.e., so that you can read the
278 * result back and recover the number you first thought of. There are
279 * complicated algorithms published for doing this, but I really don't
280 * want to get into that here. So we have this.
282 * The sign doesn't cause significant difficulty so we're going to ignore
283 * it for now. So suppose we're given a number %$x = f b^e$%, in
284 * base-%$b$% format, so %$f b^n$% and %$e$% are integers, with
285 * %$0 \le f < 1$%. We're going to convert it into the nearest integer
286 * of the form %$X = F B^E$%, with similar conditions, only with the
287 * additional requirement that %$X$% is normalized, i.e., that %$X = 0$%
288 * or %$F \ge B^{-N}$%.
290 * We're rounding to the nearest such %$X$%. If there is to be ambiguity
291 * in the conversion, then some %$x = f b^e$% and the next smallest
292 * representable number %$x' = x + b^{e-n}$% must both map to the same
293 * %$X$%, which means both %$x$% and %$x'$% must be nearer to %$X$% than
294 * any other number representable in the target system. The nest larger
295 * number is %$X' = X + B^{E-N}$%; the next smaller number will normally
296 * be %$W = X - B^{E-N}$%, but if %$F = 1/B$ then the next smaller number
297 * is actually %$X - B^{E-N-1}$%. We ignore this latter possibility in
298 * the pursuit of a conservative estimate (though actually it doesn't
301 * If both %$x$% and %$x'$% map to %$X$% then we must have
302 * %$L = X - B^{E-N}/2 \le x$% and %$x + b^{e-n} \le R = X + B^{E-N}/2$%;
303 * so firstly %$f b^e = x \ge L = W + B^{E-N}/2 > W = (F - B^{-N}) B^E$%,
304 * and secondly %$b^{e-n} \le B^{E-N}$%. Since these inequalities are in
305 * opposite senses, we can divide, giving
307 * %$f b^e/b^{e-n} > (F - B^{-N}) B^E/B^{E-N}$% ,
311 * %$f b^n > (F - B^{-N}) B^N = F B^N - 1$% .
313 * Now %$f \le 1 - b^{-n}$%, and %$F \ge B^{-1}$%, so, for this to be
314 * possible, it must be the case that
316 * %$(1 - b^{-n}) b^n = b^n - 1 > B^{N-1} - 1$% .
318 * Then rearrange and take logarithms, obtaining
320 * %$(N - 1) \log B < n \log b$% ,
324 * %$N < n \log b/\log B + 1$% .
326 * Recall that this is a necessary condition for a collision to occur; we
327 * are therefore safe whenever
329 * %$N \ge n \log b/\log B + 1$% ;
331 * so, taking ceilings,
333 * %$N \ge \lceil n \log b/\log B \rceil + 1$% .
335 * So that's why we have this.
337 * I'm going to assume that @n = DBL_MANT_DIG@ is sufficiently small that
338 * we can calculate this without ending up on the wrong side of an
341 * In C11, we have @DBL_DECIMAL_DIG@, which should be the same value only
342 * as a constant. Except that modern compilers are more than clever
343 * enough to work out that this is a constant anyway.
345 * This is sometimes an overestimate: we'll print out meaningless digits
346 * that don't represent anything we actually know about the number in
347 * question. To fix that, we'd need a complicated algorithm like Steele
348 * and White's Dragon4, Gay's @dtoa@, or Burger and Dybvig's algorithm
349 * (note that Loitsch's Grisu2 is conservative, and Grisu3 hands off to
350 * something else in difficult situations).
353 prec
= ceil(DBL_MANT_DIG
*log(FLT_RADIX
)/log(10)) + 1;
354 gprintf(gops
, go
, "%.*g", prec
, x
);
358 static int eqish_floating_p(double x
, double y
,
359 const struct tvec_floatinfo
*fi
)
363 if (NANP(x
)) return (NANP(y
)); else if (NANP(y
)) return (0);
364 if (INFP(x
)) return (x
== y
); else if (INFP(y
)) return (0);
366 switch (fi ? fi
->f
&TVFF_EQMASK
: TVFF_EXACT
) {
368 return (x
== y
&& NEGP(x
) == NEGP(y
));
370 t
= x
- y
; if (t
< 0) t
= -t
; return (t
< fi
->delta
);
372 t
= 1.0 - y
/x
; if (t
< 0) t
= -t
; return (t
< fi
->delta
);
378 static int parse_floating(double *x_out
, const char *p
,
379 const struct tvec_floatinfo
*fi
,
380 struct tvec_state
*tv
)
382 const char *pp
; char *q
;
387 if (STRCMP(p
, ==, "#nan")) {
391 tvec_error(tv
, "NaN not supported on this system");
394 } else if (STRCMP(p
, ==, "#inf") ||
395 STRCMP(p
, ==, "#+inf") ||
396 STRCMP(p
, ==, "+#inf")) {
398 x
= INFINITY
; rc
= 0;
400 tvec_error(tv
, "infinity not supported on this system");
403 } else if (STRCMP(p
, ==, "#-inf") ||
404 STRCMP(p
, ==, "-#inf")) {
406 x
= -INFINITY
; rc
= 0;
408 tvec_error(tv
, "infinity not supported on this system");
413 if (*pp
== '+' || *pp
== '-') pp
++;
414 if (*pp
== '.') pp
++;
416 tvec_syntax(tv
, *p ?
*p
: fgetc(tv
->fp
), "floating-point number");
419 olderr
= errno
; errno
= 0;
422 tvec_syntax(tv
, *q
, "end-of-line");
425 if (errno
&& (errno
!= ERANGE
|| (x
> 0 ?
-x
: x
) == HUGE_VAL
)) {
426 tvec_error(tv
, "invalid floating-point number `%s': %s",
433 if (NANP(x
) && fi
&& !(fi
->f
&TVFF_NANOK
)) {
434 tvec_error(tv
, "#nan not allowed here");
437 if (fi
&& ((!(fi
->f
&TVFF_NOMIN
) && x
< fi
->min
) ||
438 (!(fi
->f
&TVFF_NOMAX
) && x
> fi
->max
))) {
439 dstr_puts(&d
, "floating-point number ");
440 format_floating(&dstr_printops
, &d
, x
);
441 dstr_puts(&d
, " out of range (must be in ");
442 if (fi
->f
&TVFF_NOMIN
)
443 dstr_puts(&d
, "(#-inf");
445 { dstr_putc(&d
, '['); format_floating(&dstr_printops
, &d
, fi
->min
); }
446 dstr_puts(&d
, " .. ");
447 if (fi
->f
&TVFF_NOMAX
)
448 dstr_puts(&d
, "#+inf)");
450 { format_floating(&dstr_printops
, &d
, fi
->max
); dstr_putc(&d
, ']'); }
451 dstr_putc(&d
, ')'); dstr_putz(&d
);
452 tvec_error(tv
, "%s", d
.buf
); rc
= -1; goto end
;
461 static int convert_hex(char ch
, int *v_out
)
463 if ('0' <= ch
&& ch
<= '9') { *v_out
= ch
- '0'; return (0); }
464 else if ('a' <= ch
&& ch
<= 'f') { *v_out
= ch
- 'a' + 10; return (0); }
465 else if ('A' <= ch
&& ch
<= 'F') { *v_out
= ch
- 'A' + 10; return (0); }
469 static int read_escape(int *ch_out
, struct tvec_state
*tv
)
477 case EOF
: case '\n': tvec_syntax(tv
, ch
, "string escape");
478 case '\'': *ch_out
= '\''; break;
479 case '\\': *ch_out
= '\\'; break;
480 case '"': *ch_out
= '"'; break;
481 case 'a': *ch_out
= '\a'; break;
482 case 'b': *ch_out
= '\b'; break;
483 case 'e': *ch_out
= '\x1b'; break;
484 case 'f': *ch_out
= '\f'; break;
485 case 'n': *ch_out
= '\n'; break;
486 case 'r': *ch_out
= '\r'; break;
487 case 't': *ch_out
= '\t'; break;
488 case 'v': *ch_out
= '\v'; break;
492 if (ch
== '{') { f
|= f_brace
; ch
= getc(tv
->fp
); }
494 if (convert_hex(ch
, &esc
))
495 return (tvec_syntax(tv
, ch
, "hex digit"));
497 ch
= getc(tv
->fp
); if (convert_hex(ch
, &i
)) break;
500 return (tvec_error(tv
,
501 "character code %d out of range", esc
));
503 if (!(f
&f_brace
)) ungetc(ch
, tv
->fp
);
504 else if (ch
!= '}') return (tvec_syntax(tv
, ch
, "`}'"));
509 if ('0' <= ch
&& ch
< '8') {
510 i
= 1; esc
= ch
- '0';
513 if ('0' > ch
|| ch
>= '8') { ungetc(ch
, tv
->fp
); break; }
514 esc
= 8*esc
+ ch
- '0';
515 i
++; if (i
>= 3) break;
518 return (tvec_error(tv
,
519 "character code %d out of range", esc
));
522 return (tvec_syntax(tv
, ch
, "string escape"));
530 static int read_quoted_string(dstr
*d
, int quote
, struct tvec_state
*tv
)
538 return (tvec_syntax(tv
, ch
, "`%c'", quote
));
540 if (quote
== '\'') goto ordinary
;
541 ch
= getc(tv
->fp
); if (ch
== '\n') { tv
->lno
++; break; }
542 ungetc(ch
, tv
->fp
); if (read_escape(&ch
, tv
)) return (-1);
545 if (ch
== quote
) goto end
;
558 static void format_charesc(const struct gprintf_ops
*gops
, void *go
,
562 case '\a': gprintf(gops
, go
, "\\a"); break;
563 case '\b': gprintf(gops
, go
, "\\b"); break;
564 case '\x1b': gprintf(gops
, go
, "\\e"); break;
565 case '\f': gprintf(gops
, go
, "\\f"); break;
566 case '\r': gprintf(gops
, go
, "\\r"); break;
567 case '\n': gprintf(gops
, go
, "\\n"); break;
568 case '\t': gprintf(gops
, go
, "\\t"); break;
569 case '\v': gprintf(gops
, go
, "\\v"); break;
570 case '\'': gprintf(gops
, go
, "\\'"); break;
571 case '"': gprintf(gops
, go
, "\\\""); break;
574 gprintf(gops
, go
, "\\x{%0*x}", hex_width(UCHAR_MAX
), ch
);
576 gprintf(gops
, go
, "\\x%0*x", hex_width(UCHAR_MAX
), ch
);
581 static void format_char(const struct gprintf_ops
*gops
, void *go
, int ch
)
584 gprintf(gops
, go
, "#eof");
585 else if (isprint(ch
) && ch
!= '\'')
586 gprintf(gops
, go
, "'%c'", ch
);
588 gprintf(gops
, go
, "'");
589 format_charesc(gops
, go
, ch
, 0);
590 gprintf(gops
, go
, "'");
594 static void maybe_format_signed_char
595 (const struct gprintf_ops
*gops
, void *go
, long i
)
597 if (i
== EOF
|| (0 <= i
&& i
< UCHAR_MAX
))
598 { gprintf(gops
, go
, " = "); format_char(gops
, go
, i
); }
601 static void maybe_format_unsigned_char
602 (const struct gprintf_ops
*gops
, void *go
, unsigned long u
)
605 { gprintf(gops
, go
, " = "); format_char(gops
, go
, u
); }
608 enum { TVCODE_BARE
, TVCODE_HEX
, TVCODE_BASE64
, TVCODE_BASE32
};
610 static int collect_bare(dstr
*d
, struct tvec_state
*tv
)
613 enum { WORD
, SPACE
, ESCAPE
}; unsigned s
= WORD
;
620 tvec_syntax(tv
, ch
, "bareword");
623 if (s
== ESCAPE
) { tv
->lno
++; goto addch
; }
624 if (s
== WORD
) pos
= d
->len
;
625 ungetc(ch
, tv
->fp
); if (tvec_nexttoken(tv
)) { rc
= -1; goto end
; }
626 DPUTC(d
, ' '); s
= SPACE
;
628 case '"': case '\'': case '!':
629 if (s
== SPACE
) { ungetc(ch
, tv
->fp
); goto done
; }
635 if (s
!= ESCAPE
&& isspace(ch
)) {
636 if (s
== WORD
) pos
= d
->len
;
637 DPUTC(d
, ch
); s
= SPACE
;
641 DPUTC(d
, ch
); s
= WORD
;
646 if (s
== SPACE
) d
->len
= pos
;
652 static void set_up_encoding(const codec_class
**ccl_out
, unsigned *f_out
,
657 *ccl_out
= 0; *f_out
= 0;
660 *ccl_out
= &hex_class
; *f_out
= CDCF_IGNCASE
;
663 *ccl_out
= &base32_class
; *f_out
= CDCF_IGNCASE
| CDCF_IGNEQPAD
;
666 *ccl_out
= &base64_class
; *f_out
= CDCF_IGNEQPAD
;
673 static int read_compound_string(void **p_inout
, size_t *sz_inout
,
674 unsigned code
, struct tvec_state
*tv
)
676 const codec_class
*ccl
; unsigned f
;
678 dstr d
= DSTR_INIT
, w
= DSTR_INIT
;
682 set_up_encoding(&ccl
, &f
, code
);
683 if (tvec_nexttoken(tv
)) tvec_syntax(tv
, fgetc(tv
->fp
), "string");
686 if (ch
== '"' || ch
== '\'')
687 { if (read_quoted_string(&d
, ch
, tv
)) { rc
= -1; goto end
; } }
688 else if (ch
== '!') {
690 DRESET(&w
); tvec_readword(tv
, &w
, ";", "`!'-keyword");
691 if (STRCMP(w
.buf
, ==, "!bare")) code
= TVCODE_BARE
;
692 else if (STRCMP(w
.buf
, ==, "!hex")) code
= TVCODE_HEX
;
693 else if (STRCMP(w
.buf
, ==, "!base32")) code
= TVCODE_BASE32
;
694 else if (STRCMP(w
.buf
, ==, "!base64")) code
= TVCODE_BASE64
;
696 tvec_error(tv
, "unknown string keyword `%s'", w
.buf
);
699 set_up_encoding(&ccl
, &f
, code
);
703 if (tvec_readword(tv
, &w
, ";", "%s-encoded fragment", ccl
->name
))
704 { rc
= -1; goto end
; }
705 cdc
= ccl
->decoder(f
);
706 err
= cdc
->ops
->code(cdc
, w
.buf
, w
.len
, &d
);
707 if (!err
) err
= cdc
->ops
->code(cdc
, 0, 0, &d
);
709 tvec_error(tv
, "invalid %s fragment `%s': %s",
710 ccl
->name
, w
.buf
, codec_strerror(err
));
713 cdc
->ops
->destroy(cdc
);
714 } else switch (code
) {
717 if (collect_bare(&d
, tv
)) goto done
;
722 } while (!tvec_nexttoken(tv
));
725 if (*sz_inout
<= d
.len
)
726 { xfree(*p_inout
); *p_inout
= xmalloc(d
.len
+ 1); }
727 p
= *p_inout
; memcpy(p
, d
.buf
, d
.len
); p
[d
.len
] = 0; *sz_inout
= d
.len
;
730 dstr_destroy(&d
); dstr_destroy(&w
);
734 /*----- Skeleton ----------------------------------------------------------*/
736 static void init_...(union tvec_regval *rv, const struct tvec_regdef *rd)
737 static void release_...(union tvec_regval *rv, const struct tvec_regdef *rd)
738 static int eq_...(const union tvec_regval *rv0, const union tvec_regval *rv1,
739 const struct tvec_regdef *rd)
740 static int tobuf_...(buf *b, const union tvec_regval *rv,
741 const struct tvec_regdef *rd)
742 static int frombuf_...(buf *b, union tvec_regval *rv,
743 const struct tvec_regdef *rd)
744 static int parse_...(union tvec_regval *rv, const struct tvec_regdef *rd,
745 struct tvec_state *tv)
746 static void dump_...(const union tvec_regval *rv,
747 const struct tvec_regdef *rd,
748 struct tvec_state *tv, unsigned style)
750 const struct tvec_regty tvty_... = {
751 init_..., release_..., eq_...,
752 tobuf_..., frombuf_...,
756 /*----- Signed and unsigned integer types ---------------------------------*/
758 static void init_int(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
761 static void init_uint(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
764 static int eq_int(const union tvec_regval
*rv0
, const union tvec_regval
*rv1
,
765 const struct tvec_regdef
*rd
)
766 { return (rv0
->i
== rv1
->i
); }
768 static int eq_uint(const union tvec_regval
*rv0
,
769 const union tvec_regval
*rv1
,
770 const struct tvec_regdef
*rd
)
771 { return (rv0
->u
== rv1
->u
); }
773 static int tobuf_int(buf
*b
, const union tvec_regval
*rv
,
774 const struct tvec_regdef
*rd
)
775 { return (signed_to_buf(b
, rv
->i
)); }
777 static int tobuf_uint(buf
*b
, const union tvec_regval
*rv
,
778 const struct tvec_regdef
*rd
)
779 { return (unsigned_to_buf(b
, rv
->u
)); }
781 static int frombuf_int(buf
*b
, union tvec_regval
*rv
,
782 const struct tvec_regdef
*rd
)
783 { return (signed_from_buf(b
, &rv
->i
)); }
785 static int frombuf_uint(buf
*b
, union tvec_regval
*rv
,
786 const struct tvec_regdef
*rd
)
787 { return (unsigned_from_buf(b
, &rv
->u
)); }
789 static int parse_int(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
790 struct tvec_state
*tv
)
795 if (tvec_readword(tv
, &d
, ";", "signed integer"))
796 { rc
= -1; goto end
; }
797 if (parse_signed(&rv
->i
, d
.buf
, rd
->arg
.p
, tv
))
798 { rc
= -1; goto end
; }
799 if (tvec_flushtoeol(tv
, 0))
800 { rc
= -1; goto end
; }
807 static int parse_uint(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
808 struct tvec_state
*tv
)
813 if (tvec_readword(tv
, &d
, ";", "unsigned integer"))
814 { rc
= -1; goto end
; }
815 if (parse_unsigned(&rv
->u
, d
.buf
, rd
->arg
.p
, tv
))
816 { rc
= -1; goto end
; }
817 if (tvec_flushtoeol(tv
, 0))
818 { rc
= -1; goto end
; }
825 static void dump_int(const union tvec_regval
*rv
,
826 const struct tvec_regdef
*rd
,
828 const struct gprintf_ops
*gops
, void *go
)
831 gprintf(gops
, go
, "%ld", rv
->i
);
832 if (!(style
&TVSF_COMPACT
)) {
833 gprintf(gops
, go
, " ; = ");
834 format_signed_hex(gops
, go
, rv
->i
);
835 maybe_format_signed_char(gops
, go
, rv
->i
);
839 static void dump_uint(const union tvec_regval
*rv
,
840 const struct tvec_regdef
*rd
,
842 const struct gprintf_ops
*gops
, void *go
)
844 gprintf(gops
, go
, "%lu", rv
->u
);
845 if (!(style
&TVSF_COMPACT
)) {
846 gprintf(gops
, go
, " ; = ");
847 format_unsigned_hex(gops
, go
, rv
->u
);
848 maybe_format_unsigned_char(gops
, go
, rv
->u
);
852 const struct tvec_regty tvty_int
= {
853 init_int
, trivial_release
, eq_int
,
854 tobuf_int
, frombuf_int
,
858 const struct tvec_irange
859 tvrange_schar
= { SCHAR_MIN
, SCHAR_MAX
},
860 tvrange_short
= { SHRT_MIN
, SHRT_MAX
},
861 tvrange_int
= { INT_MIN
, INT_MAX
},
862 tvrange_long
= { LONG_MIN
, LONG_MAX
},
863 tvrange_sbyte
= { -128, 127 },
864 tvrange_i16
= { -32768, +32767 },
865 tvrange_i32
= { -2147483648, 2147483647 };
867 const struct tvec_regty tvty_uint
= {
868 init_uint
, trivial_release
, eq_uint
,
869 tobuf_uint
, frombuf_uint
,
870 parse_uint
, dump_uint
873 const struct tvec_urange
874 tvrange_uchar
= { 0, UCHAR_MAX
},
875 tvrange_ushort
= { 0, USHRT_MAX
},
876 tvrange_uint
= { 0, UINT_MAX
},
877 tvrange_ulong
= { 0, ULONG_MAX
},
878 tvrange_size
= { 0, (size_t)-1 },
879 tvrange_byte
= { 0, 255 },
880 tvrange_u16
= { 0, 65535 },
881 tvrange_u32
= { 0, 4294967296 };
883 int tvec_claimeq_int(struct tvec_state
*tv
, long i0
, long i1
,
884 const char *file
, unsigned lno
, const char *expr
)
886 tv
->out
[0].v
.i
= i0
; tv
->in
[0].v
.i
= i1
;
887 return (tvec_claimeq(tv
, &tvty_int
, 0, file
, lno
, expr
));
890 int tvec_claimeq_uint(struct tvec_state
*tv
,
891 unsigned long u0
, unsigned long u1
,
892 const char *file
, unsigned lno
, const char *expr
)
894 tv
->out
[0].v
.u
= u0
; tv
->in
[0].v
.u
= u1
;
895 return (tvec_claimeq(tv
, &tvty_uint
, 0, file
, lno
, expr
));
898 /*----- Floating-point type -----------------------------------------------*/
900 static void init_float(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
903 static int eq_float(const union tvec_regval
*rv0
,
904 const union tvec_regval
*rv1
,
905 const struct tvec_regdef
*rd
)
906 { return (eqish_floating_p(rv0
->f
, rv1
->f
, rd
->arg
.p
)); }
908 static int tobuf_float(buf
*b
, const union tvec_regval
*rv
,
909 const struct tvec_regdef
*rd
)
910 { return (buf_putf64l(b
, rv
->f
)); }
911 static int frombuf_float(buf
*b
, union tvec_regval
*rv
,
912 const struct tvec_regdef
*rd
)
913 { return (buf_getf64l(b
, &rv
->f
)); }
915 static int parse_float(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
916 struct tvec_state
*tv
)
921 if (tvec_readword(tv
, &d
, ";", "floating-point number"))
922 { rc
= -1; goto end
; }
923 if (parse_floating(&rv
->f
, d
.buf
, rd
->arg
.p
, tv
))
924 { rc
= -1; goto end
; }
925 if (tvec_flushtoeol(tv
, 0))
926 { rc
= -1; goto end
; }
933 static void dump_float(const union tvec_regval
*rv
,
934 const struct tvec_regdef
*rd
,
936 const struct gprintf_ops
*gops
, void *go
)
937 { format_floating(gops
, go
, rv
->f
); }
939 const struct tvec_regty tvty_float
= {
940 init_float
, trivial_release
, eq_float
,
941 tobuf_float
, frombuf_float
,
942 parse_float
, dump_float
945 int tvec_claimeq_float(struct tvec_state
*tv
,
946 double f0
, double f1
,
947 const char *file
, unsigned lno
,
950 return (tvec_claimeqish_float(tv
, f0
, f1
, TVFF_EXACT
, 0.0,
953 int tvec_claimeqish_float(struct tvec_state
*tv
,
954 double f0
, double f1
, unsigned f
, double delta
,
955 const char *file
, unsigned lno
,
958 struct tvec_floatinfo fi
;
961 fi
.f
= f
; fi
.min
= fi
.max
= 0.0; fi
.delta
= delta
; arg
.p
= &fi
;
962 tv
->out
[0].v
.f
= f0
; tv
->in
[0].v
.f
= f1
;
963 return (tvec_claimeq(tv
, &tvty_float
, &arg
, file
, lno
, expr
));
966 /*----- Enumerations ------------------------------------------------------*/
968 #define init_ienum init_int
969 #define init_uenum init_uint
970 #define init_fenum init_float
971 static void init_penum(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
974 #define eq_ienum eq_int
975 #define eq_uenum eq_uint
976 static int eq_fenum(const union tvec_regval
*rv0
,
977 const union tvec_regval
*rv1
,
978 const struct tvec_regdef
*rd
)
980 const struct tvec_fenuminfo
*ei
= rd
->arg
.p
;
981 return (eqish_floating_p(rv0
->f
, rv1
->f
, ei
->fi
));
983 static int eq_penum(const union tvec_regval
*rv0
,
984 const union tvec_regval
*rv1
,
985 const struct tvec_regdef
*rd
)
986 { return (rv0
->p
== rv1
->p
); }
988 #define tobuf_ienum tobuf_int
989 #define tobuf_uenum tobuf_uint
990 #define tobuf_fenum tobuf_float
991 static int tobuf_penum(buf
*b
, const union tvec_regval
*rv
,
992 const struct tvec_regdef
*rd
)
994 const struct tvec_penuminfo
*pei
= rd
->arg
.p
;
995 const struct tvec_passoc
*pa
;
998 for (pa
= pei
->av
, i
= 0; pa
->tag
; pa
++, i
++)
999 if (pa
->p
== rv
->p
) goto found
;
1003 return (signed_to_buf(b
, i
));
1006 #define frombuf_ienum frombuf_int
1007 #define frombuf_uenum frombuf_uint
1008 #define frombuf_fenum frombuf_float
1009 static int frombuf_penum(buf
*b
, union tvec_regval
*rv
,
1010 const struct tvec_regdef
*rd
)
1012 const struct tvec_penuminfo
*pei
= rd
->arg
.p
;
1013 const struct tvec_passoc
*pa
;
1016 for (pa
= pei
->av
, n
= 0; pa
->tag
; pa
++, n
++);
1017 if (signed_from_buf(b
, &i
)) return (-1);
1018 if (0 <= i
&& i
< n
) rv
->p
= (/*unconst*/ void *)pei
->av
[i
].p
;
1019 else if (i
== -1) rv
->p
= 0;
1024 #define DEFPARSE_ENUM(tag_, ty, slot) \
1025 static int parse_##slot##enum(union tvec_regval *rv, \
1026 const struct tvec_regdef *rd, \
1027 struct tvec_state *tv) \
1029 const struct tvec_##slot##enuminfo *ei = rd->arg.p; \
1030 const struct tvec_##slot##assoc *a; \
1031 dstr d = DSTR_INIT; \
1034 if (tvec_readword(tv, &d, ";", "enumeration tag or " LITSTR_##tag_)) \
1035 { rc = -1; goto end; } \
1036 for (a = ei->av; a->tag; a++) \
1037 if (STRCMP(a->tag, ==, d.buf)) { FOUND_##tag_ goto done; } \
1040 if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } \
1047 #define LITSTR_INT "literal signed integer"
1048 #define FOUND_INT rv->i = a->i;
1049 #define MISSING_INT if (parse_signed(&rv->i, d.buf, ei->ir, tv)) \
1050 { rc = -1; goto end; }
1052 #define LITSTR_UINT "literal unsigned integer"
1053 #define FOUND_UINT rv->u = a->u;
1054 #define MISSING_UINT if (parse_unsigned(&rv->u, d.buf, ei->ur, tv)) \
1055 { rc = -1; goto end; }
1057 #define LITSTR_FLT "literal floating-point number, " \
1058 "`#-inf', `#+inf', or `#nan'"
1059 #define FOUND_FLT rv->f = a->f;
1060 #define MISSING_FLT if (parse_floating(&rv->f, d.buf, ei->fi, tv)) \
1061 { rc = -1; goto end; }
1063 #define LITSTR_PTR "`#nil'"
1064 #define FOUND_PTR rv->p = (/*unconst*/ void *)a->p;
1065 #define MISSING_PTR if (STRCMP(d.buf, ==, "#nil")) \
1068 tvec_error(tv, "unknown `%s' value `%s'", \
1070 rc = -1; goto end; \
1073 TVEC_MISCSLOTS(DEFPARSE_ENUM
)
1091 #undef DEFPARSE_ENUM
1093 #define DEFDUMP_ENUM(tag_, ty, slot) \
1094 static void dump_##slot##enum(const union tvec_regval *rv, \
1095 const struct tvec_regdef *rd, \
1097 const struct gprintf_ops *gops, void *go) \
1099 const struct tvec_##slot##enuminfo *ei = rd->arg.p; \
1100 const struct tvec_##slot##assoc *a; \
1102 for (a = ei->av; a->tag; a++) \
1103 if (rv->slot == a->slot) { \
1104 gprintf(gops, go, "%s", a->tag); \
1105 if (style&TVSF_COMPACT) return; \
1106 gprintf(gops, go, " ; = "); break; \
1112 #define MAYBE_PRINT_EXTRA \
1113 if (style&TVSF_COMPACT) ; \
1114 else if (!a->tag) { gprintf(gops, go, " ; = "); goto _extra; } \
1115 else if (1) { gprintf(gops, go, " = "); goto _extra; } \
1118 #define PRINTRAW_INT gprintf(gops, go, "%ld", rv->i); \
1119 MAYBE_PRINT_EXTRA { \
1120 format_signed_hex(gops, go, rv->i); \
1121 maybe_format_signed_char(gops, go, rv->i); \
1124 #define PRINTRAW_UINT gprintf(gops, go, "%lu", rv->u); \
1125 MAYBE_PRINT_EXTRA { \
1126 format_unsigned_hex(gops, go, rv->u); \
1127 maybe_format_unsigned_char(gops, go, rv->u); \
1130 #define PRINTRAW_FLT format_floating(gops, go, rv->f);
1132 #define PRINTRAW_PTR if (!rv->p) gprintf(gops, go, "#nil"); \
1133 else gprintf(gops, go, "#<%s %p>", ei->name, rv->p);
1135 TVEC_MISCSLOTS(DEFDUMP_ENUM
)
1138 #undef PRINTRAW_UINT
1142 #undef MAYBE_PRINT_EXTRA
1145 #define DEFTY_ENUM(tag, ty, slot) \
1146 const struct tvec_regty tvty_##slot##enum = { \
1147 init_##slot##enum, trivial_release, eq_##slot##enum, \
1148 tobuf_##slot##enum, frombuf_##slot##enum, \
1149 parse_##slot##enum, dump_##slot##enum \
1151 TVEC_MISCSLOTS(DEFTY_ENUM
)
1154 static const struct tvec_iassoc bool_assoc
[] = {
1171 const struct tvec_ienuminfo tvenum_bool
=
1172 { "bool", bool_assoc
, &tvrange_int
};
1174 #define DEFCLAIM(tag, ty, slot) \
1175 int tvec_claimeq_##slot##enum \
1176 (struct tvec_state *tv, \
1177 const struct tvec_##slot##enuminfo *ei, ty e0, ty e1, \
1178 const char *file, unsigned lno, const char *expr) \
1180 union tvec_misc arg; \
1183 tv->out[0].v.slot = GET_##tag(e0); \
1184 tv->in[0].v.slot = GET_##tag(e1); \
1185 return (tvec_claimeq(tv, &tvty_##slot##enum, &arg, \
1186 file, lno, expr)); \
1188 #define GET_INT(e) (e)
1189 #define GET_UINT(e) (e)
1190 #define GET_FLT(e) (e)
1191 #define GET_PTR(e) ((/*unconst*/ void *)(e))
1192 TVEC_MISCSLOTS(DEFCLAIM
)
1199 /*----- Flag types --------------------------------------------------------*/
1201 static int parse_flags(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
1202 struct tvec_state
*tv
)
1204 const struct tvec_flaginfo
*fi
= rd
->arg
.p
;
1205 const struct tvec_flag
*f
;
1206 unsigned long m
= 0, v
= 0, t
;
1212 if (tvec_readword(tv
, &d
, "|;", "flag name or integer"))
1213 { rc
= -1; goto end
; }
1215 for (f
= fi
->fv
; f
->tag
; f
++)
1216 if (STRCMP(f
->tag
, ==, d
.buf
)) {
1218 { tvec_error(tv
, "colliding flag setting"); rc
= -1; goto end
; }
1220 { m
|= f
->m
; v
|= f
->v
; goto next
; }
1223 if (parse_unsigned(&t
, d
.buf
, fi
->range
, tv
))
1224 { rc
= -1; goto end
; }
1227 if (tvec_nexttoken(tv
)) break;
1229 if (ch
!= '|') { tvec_syntax(tv
, ch
, "`|'"); rc
= -1; goto end
; }
1230 if (tvec_nexttoken(tv
))
1231 { tvec_syntax(tv
, '\n', "flag name or integer"); rc
= -1; goto end
; }
1240 static void dump_flags(const union tvec_regval
*rv
,
1241 const struct tvec_regdef
*rd
,
1243 const struct gprintf_ops
*gops
, void *go
)
1245 const struct tvec_flaginfo
*fi
= rd
->arg
.p
;
1246 const struct tvec_flag
*f
;
1247 unsigned long m
= ~(unsigned long)0, v
= rv
->u
;
1250 for (f
= fi
->fv
, sep
= ""; f
->tag
; f
++)
1251 if ((m
&f
->m
) && (v
&f
->m
) == f
->v
) {
1252 gprintf(gops
, go
, "%s%s", sep
, f
->tag
); m
&= ~f
->m
;
1253 sep
= style
&TVSF_COMPACT ?
"|" : " | ";
1256 if (v
&m
) gprintf(gops
, go
, "%s0x%0*lx", sep
, hex_width(v
), v
&m
);
1258 if (!(style
&TVSF_COMPACT
))
1259 gprintf(gops
, go
, " ; = 0x%0*lx", hex_width(rv
->u
), rv
->u
);
1262 const struct tvec_regty tvty_flags
= {
1263 init_uint
, trivial_release
, eq_uint
,
1264 tobuf_uint
, frombuf_uint
,
1265 parse_flags
, dump_flags
1268 int tvec_claimeq_flags(struct tvec_state
*tv
,
1269 const struct tvec_flaginfo
*fi
,
1270 unsigned long f0
, unsigned long f1
,
1271 const char *file
, unsigned lno
, const char *expr
)
1273 union tvec_misc arg
;
1275 arg
.p
= fi
; tv
->out
[0].v
.u
= f0
; tv
->in
[0].v
.u
= f1
;
1276 return (tvec_claimeq(tv
, &tvty_flags
, &arg
, file
, lno
, expr
));
1279 /*----- Characters --------------------------------------------------------*/
1281 static int tobuf_char(buf
*b
, const union tvec_regval
*rv
,
1282 const struct tvec_regdef
*rd
)
1285 if (0 <= rv
->i
&& rv
->i
<= UCHAR_MAX
) u
= rv
->i
;
1286 else if (rv
->i
== EOF
) u
= MASK32
;
1288 return (buf_putu32l(b
, u
));
1291 static int frombuf_char(buf
*b
, union tvec_regval
*rv
,
1292 const struct tvec_regdef
*rd
)
1296 if (buf_getu32l(b
, &u
)) return (-1);
1297 if (0 <= u
&& u
<= UCHAR_MAX
) rv
->i
= u
;
1298 else if (u
== MASK32
) rv
->i
= EOF
;
1303 static int parse_char(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
1304 struct tvec_state
*tv
)
1314 if (tvec_readword(tv
, &d
, ";", "character name")) { rc
= -1; goto end
; }
1315 if (STRCMP(d
.buf
, ==, "#eof"))
1318 rc
= tvec_error(tv
, "unknown character name `%s'", d
.buf
);
1324 if (ch
== '\'') { f
|= f_quote
; ch
= getc(tv
->fp
); }
1327 if (!(f
&f_quote
)) goto plain
;
1328 case EOF
: case '\n':
1329 rc
= tvec_syntax(tv
, ch
, "character"); goto end
;
1331 if (read_escape(&ch
, tv
)) return (-1);
1337 if (ch
!= '\'') { rc
= tvec_syntax(tv
, ch
, "`''"); goto end
; }
1339 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
1348 static void dump_char(const union tvec_regval
*rv
,
1349 const struct tvec_regdef
*rd
,
1351 const struct gprintf_ops
*gops
, void *go
)
1353 if ((style
&TVSF_COMPACT
) && isprint(rv
->i
) && rv
->i
!= '\'')
1354 gprintf(gops
, go
, "%c", (int)rv
->i
);
1356 format_char(gops
, go
, rv
->i
);
1358 if (!(style
&TVSF_COMPACT
)) {
1359 gprintf(gops
, go
, " ; = %ld = ", rv
->i
);
1360 format_signed_hex(gops
, go
, rv
->i
);
1364 const struct tvec_regty tvty_char
= {
1365 init_int
, trivial_release
, eq_int
,
1366 tobuf_char
, frombuf_char
,
1367 parse_char
, dump_char
1370 int tvec_claimeq_char(struct tvec_state
*tv
, int c0
, int c1
,
1371 const char *file
, unsigned lno
, const char *expr
)
1373 tv
->out
[0].v
.i
= c0
; tv
->in
[0].v
.i
= c1
;
1374 return (tvec_claimeq(tv
, &tvty_char
, 0, file
, lno
, expr
));
1377 /*----- Text and byte strings ---------------------------------------------*/
1379 void tvec_allocstring(union tvec_regval
*rv
, size_t sz
)
1381 if (rv
->str
.sz
< sz
) { xfree(rv
->str
.p
); rv
->str
.p
= xmalloc(sz
); }
1385 void tvec_allocbytes(union tvec_regval
*rv
, size_t sz
)
1387 if (rv
->bytes
.sz
< sz
) { xfree(rv
->bytes
.p
); rv
->bytes
.p
= xmalloc(sz
); }
1391 static void init_string(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
1392 { rv
->str
.p
= 0; rv
->str
.sz
= 0; }
1394 static void init_bytes(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
1395 { rv
->bytes
.p
= 0; rv
->bytes
.sz
= 0; }
1397 static void release_string(union tvec_regval
*rv
,
1398 const struct tvec_regdef
*rd
)
1399 { xfree(rv
->str
.p
); }
1401 static void release_bytes(union tvec_regval
*rv
,
1402 const struct tvec_regdef
*rd
)
1403 { xfree(rv
->bytes
.p
); }
1405 static int eq_string(const union tvec_regval
*rv0
,
1406 const union tvec_regval
*rv1
,
1407 const struct tvec_regdef
*rd
)
1409 return (rv0
->str
.sz
== rv1
->str
.sz
&&
1411 MEMCMP(rv0
->str
.p
, ==, rv1
->str
.p
, rv1
->str
.sz
)));
1414 static int eq_bytes(const union tvec_regval
*rv0
,
1415 const union tvec_regval
*rv1
,
1416 const struct tvec_regdef
*rd
)
1418 return (rv0
->bytes
.sz
== rv1
->bytes
.sz
&&
1420 MEMCMP(rv0
->bytes
.p
, ==, rv1
->bytes
.p
, rv1
->bytes
.sz
)));
1423 static int tobuf_string(buf
*b
, const union tvec_regval
*rv
,
1424 const struct tvec_regdef
*rd
)
1425 { return (buf_putmem32l(b
, rv
->str
.p
, rv
->str
.sz
)); }
1427 static int tobuf_bytes(buf
*b
, const union tvec_regval
*rv
,
1428 const struct tvec_regdef
*rd
)
1429 { return (buf_putmem32l(b
, rv
->bytes
.p
, rv
->bytes
.sz
)); }
1431 static int frombuf_string(buf
*b
, union tvec_regval
*rv
,
1432 const struct tvec_regdef
*rd
)
1437 p
= buf_getmem32l(b
, &sz
); if (!p
) return (-1);
1438 tvec_allocstring(rv
, sz
); memcpy(rv
->str
.p
, p
, sz
);
1442 static int frombuf_bytes(buf
*b
, union tvec_regval
*rv
,
1443 const struct tvec_regdef
*rd
)
1448 p
= buf_getmem32l(b
, &sz
); if (!p
) return (-1);
1449 tvec_allocbytes(rv
, sz
); memcpy(rv
->bytes
.p
, p
, sz
);
1453 static int check_string_length(size_t sz
, const struct tvec_urange
*ur
,
1454 struct tvec_state
*tv
)
1456 if (ur
&& (ur
->min
> sz
|| sz
> ur
->max
))
1457 return (tvec_error(tv
,
1458 "invalid string length %lu; must be in [%lu..%lu]",
1459 (unsigned long)sz
, ur
->min
, ur
->max
));
1463 static int parse_string(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
1464 struct tvec_state
*tv
)
1466 void *p
= rv
->str
.p
;
1468 if (read_compound_string(&p
, &rv
->str
.sz
, TVCODE_BARE
, tv
)) return (-1);
1470 if (check_string_length(rv
->str
.sz
, rd
->arg
.p
, tv
)) return (-1);
1474 static int parse_bytes(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
1475 struct tvec_state
*tv
)
1477 void *p
= rv
->bytes
.p
;
1479 if (read_compound_string(&p
, &rv
->bytes
.sz
, TVCODE_HEX
, tv
)) return (-1);
1481 if (check_string_length(rv
->bytes
.sz
, rd
->arg
.p
, tv
)) return (-1);
1485 static void dump_string(const union tvec_regval
*rv
,
1486 const struct tvec_regdef
*rd
,
1488 const struct gprintf_ops
*gops
, void *go
)
1490 const unsigned char *p
, *q
, *l
;
1492 #define f_nonword 1u
1493 #define f_newline 2u
1495 if (!rv
->str
.sz
) { gprintf(gops
, go
, "\"\""); return; }
1497 p
= (const unsigned char *)rv
->str
.p
; l
= p
+ rv
->str
.sz
;
1498 if (*p
== '!' || *p
== ';' || *p
== '"' || *p
== '\'') goto quote
;
1499 for (q
= p
; q
< l
; q
++)
1500 if (*q
== '\n' && q
!= l
- 1) f
|= f_newline
;
1501 else if (!*q
|| !isgraph(*q
) || *q
== '\\') f
|= f_nonword
;
1502 if (f
&f_newline
) { gprintf(gops
, go
, "\n\t"); goto quote
; }
1503 else if (f
&f_nonword
) goto quote
;
1504 gops
->putm(go
, (const char *)p
, rv
->str
.sz
); return;
1507 gprintf(gops
, go
, "\"");
1508 for (q
= p
; q
< l
; q
++)
1509 if (!isprint(*q
) || *q
== '"') {
1510 if (p
< q
) gops
->putm(go
, (const char *)p
, q
- p
);
1511 if (*q
== '\n' && !(style
&TVSF_COMPACT
))
1512 gprintf(gops
, go
, "\\n\"\t\"");
1514 format_charesc(gops
, go
, *q
, FCF_BRACE
);
1516 if (p
< q
) gops
->putm(go
, (const char *)p
, q
- p
);
1517 gprintf(gops
, go
, "\"");
1523 static void dump_bytes(const union tvec_regval
*rv
,
1524 const struct tvec_regdef
*rd
,
1526 const struct gprintf_ops
*gops
, void *go
)
1528 const unsigned char *p
= rv
->bytes
.p
, *l
= p
+ rv
->bytes
.sz
;
1529 size_t off
, sz
= rv
->bytes
.sz
;
1534 gprintf(gops
, go
, style
&TVSF_COMPACT ?
"\"\"" : "\"\" ; empty");
1538 if (style
&TVSF_COMPACT
) {
1539 while (p
< l
) gprintf(gops
, go
, "%02x", *p
++);
1543 if (sz
> 16) gprintf(gops
, go
, "\n\t");
1545 off
= 0; wd
= hex_width(sz
);
1547 if (l
- p
< 16) n
= l
- p
;
1550 for (i
= 0; i
< 16; i
++) {
1551 if (i
< n
) gprintf(gops
, go
, "%02x", p
[i
]);
1552 else gprintf(gops
, go
, " ");
1553 if (i
%4 == 3) gprintf(gops
, go
, " ");
1555 gprintf(gops
, go
, " ; ");
1556 if (sz
> 16) gprintf(gops
, go
, "[%0*lx] ", wd
, (unsigned long)off
);
1557 for (i
= 0; i
< n
; i
++)
1558 gprintf(gops
, go
, "%c", isprint(p
[i
]) ? p
[i
] : '.');
1560 if (p
< l
) gprintf(gops
, go
, "\n\t");
1564 const struct tvec_regty tvty_string
= {
1565 init_string
, release_string
, eq_string
,
1566 tobuf_string
, frombuf_string
,
1567 parse_string
, dump_string
1570 const struct tvec_regty tvty_bytes
= {
1571 init_bytes
, release_bytes
, eq_bytes
,
1572 tobuf_bytes
, frombuf_bytes
,
1573 parse_bytes
, dump_bytes
1576 int tvec_claimeq_string(struct tvec_state
*tv
,
1577 const char *p0
, size_t sz0
,
1578 const char *p1
, size_t sz1
,
1579 const char *file
, unsigned lno
, const char *expr
)
1581 tv
->out
[0].v
.str
.p
= (/*unconst*/ char *)p0
; tv
->out
[0].v
.str
.sz
= sz0
;
1582 tv
->in
[0].v
.str
.p
=(/*unconst*/ char *) p1
; tv
->in
[0].v
.str
.sz
= sz1
;
1583 return (tvec_claimeq(tv
, &tvty_string
, 0, file
, lno
, expr
));
1586 int tvec_claimeq_strz(struct tvec_state
*tv
,
1587 const char *p0
, const char *p1
,
1588 const char *file
, unsigned lno
, const char *expr
)
1590 tv
->out
[0].v
.str
.p
= (/*unconst*/ char *)p0
;
1591 tv
->out
[0].v
.str
.sz
= strlen(p0
);
1592 tv
->in
[0].v
.str
.p
= (/*unconst*/ char *)p1
;
1593 tv
->in
[0].v
.str
.sz
= strlen(p1
);
1594 return (tvec_claimeq(tv
, &tvty_string
, 0, file
, lno
, expr
));
1597 int tvec_claimeq_bytes(struct tvec_state
*tv
,
1598 const void *p0
, size_t sz0
,
1599 const void *p1
, size_t sz1
,
1600 const char *file
, unsigned lno
, const char *expr
)
1602 tv
->out
[0].v
.bytes
.p
= (/*unconst*/ void *)p0
;
1603 tv
->out
[0].v
.bytes
.sz
= sz0
;
1604 tv
->in
[0].v
.bytes
.p
= (/*unconst*/ void *)p1
;
1605 tv
->in
[0].v
.bytes
.sz
= sz1
;
1606 return (tvec_claimeq(tv
, &tvty_bytes
, 0, file
, lno
, expr
));
1609 /*----- Buffer type -------------------------------------------------------*/
1611 static int eq_buffer(const union tvec_regval
*rv0
,
1612 const union tvec_regval
*rv1
,
1613 const struct tvec_regdef
*rd
)
1614 { return (rv0
->bytes
.sz
== rv1
->bytes
.sz
); }
1616 static int tobuf_buffer(buf
*b
, const union tvec_regval
*rv
,
1617 const struct tvec_regdef
*rd
)
1618 { return (unsigned_to_buf(b
, rv
->bytes
.sz
)); }
1620 static int frombuf_buffer(buf
*b
, union tvec_regval
*rv
,
1621 const struct tvec_regdef
*rd
)
1625 if (unsigned_from_buf(b
, &u
)) return (-1);
1626 if (u
> (size_t)-1) return (-1);
1627 tvec_allocbytes(rv
, u
); memset(rv
->bytes
.p
, '!', u
);
1631 static const char units
[] = "kMGTPEZY";
1633 static int parse_buffer(union tvec_regval
*rv
,
1634 const struct tvec_regdef
*rd
,
1635 struct tvec_state
*tv
)
1638 const char *q
, *unit
;
1645 if (tvec_readword(tv
, &d
, ";", "buffer length")) { rc
= -1; goto end
; }
1646 if (parse_unsigned_integer(&u
, &q
, d
.buf
)) goto bad
;
1648 tvec_skipspc(tv
); pos
= d
.len
;
1649 if (!tvec_readword(tv
, &d
, ";", 0)) pos
++;
1653 if (u
> (size_t)-1) goto rangerr
;
1654 for (t
= u
, unit
= units
; *unit
; unit
++) {
1655 if (t
> (size_t)-1/1024) f
|= f_range
;
1657 if (*q
== *unit
&& (!q
[1] || q
[1] == 'B')) {
1658 if (f
&f_range
) goto rangerr
;
1659 u
= t
; q
+= 2; break;
1662 if (*q
&& *q
!= ';') goto bad
;
1663 if (check_string_length(u
, rd
->arg
.p
, tv
)) { rc
= -1; goto end
; }
1665 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
1666 tvec_allocbytes(rv
, u
); memset(rv
->bytes
.p
, '?', u
);
1669 DDESTROY(&d
); return (rc
);
1672 tvec_error(tv
, "invalid buffer length `%s'", d
.buf
);
1676 tvec_error(tv
, "buffer length `%s' out of range", d
.buf
);
1682 static void dump_buffer(const union tvec_regval
*rv
,
1683 const struct tvec_regdef
*rd
,
1685 const struct gprintf_ops
*gops
, void *go
)
1688 unsigned long u
= rv
->bytes
.sz
;
1691 gprintf(gops
, go
, "%lu B", u
);
1693 for (unit
= units
, u
/= 1024; !(u
%1024) && unit
[1]; u
/= 1024, unit
++);
1694 gprintf(gops
, go
, "%lu %cB", u
, *unit
);
1698 const struct tvec_regty tvty_buffer
= {
1699 init_bytes
, release_bytes
, eq_buffer
,
1700 tobuf_buffer
, frombuf_buffer
,
1701 parse_buffer
, dump_buffer
1704 /*----- That's all, folks -------------------------------------------------*/