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 ------------------------------------------------------*/
45 /*----- Preliminary utilities ---------------------------------------------*/
47 static int signed_to_buf(buf
*b
, long i
)
53 if (i
>= 0) ASSIGN64(k
, u
);
54 else { ASSIGN64(k
, ~u
); CPL64(k
, k
); }
55 return (buf_putk64l(b
, k
));
58 static int signed_from_buf(buf
*b
, long *i_out
)
60 kludge64 k
, lmax
, not_lmin
;
62 ASSIGN64(lmax
, LONG_MAX
); ASSIGN64(not_lmin
, ~(unsigned long)LONG_MIN
);
63 if (buf_getk64l(b
, &k
)) return (-1);
64 if (CMP64(k
, <=, lmax
)) *i_out
= (long)GET64(unsigned long, k
);
67 if (CMP64(k
, <=, not_lmin
)) *i_out
= -(long)GET64(unsigned long, k
) - 1;
73 static int unsigned_to_buf(buf
*b
, unsigned long u
)
74 { kludge64 k
; ASSIGN64(k
, u
); return (buf_putk64l(b
, k
)); }
76 static int unsigned_from_buf(buf
*b
, unsigned long *u_out
)
80 ASSIGN64(ulmax
, ULONG_MAX
);
81 if (buf_getk64l(b
, &k
)) return (-1);
82 if (CMP64(k
, >, ulmax
)) return (-1);
83 *u_out
= GET64(unsigned long, k
); return (0);
86 static int hex_width(unsigned long u
)
91 for (t
= u
>> 4, wd
= 4; t
>>= wd
, wd
*= 2, t
; );
95 static void check_signed_range(long i
,
96 const struct tvec_irange
*ir
,
97 struct tvec_state
*tv
)
99 if (ir
&& (ir
->min
> i
|| i
> ir
->max
))
100 tvec_error(tv
, "integer %ld out of range (must be in [%ld .. %ld])",
101 i
, ir
->min
, ir
->max
);
104 static void check_unsigned_range(unsigned long u
,
105 const struct tvec_urange
*ur
,
106 struct tvec_state
*tv
)
108 if (ur
&& (ur
->min
> u
|| u
> ur
->max
))
109 tvec_error(tv
, "integer %lu out of range (must be in [%lu .. %lu])",
110 u
, ur
->min
, ur
->max
);
113 static void parse_signed(long *i_out
, const char *p
,
114 const struct tvec_irange
*ir
,
115 struct tvec_state
*tv
)
117 char *q
; const char *pp
;
121 olderr
= errno
; errno
= 0;
122 pp
= p
; if (*pp
== '-' || *pp
== '+') pp
++;
123 if (!ISDIGIT(*pp
)) tvec_syntax(tv
, *pp
, "signed integer");
124 i
= strtol(p
, &q
, 0);
125 if (*q
&& !ISSPACE(*q
)) tvec_syntax(tv
, *q
, "end-of-line");
126 if (errno
) tvec_error(tv
, "invalid integer `%s'", p
);
127 check_signed_range(i
, ir
, tv
);
128 errno
= olderr
; *i_out
= i
;
131 static void parse_unsigned(unsigned long *u_out
, const char *p
,
132 const struct tvec_urange
*ur
,
133 struct tvec_state
*tv
)
139 olderr
= errno
; errno
= 0;
140 if (!ISDIGIT(*p
)) tvec_syntax(tv
, *p
, "unsigned integer");
141 u
= strtoul(p
, &q
, 0);
142 if (*q
&& !ISSPACE(*q
)) tvec_syntax(tv
, *q
, "end-of-line");
143 if (errno
) tvec_error(tv
, "invalid integer `%s'", p
);
144 check_unsigned_range(u
, ur
, tv
);
145 errno
= olderr
; *u_out
= u
;
148 static int convert_hex(char ch
, int *v_out
)
150 if ('0' <= ch
&& ch
<= '9') { *v_out
= ch
- '0'; return (0); }
151 else if ('a' <= ch
&& ch
<= 'f') { *v_out
= ch
- 'a' + 10; return (0); }
152 else if ('A' <= ch
&& ch
<= 'F') { *v_out
= ch
- 'A' + 10; return (0); }
156 static void read_quoted_string(dstr
*d
, int quote
, struct tvec_state
*tv
)
163 sprintf(expect
, "`%c'", quote
);
170 tvec_syntax(tv
, ch
, expect
);
173 if (quote
== '\'') goto ordinary
;
176 case EOF
: tvec_syntax(tv
, ch
, expect
);
177 case '\n': tv
->lno
++; break;
178 case '\'': DPUTC(d
, '\''); break;
179 case '\\': DPUTC(d
, '\\'); break;
180 case '"': DPUTC(d
, '"'); break;
181 case 'a': DPUTC(d
, '\a'); break;
182 case 'b': DPUTC(d
, '\b'); break;
183 case 'e': DPUTC(d
, '\x1b'); break;
184 case 'f': DPUTC(d
, '\f'); break;
185 case 'n': DPUTC(d
, '\n'); break;
186 case 'r': DPUTC(d
, '\r'); break;
187 case 't': DPUTC(d
, '\t'); break;
188 case 'v': DPUTC(d
, '\v'); break;
192 if (ch
== '{') { f
|= f_brace
; ch
= getc(tv
->fp
); }
194 if (convert_hex(ch
, &esc
)) tvec_syntax(tv
, ch
, "hex digit");
196 ch
= getc(tv
->fp
); if (convert_hex(ch
, &i
)) break;
199 tvec_error(tv
, "character code %d out of range", esc
);
202 if (!(f
&f_brace
)) goto reinsert
;
203 else if (ch
!= '}') tvec_syntax(tv
, ch
, "`}'");
207 if ('0' <= ch
&& ch
< '8') {
208 i
= 1; esc
= ch
- '0';
211 if (i
> 3 || '0' > ch
|| ch
>= '8') break;
212 esc
= 8*esc
+ ch
- '0'; i
++;
215 tvec_error(tv
, "character code %d out of range", esc
);
219 tvec_syntax(tv
, ch
, "string escape");
225 if (ch
== quote
) goto end
;
238 enum { TVCODE_BARE
, TVCODE_HEX
, TVCODE_BASE64
, TVCODE_BASE32
};
240 static int collect_bare(dstr
*d
, struct tvec_state
*tv
)
243 enum { WORD
, SPACE
, ESCAPE
}; unsigned s
= WORD
;
252 if (s
== ESCAPE
) { tv
->lno
++; goto addch
; }
253 if (s
== WORD
) pos
= d
->len
;
254 ungetc(ch
, tv
->fp
); if (tvec_nexttoken(tv
)) { rc
= -1; goto done
; }
255 DPUTC(d
, ' '); s
= SPACE
;
257 case '"': case '\'': case '!':
258 if (s
== SPACE
) { ungetc(ch
, tv
->fp
); rc
= 0; goto done
; }
264 if (s
!= ESCAPE
&& isspace(ch
)) {
265 if (s
== WORD
) pos
= d
->len
;
266 DPUTC(d
, ch
); s
= SPACE
;
270 DPUTC(d
, ch
); s
= WORD
;
275 if (s
== SPACE
) d
->len
= pos
;
276 DPUTZ(d
); return (rc
);
279 tvec_syntax(tv
, ch
, "bareword");
282 static void set_up_encoding(const codec_class
**ccl_out
, unsigned *f_out
,
287 *ccl_out
= 0; *f_out
= 0;
290 *ccl_out
= &hex_class
; *f_out
= CDCF_IGNCASE
;
293 *ccl_out
= &base32_class
; *f_out
= CDCF_IGNCASE
| CDCF_IGNEQPAD
;
296 *ccl_out
= &base64_class
; *f_out
= CDCF_IGNEQPAD
;
303 static void read_compound_string(void **p_inout
, size_t *sz_inout
,
304 unsigned code
, struct tvec_state
*tv
)
306 const codec_class
*ccl
; unsigned f
;
308 dstr d
= DSTR_INIT
, w
= DSTR_INIT
;
312 set_up_encoding(&ccl
, &f
, code
);
313 if (tvec_nexttoken(tv
)) tvec_syntax(tv
, fgetc(tv
->fp
), "string");
316 if (ch
== '"' || ch
== '\'')
317 read_quoted_string(&d
, ch
, tv
);
318 else if (ch
== '!') {
320 DRESET(&w
); tvec_readword(tv
, &w
, ";", "`!'-keyword");
321 if (STRCMP(w
.buf
, ==, "!bare")) code
= TVCODE_BARE
;
322 else if (STRCMP(w
.buf
, ==, "!hex")) code
= TVCODE_HEX
;
323 else if (STRCMP(w
.buf
, ==, "!base32")) code
= TVCODE_BASE32
;
324 else if (STRCMP(w
.buf
, ==, "!base64")) code
= TVCODE_BASE64
;
325 else tvec_error(tv
, "unknown string keyword `%s'", w
.buf
);
326 set_up_encoding(&ccl
, &f
, code
);
330 tvec_readword(tv
, &w
, ";", "%s-encoded fragment", ccl
->name
);
331 cdc
= ccl
->decoder(f
);
332 err
= cdc
->ops
->code(cdc
, w
.buf
, w
.len
, &d
);
333 if (!err
) err
= cdc
->ops
->code(cdc
, 0, 0, &d
);
335 tvec_error(tv
, "invalid %s fragment `%s': %s",
336 ccl
->name
, w
.buf
, codec_strerror(err
));
337 cdc
->ops
->destroy(cdc
);
338 } else switch (code
) {
341 if (collect_bare(&d
, tv
)) goto done
;
346 } while (!tvec_nexttoken(tv
));
349 if (*sz_inout
<= d
.len
)
350 { xfree(*p_inout
); *p_inout
= xmalloc(d
.len
+ 1); }
351 p
= *p_inout
; memcpy(p
, d
.buf
, d
.len
); p
[d
.len
] = 0; *sz_inout
= d
.len
;
352 dstr_destroy(&d
); dstr_destroy(&w
);
355 /*----- Skeleton ----------------------------------------------------------*/
357 static void init_...(union tvec_regval *rv, const struct tvec_regdef *rd)
358 static void release_...(union tvec_regval *rv, const struct tvec_regdef *rd)
359 static int eq_...(const union tvec_regval *rv0, const union tvec_regval *rv1,
360 const struct tvec_regdef *rd)
361 static size_t measure_...(const union tvec_regval *rv,
362 const struct tvec_regdef *rd)
363 static int tobuf_...(buf *b, const union tvec_regval *rv,
364 const struct tvec_regdef *rd)
365 static int frombuf_...(buf *b, union tvec_regval *rv,
366 const struct tvec_regdef *rd)
367 static void parse_...(union tvec_regval *rv, const struct tvec_regdef *rd,
368 struct tvec_state *tv)
369 static void dump_...(const union tvec_regval *rv,
370 const struct tvec_regdef *rd,
371 struct tvec_state *tv, unsigned style)
373 const struct tvec_regty tvty_... = {
374 init_..., release_..., eq_..., measure_...,
375 tobuf_..., frombuf_...,
379 /*----- Signed and unsigned integer types ---------------------------------*/
381 static void init_int(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
384 static void init_uint(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
387 static void release_int(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
390 static int eq_int(const union tvec_regval
*rv0
, const union tvec_regval
*rv1
,
391 const struct tvec_regdef
*rd
)
392 { return (rv0
->i
== rv1
->i
); }
394 static int eq_uint(const union tvec_regval
*rv0
,
395 const union tvec_regval
*rv1
,
396 const struct tvec_regdef
*rd
)
397 { return (rv0
->u
== rv1
->u
); }
399 static size_t measure_int(const union tvec_regval
*rv
,
400 const struct tvec_regdef
*rd
)
403 static int tobuf_int(buf
*b
, const union tvec_regval
*rv
,
404 const struct tvec_regdef
*rd
)
405 { return (signed_to_buf(b
, rv
->i
)); }
407 static int tobuf_uint(buf
*b
, const union tvec_regval
*rv
,
408 const struct tvec_regdef
*rd
)
409 { return (unsigned_to_buf(b
, rv
->u
)); }
411 static int frombuf_int(buf
*b
, union tvec_regval
*rv
,
412 const struct tvec_regdef
*rd
)
413 { return signed_from_buf(b
, &rv
->i
); }
415 static int frombuf_uint(buf
*b
, union tvec_regval
*rv
,
416 const struct tvec_regdef
*rd
)
417 { return (unsigned_from_buf(b
, &rv
->u
)); }
419 static void parse_int(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
420 struct tvec_state
*tv
)
424 tvec_readword(tv
, &d
, ";", "signed integer");
425 parse_signed(&rv
->i
, d
.buf
, rd
->arg
.p
, tv
);
426 tvec_flushtoeol(tv
, 0);
430 static void parse_uint(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
431 struct tvec_state
*tv
)
435 tvec_readword(tv
, &d
, ";", "unsigned integer");
436 parse_unsigned(&rv
->u
, d
.buf
, rd
->arg
.p
, tv
);
437 tvec_flushtoeol(tv
, 0);
441 static void dump_int(const union tvec_regval
*rv
,
442 const struct tvec_regdef
*rd
,
443 struct tvec_state
*tv
, unsigned style
)
447 tvec_write(tv
, "%ld", rv
->i
);
448 if (!(style
&TVSF_COMPACT
)) {
449 if (rv
->i
>= 0) u
= rv
->i
;
450 else u
= -(unsigned long)rv
->i
;
451 tvec_write(tv
, " ; = %s0x%0*lx", rv
->i
< 0 ?
"-" : "", hex_width(u
), u
);
455 static void dump_uint(const union tvec_regval
*rv
,
456 const struct tvec_regdef
*rd
,
457 struct tvec_state
*tv
, unsigned style
)
459 tvec_write(tv
, "%lu", rv
->u
);
460 if (!(style
&TVSF_COMPACT
))
461 tvec_write(tv
, " ; = 0x%0*lx", hex_width(rv
->u
), rv
->u
);
464 const struct tvec_regty tvty_int
= {
465 init_int
, release_int
, eq_int
, measure_int
,
466 tobuf_int
, frombuf_int
,
470 const struct tvec_irange
471 tvrange_schar
= { SCHAR_MIN
, SCHAR_MAX
},
472 tvrange_short
= { SHRT_MIN
, SHRT_MAX
},
473 tvrange_int
= { INT_MIN
, INT_MAX
},
474 tvrange_long
= { LONG_MIN
, LONG_MAX
},
475 tvrange_sbyte
= { -128, 127 },
476 tvrange_i16
= { -32768, +32767 },
477 tvrange_i32
= { -2147483648, 2147483647 };
479 const struct tvec_regty tvty_uint
= {
480 init_uint
, release_int
, eq_uint
, measure_int
,
481 tobuf_uint
, frombuf_uint
,
482 parse_uint
, dump_uint
485 const struct tvec_urange
486 tvrange_uchar
= { 0, UCHAR_MAX
},
487 tvrange_ushort
= { 0, USHRT_MAX
},
488 tvrange_uint
= { 0, UINT_MAX
},
489 tvrange_ulong
= { 0, ULONG_MAX
},
490 tvrange_size
= { 0, (size_t)-1 },
491 tvrange_byte
= { 0, 255 },
492 tvrange_u16
= { 0, 65535 },
493 tvrange_u32
= { 0, 4294967296 };
495 int tvec_claimeq_int(struct tvec_state
*tv
, long i0
, long i1
,
496 const char *file
, unsigned lno
, const char *expr
)
498 tv
->in
[0].v
.i
= i0
; tv
->out
[0].v
.i
= i1
;
499 return (tvec_claimeq(tv
, &tvty_int
, 0, file
, lno
, expr
));
502 int tvec_claimeq_uint(struct tvec_state
*tv
,
503 unsigned long u0
, unsigned long u1
,
504 const char *file
, unsigned lno
, const char *expr
)
506 tv
->in
[0].v
.u
= u0
; tv
->out
[0].v
.u
= u1
;
507 return (tvec_claimeq(tv
, &tvty_uint
, 0, file
, lno
, expr
));
510 /*----- Enumerations ------------------------------------------------------*/
512 static void init_enum(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
514 const struct tvec_enuminfo
*ei
= rd
->arg
.p
;
517 #define CASE(tag, ty, slot) \
518 case TVMISC_##tag: rv->slot = 0; break;
525 static int eq_enum(const union tvec_regval
*rv0
,
526 const union tvec_regval
*rv1
,
527 const struct tvec_regdef
*rd
)
529 const struct tvec_enuminfo
*ei
= rd
->arg
.p
;
532 #define CASE(tag, ty, slot) \
533 case TVMISC_##tag: return (rv0->slot == rv1->slot);
540 static int tobuf_enum(buf
*b
, const union tvec_regval
*rv
,
541 const struct tvec_regdef
*rd
)
543 const struct tvec_enuminfo
*ei
= rd
->arg
.p
;
546 #define CASE(tag, ty, slot) \
547 case TVMISC_##tag: return (HANDLE_##tag);
548 #define HANDLE_INT signed_to_buf(b, rv->i)
549 #define HANDLE_UINT unsigned_to_buf(b, rv->u)
550 #define HANDLE_PTR -1
561 static int frombuf_enum(buf
*b
, union tvec_regval
*rv
,
562 const struct tvec_regdef
*rd
)
564 const struct tvec_enuminfo
*ei
= rd
->arg
.p
;
567 #define CASE(tag, ty, slot) \
568 case TVMISC_##tag: return (HANDLE_##tag);
569 #define HANDLE_INT signed_from_buf(b, &rv->i)
570 #define HANDLE_UINT unsigned_from_buf(b, &rv->u)
571 #define HANDLE_PTR -1
581 static void parse_enum(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
582 struct tvec_state
*tv
)
584 const struct tvec_enuminfo
*ei
= rd
->arg
.p
;
585 #define DECLS(tag, ty, slot) \
586 const struct tvec_##slot##assoc *slot##a;
587 TVEC_MISCSLOTS(DECLS
)
591 tvec_readword(tv
, &d
, ";", "enumeration tag or literal integer");
593 #define CASE(tag_, ty, slot) \
594 case TVMISC_##tag_: \
595 for (slot##a = ei->u.slot.av; slot##a->tag; slot##a++) \
596 if (STRCMP(d.buf, ==, slot##a->tag)) \
597 { rv->slot = FETCH_##tag_; goto end; }
598 #define FETCH_INT (ia->i)
599 #define FETCH_UINT (ua->u)
600 #define FETCH_PTR ((/*unconst*/ void *)(pa->p))
609 #define CASE(tag, ty, slot) \
610 case TVMISC_##tag: HANDLE_##tag goto end;
611 #define HANDLE_INT parse_signed(&rv->i, d.buf, ei->u.i.ir, tv);
612 #define HANDLE_UINT parse_unsigned(&rv->u, d.buf, ei->u.u.ur, tv);
613 #define HANDLE_PTR if (STRCMP(d.buf, ==, "#nil")) rv->p = 0; \
621 tvec_error(tv
, "unknown `%s' value `%s'", ei
->name
, d
.buf
);
625 tvec_flushtoeol(tv
, 0);
629 static void dump_enum(const union tvec_regval
*rv
,
630 const struct tvec_regdef
*rd
,
631 struct tvec_state
*tv
, unsigned style
)
633 const struct tvec_enuminfo
*ei
= rd
->arg
.p
;
634 #define DECLS(tag, ty, slot) \
635 const struct tvec_##slot##assoc *slot##a;
636 TVEC_MISCSLOTS(DECLS
)
644 #define CASE(tag_, ty, slot) \
645 case TVMISC_##tag_: \
646 for (slot##a = ei->u.slot.av; slot##a->tag; slot##a++) \
647 if (rv->slot == slot##a->slot) \
648 { tag = slot##a->tag; goto found; } \
658 tvec_write(tv
, "%s", tag
);
659 if (style
&TVSF_COMPACT
) return;
660 tvec_write(tv
, " ; = ");
664 #define CASE(tag, ty, slot) \
665 case TVMISC_##tag: HANDLE_##tag break;
666 #define HANDLE_INT tvec_write(tv, "%ld", rv->i);
667 #define HANDLE_UINT tvec_write(tv, "%lu", rv->u);
668 #define HANDLE_PTR if (!rv->p) tvec_write(tv, "#nil"); \
669 else tvec_write(tv, "#<%s %p>", ei->name, rv->p);
679 if (!(f
&f_known
)) tvec_write(tv
, " ;");
680 if (rv
->i
>= 0) u
= rv
->i
;
681 else u
= -(unsigned long)rv
->i
;
682 tvec_write(tv
, " = %s0x%0*lx", rv
->i
< 0 ?
"-" : "", hex_width(u
), u
);
685 if (!(f
&f_known
)) tvec_write(tv
, " ;");
686 tvec_write(tv
, " = 0x%0*lx", hex_width(rv
->u
), rv
->u
);
691 const struct tvec_regty tvty_enum
= {
692 init_enum
, release_int
, eq_enum
, measure_int
,
693 tobuf_enum
, frombuf_enum
,
694 parse_enum
, dump_enum
697 #define DEFCLAIM(tag, ty, slot) \
698 int tvec_claimeq_##slot##enum(struct tvec_state *tv, \
699 const struct tvec_enuminfo *ei, \
701 const char *file, unsigned lno, \
704 union tvec_misc arg; \
706 assert(ei->mv == TVMISC_##tag); \
708 tv->in[0].v.slot = GET_##tag(e0); \
709 tv->out[0].v.slot = GET_##tag(e1); \
710 return (tvec_claimeq(tv, &tvty_enum, &arg, file, lno, expr)); \
712 #define GET_INT(e) (e)
713 #define GET_UINT(e) (e)
714 #define GET_PTR(e) ((/*unconst*/ void *)(e))
715 TVEC_MISCSLOTS(DEFCLAIM
)
721 /*----- Flag types --------------------------------------------------------*/
723 static void parse_flags(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
724 struct tvec_state
*tv
)
726 const struct tvec_flaginfo
*fi
= rd
->arg
.p
;
727 const struct tvec_flag
*f
;
728 unsigned long m
= 0, v
= 0, t
;
733 DRESET(&d
); tvec_readword(tv
, &d
, "|;", "flag name or integer");
735 for (f
= fi
->fv
; f
->tag
; f
++)
736 if (STRCMP(f
->tag
, ==, d
.buf
)) {
737 if (m
&f
->m
) tvec_error(tv
, "colliding flag setting");
738 else { m
|= f
->m
; v
|= f
->v
; goto next
; }
741 parse_unsigned(&t
, d
.buf
, fi
->range
, tv
); v
|= t
;
743 if (tvec_nexttoken(tv
)) break;
744 ch
= getc(tv
->fp
); if (ch
!= '|') tvec_syntax(tv
, ch
, "`|'");
745 if (tvec_nexttoken(tv
)) tvec_syntax(tv
, '\n', "flag name or integer");
750 static void dump_flags(const union tvec_regval
*rv
,
751 const struct tvec_regdef
*rd
,
752 struct tvec_state
*tv
, unsigned style
)
754 const struct tvec_flaginfo
*fi
= rd
->arg
.p
;
755 const struct tvec_flag
*f
;
756 unsigned long m
= ~(unsigned long)0, v
= rv
->u
;
759 for (f
= fi
->fv
, sep
= ""; f
->tag
; f
++)
760 if ((m
&f
->m
) && (v
&f
->m
) == f
->v
) {
761 tvec_write(tv
, "%s%s", sep
, f
->tag
); m
&= ~f
->m
;
762 sep
= style
&TVSF_COMPACT ?
"|" : " | ";
765 if (v
&m
) tvec_write(tv
, "%s0x%0*lx", sep
, hex_width(v
), v
&m
);
767 if (!(style
&TVSF_COMPACT
))
768 tvec_write(tv
, " ; = 0x%0*lx", hex_width(rv
->u
), rv
->u
);
771 const struct tvec_regty tvty_flags
= {
772 init_uint
, release_int
, eq_uint
, measure_int
,
773 tobuf_uint
, frombuf_uint
,
774 parse_flags
, dump_flags
777 int tvec_claimeq_flags(struct tvec_state
*tv
,
778 const struct tvec_flaginfo
*fi
,
779 unsigned long f0
, unsigned long f1
,
780 const char *file
, unsigned lno
, const char *expr
)
784 arg
.p
= fi
; tv
->in
[0].v
.u
= f0
; tv
->out
[0].v
.u
= f1
;
785 return (tvec_claimeq(tv
, &tvty_flags
, &arg
, file
, lno
, expr
));
788 /*----- Text and byte strings ---------------------------------------------*/
790 void tvec_allocstring(union tvec_regval
*rv
, size_t sz
)
792 if (rv
->str
.sz
< sz
) { xfree(rv
->str
.p
); rv
->str
.p
= xmalloc(sz
); }
796 void tvec_allocbytes(union tvec_regval
*rv
, size_t sz
)
798 if (rv
->bytes
.sz
< sz
) { xfree(rv
->bytes
.p
); rv
->bytes
.p
= xmalloc(sz
); }
802 static void init_string(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
803 { rv
->str
.p
= 0; rv
->str
.sz
= 0; }
805 static void init_bytes(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
806 { rv
->bytes
.p
= 0; rv
->bytes
.sz
= 0; }
808 static void release_string(union tvec_regval
*rv
,
809 const struct tvec_regdef
*rd
)
810 { xfree(rv
->str
.p
); }
812 static void release_bytes(union tvec_regval
*rv
,
813 const struct tvec_regdef
*rd
)
814 { xfree(rv
->bytes
.p
); }
816 static int eq_string(const union tvec_regval
*rv0
,
817 const union tvec_regval
*rv1
,
818 const struct tvec_regdef
*rd
)
820 return (rv0
->str
.sz
== rv1
->str
.sz
&&
822 MEMCMP(rv0
->str
.p
, ==, rv1
->str
.p
, rv1
->str
.sz
)));
825 static int eq_bytes(const union tvec_regval
*rv0
,
826 const union tvec_regval
*rv1
,
827 const struct tvec_regdef
*rd
)
829 return (rv0
->bytes
.sz
== rv1
->bytes
.sz
&&
831 MEMCMP(rv0
->bytes
.p
, ==, rv1
->bytes
.p
, rv1
->bytes
.sz
)));
834 static size_t measure_string(const union tvec_regval
*rv
,
835 const struct tvec_regdef
*rd
)
836 { return (rv
->str
.sz
+ 4); }
838 static size_t measure_bytes(const union tvec_regval
*rv
,
839 const struct tvec_regdef
*rd
)
840 { return (rv
->bytes
.sz
+ 4); }
842 static int tobuf_string(buf
*b
, const union tvec_regval
*rv
,
843 const struct tvec_regdef
*rd
)
844 { return (buf_putmem32l(b
, rv
->str
.p
, rv
->str
.sz
)); }
846 static int tobuf_bytes(buf
*b
, const union tvec_regval
*rv
,
847 const struct tvec_regdef
*rd
)
848 { return (buf_putmem32l(b
, rv
->bytes
.p
, rv
->bytes
.sz
)); }
850 static int frombuf_string(buf
*b
, union tvec_regval
*rv
,
851 const struct tvec_regdef
*rd
)
856 p
= buf_getmem32l(b
, &sz
); if (!p
) return (-1);
857 tvec_allocstring(rv
, sz
); memcpy(rv
->str
.p
, p
, sz
);
861 static int frombuf_bytes(buf
*b
, union tvec_regval
*rv
,
862 const struct tvec_regdef
*rd
)
867 p
= buf_getmem32l(b
, &sz
); if (!p
) return (-1);
868 tvec_allocbytes(rv
, sz
); memcpy(rv
->bytes
.p
, p
, sz
);
872 static void check_string_length(size_t sz
, const struct tvec_urange
*ur
,
873 struct tvec_state
*tv
)
875 if (ur
&& (ur
->min
> sz
|| sz
> ur
->max
))
876 tvec_error(tv
, "invalid string length %lu; must be in [%lu..%lu]",
877 (unsigned long)sz
, ur
->min
, ur
->max
);
880 static void parse_string(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
881 struct tvec_state
*tv
)
885 read_compound_string(&p
, &rv
->str
.sz
, TVCODE_BARE
, tv
); rv
->str
.p
= p
;
886 check_string_length(rv
->str
.sz
, rd
->arg
.p
, tv
);
889 static void parse_bytes(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
890 struct tvec_state
*tv
)
892 void *p
= rv
->bytes
.p
;
894 read_compound_string(&p
, &rv
->bytes
.sz
, TVCODE_HEX
, tv
); rv
->bytes
.p
= p
;
895 check_string_length(rv
->bytes
.sz
, rd
->arg
.p
, tv
);
898 static void dump_string(const union tvec_regval
*rv
,
899 const struct tvec_regdef
*rd
,
900 struct tvec_state
*tv
, unsigned style
)
902 const unsigned char *p
, *q
, *l
;
908 if (!rv
->str
.sz
) { tvec_write(tv
, "\"\""); return; }
910 p
= (const unsigned char *)rv
->str
.p
; l
= p
+ rv
->str
.sz
;
911 if (*p
== '!' || *p
== ';' || *p
== '"' || *p
== '\'') goto quote
;
912 for (q
= p
; q
< l
; q
++)
913 if (*q
== '\n' && q
!= l
- 1) f
|= f_newline
;
914 else if (!*q
|| !isgraph(*q
) || *q
== '\\') f
|= f_nonword
;
915 if (f
&f_newline
) { tvec_write(tv
, "\n\t"); goto quote
; }
916 else if (f
&f_nonword
) goto quote
;
917 tv
->output
->ops
->write(tv
->output
, (const char *)p
, rv
->str
.sz
); return;
920 tvec_write(tv
, "\"");
921 for (q
= p
; q
< l
; q
++)
923 case '"': case '\\': ch
= *q
; goto escape
;
924 case '\a': ch
= 'a'; goto escape
;
925 case '\b': ch
= 'b'; goto escape
;
926 case '\x1b': ch
= 'e'; goto escape
;
927 case '\f': ch
= 'f'; goto escape
;
928 case '\r': ch
= 'r'; goto escape
;
929 case '\t': ch
= 't'; goto escape
;
930 case '\v': ch
= 'v'; goto escape
;
933 tv
->output
->ops
->write(tv
->output
, (const char *)p
, q
- p
);
934 tvec_write(tv
, "\\%c", ch
); p
= q
+ 1;
939 tv
->output
->ops
->write(tv
->output
, (const char *)p
, q
- p
);
940 tvec_write(tv
, "\\n"); p
= q
+ 1;
941 if (!(style
&TVSF_COMPACT
) && q
< l
) tvec_write(tv
, "\"\t\"");
945 if (isprint(*q
)) break;
947 tv
->output
->ops
->write(tv
->output
, (const char *)p
, q
- p
);
948 tvec_write(tv
, "\\x{%0*x}", hex_width(UCHAR_MAX
), *q
); p
= q
+ 1;
951 if (p
< q
) tv
->output
->ops
->write(tv
->output
, (const char *)p
, q
- p
);
952 tvec_write(tv
, "\"");
958 static void dump_bytes(const union tvec_regval
*rv
,
959 const struct tvec_regdef
*rd
,
960 struct tvec_state
*tv
, unsigned style
)
962 const unsigned char *p
= rv
->bytes
.p
, *l
= p
+ rv
->bytes
.sz
;
963 size_t off
, sz
= rv
->bytes
.sz
;
968 tvec_write(tv
, style
&TVSF_COMPACT ?
"\"\"" : "\"\" ; empty");
972 if (style
&TVSF_COMPACT
) {
973 while (p
< l
) tvec_write(tv
, "%02x", *p
++);
977 if (sz
> 16) tvec_write(tv
, "\n\t");
979 off
= 0; wd
= hex_width(sz
);
981 if (l
- p
< 16) n
= l
- p
;
984 for (i
= 0; i
< 16; i
++) {
985 if (i
< n
) tvec_write(tv
, "%02x", p
[i
]);
986 else tvec_write(tv
, " ");
987 if (i
%4 == 3) tvec_write(tv
, " ");
989 tvec_write(tv
, " ; ");
990 if (sz
> 16) tvec_write(tv
, "[%0*lx] ", wd
, (unsigned long)off
);
991 for (i
= 0; i
< n
; i
++)
992 tvec_write(tv
, "%c", isprint(p
[i
]) ? p
[i
] : '.');
994 if (p
< l
) tvec_write(tv
, "\n\t");
998 const struct tvec_regty tvty_string
= {
999 init_string
, release_string
, eq_string
, measure_string
,
1000 tobuf_string
, frombuf_string
,
1001 parse_string
, dump_string
1004 const struct tvec_regty tvty_bytes
= {
1005 init_bytes
, release_bytes
, eq_bytes
, measure_bytes
,
1006 tobuf_bytes
, frombuf_bytes
,
1007 parse_bytes
, dump_bytes
1010 int tvec_claimeq_string(struct tvec_state
*tv
,
1011 const char *p0
, size_t sz0
,
1012 const char *p1
, size_t sz1
,
1013 const char *file
, unsigned lno
, const char *expr
)
1015 tv
->in
[0].v
.str
.p
= (/*unconst*/ char *)p0
; tv
->in
[0].v
.str
.sz
= sz0
;
1016 tv
->out
[0].v
.str
.p
=(/*unconst*/ char *) p1
; tv
->out
[0].v
.str
.sz
= sz1
;
1017 return (tvec_claimeq(tv
, &tvty_string
, 0, file
, lno
, expr
));
1020 int tvec_claimeq_strz(struct tvec_state
*tv
,
1021 const char *p0
, const char *p1
,
1022 const char *file
, unsigned lno
, const char *expr
)
1024 tv
->in
[0].v
.str
.p
= (/*unconst*/ char *)p0
;
1025 tv
->in
[0].v
.str
.sz
= strlen(p0
);
1026 tv
->out
[0].v
.str
.p
= (/*unconst*/ char *)p1
;
1027 tv
->out
[0].v
.str
.sz
= strlen(p1
);
1028 return (tvec_claimeq(tv
, &tvty_string
, 0, file
, lno
, expr
));
1031 int tvec_claimeq_bytes(struct tvec_state
*tv
,
1032 const void *p0
, size_t sz0
,
1033 const void *p1
, size_t sz1
,
1034 const char *file
, unsigned lno
, const char *expr
)
1036 tv
->in
[0].v
.bytes
.p
= (/*unconst*/ void *)p0
;
1037 tv
->in
[0].v
.bytes
.sz
= sz0
;
1038 tv
->out
[0].v
.bytes
.p
= (/*unconst*/ void *)p1
;
1039 tv
->out
[0].v
.bytes
.sz
= sz1
;
1040 return (tvec_claimeq(tv
, &tvty_bytes
, 0, file
, lno
, expr
));
1043 /*----- Buffer type -------------------------------------------------------*/
1045 static int eq_buffer(const union tvec_regval
*rv0
,
1046 const union tvec_regval
*rv1
,
1047 const struct tvec_regdef
*rd
)
1048 { return (rv0
->bytes
.sz
== rv1
->bytes
.sz
); }
1050 static int tobuf_buffer(buf
*b
, const union tvec_regval
*rv
,
1051 const struct tvec_regdef
*rd
)
1052 { return (unsigned_to_buf(b
, rv
->bytes
.sz
)); }
1054 static int frombuf_buffer(buf
*b
, union tvec_regval
*rv
,
1055 const struct tvec_regdef
*rd
)
1059 if (unsigned_from_buf(b
, &u
)) return (-1);
1060 if (u
> (size_t)-1) return (-1);
1061 tvec_allocbytes(rv
, u
); memset(rv
->bytes
.p
, '!', u
);
1065 static const char units
[] = "kMGTPEZY";
1067 static void parse_buffer(union tvec_regval
*rv
,
1068 const struct tvec_regdef
*rd
,
1069 struct tvec_state
*tv
)
1072 char *q
; const char *unit
;
1079 tvec_readword(tv
, &d
, ";", "buffer length");
1080 olderr
= errno
; errno
= 0;
1081 u
= strtoul(d
.buf
, &q
, 0);
1082 if (errno
) goto bad
;
1085 tvec_skipspc(tv
); pos
= d
.len
;
1086 if (!tvec_readword(tv
, &d
, ";", 0)) pos
++;
1090 if (u
> (size_t)-1) goto rangerr
;
1091 for (t
= u
, unit
= units
; *unit
; unit
++) {
1092 if (t
> (size_t)-1/1024) f
|= f_range
;
1094 if (*q
== *unit
&& (!q
[1] || q
[1] == 'B')) {
1095 if (f
&f_range
) goto rangerr
;
1096 u
= t
; q
+= 2; break;
1099 if (*q
&& *q
!= ';') goto bad
;
1100 check_string_length(u
, rd
->arg
.p
, tv
);
1102 tvec_flushtoeol(tv
, 0);
1103 tvec_allocbytes(rv
, u
); memset(rv
->bytes
.p
, 0, u
);
1104 DDESTROY(&d
); return;
1107 tvec_error(tv
, "invalid buffer length `%s'", d
.buf
);
1110 tvec_error(tv
, "buffer length `%s' out of range", d
.buf
);
1115 static void dump_buffer(const union tvec_regval
*rv
,
1116 const struct tvec_regdef
*rd
,
1117 struct tvec_state
*tv
, unsigned style
)
1120 unsigned long u
= rv
->bytes
.sz
;
1123 tvec_write(tv
, "%lu B", u
);
1125 for (unit
= units
, u
/= 1024; !(u
%1024) && unit
[1]; u
/= 1024, unit
++);
1126 tvec_write(tv
, "%lu %cB", u
, *unit
);
1130 const struct tvec_regty tvty_buffer
= {
1131 init_bytes
, release_bytes
, eq_buffer
, measure_int
,
1132 tobuf_buffer
, frombuf_buffer
,
1133 parse_buffer
, dump_buffer
1136 /*----- That's all, folks -------------------------------------------------*/