/* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 2016-2021 Free Software Foundation, Inc. GNU Mailutils is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Mailutils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Mailutils. If not, see . */ #include union value { char *v_string; signed short v_short; unsigned short v_ushort; int v_int; unsigned v_uint; long v_long; unsigned long v_ulong; size_t v_size; time_t v_time; struct mu_cidr v_cidr; }; struct value_handler { void (*format) (union value *, FILE *); int (*compare) (union value *, union value *); }; static void v_string_format (union value *val, FILE *fp) { char *p; fputc ('"', fp); for (p = val->v_string; *p; p++) { if (*p == '\\' || *p == '"') fputc ('\\', fp); fputc (*p, fp); } fputc ('"', fp); } static int v_string_compare (union value *a, union value *b) { return strcmp (a->v_string, b->v_string); } static void v_short_format (union value *val, FILE *fp) { fprintf (fp, "%hd", val->v_short); } static int v_short_compare (union value *a, union value *b) { return a->v_short != b->v_short; } static void v_ushort_format (union value *val, FILE *fp) { fprintf (fp, "%hu", val->v_short); } static int v_ushort_compare (union value *a, union value *b) { return a->v_ushort != b->v_ushort; } static void v_int_format (union value *val, FILE *fp) { fprintf (fp, "%d", val->v_int); } static int v_int_compare (union value *a, union value *b) { return a->v_int != b->v_int; } static void v_uint_format (union value *val, FILE *fp) { fprintf (fp, "%u", val->v_uint); } static int v_uint_compare (union value *a, union value *b) { return a->v_uint != b->v_uint; } static void v_long_format (union value *val, FILE *fp) { fprintf (fp, "%ld", val->v_long); } static int v_long_compare (union value *a, union value *b) { return a->v_long != b->v_long; } static void v_ulong_format (union value *val, FILE *fp) { fprintf (fp, "%lu", val->v_long); } static int v_ulong_compare (union value *a, union value *b) { return a->v_ulong != b->v_ulong; } static void v_size_format (union value *val, FILE *fp) { size_t v = val->v_size; char buf[80]; size_t buflen = 80; char *p = buf + buflen; *--p = 0; do { int n = v % 10; *--p = n + '0'; v /= 10; } while (v && p > buf); if (v) fputs ("[overflow]", fp); else fputs (p, fp); } static int v_size_compare (union value *a, union value *b) { return a->v_size != b->v_size; } static void v_cidr_format (union value *val, FILE *fp) { char *buf; int rc; rc = mu_cidr_format (&val->v_cidr, 0, &buf); if (rc) { fprintf (fp, "(can't convert value: %s)", mu_strerror (rc)); } else { fprintf (fp, "%s", buf); free (buf); } } static int v_cidr_compare (union value *a, union value *b) { if (a->v_cidr.family != b->v_cidr.family) return 1; if (a->v_cidr.len != b->v_cidr.len) return 1; if (memcmp (a->v_cidr.address, b->v_cidr.address, a->v_cidr.len)) return 1; if (memcmp (a->v_cidr.netmask, b->v_cidr.netmask, a->v_cidr.len)) return 1; return 0; } struct value_handler value_handler[] = { [mu_c_string] = { v_string_format, v_string_compare }, [mu_c_short] = { v_short_format, v_short_compare }, [mu_c_ushort] = { v_ushort_format, v_ushort_compare }, [mu_c_int] = { v_int_format, v_int_compare }, [mu_c_uint] = { v_uint_format, v_uint_compare }, [mu_c_long] = { v_long_format, v_long_compare }, [mu_c_ulong] = { v_ulong_format, v_ulong_compare }, [mu_c_size] = { v_size_format, v_size_compare }, [mu_c_hsize] = { v_size_format, v_size_compare }, #if 0 mu_c_time, #endif [mu_c_bool] = { v_int_format, v_int_compare }, [mu_c_cidr] = { v_cidr_format, v_cidr_compare }, [mu_c_incr] = { v_int_format, v_int_compare }, }; int valcmp (mu_c_type_t type, union value *a, union value *b) { if ((size_t)type < sizeof (value_handler) / sizeof (value_handler[0]) && value_handler[type].compare) return value_handler[type].compare (a, b); else { fprintf (stderr, "unsupported value type: %d\n", type); abort (); } } void valprint (FILE *fp, mu_c_type_t type, union value *val) { if ((size_t)type < sizeof (value_handler) / sizeof (value_handler[0]) && value_handler[type].format) value_handler[type].format (val, fp); else { fprintf (stderr, "unsupported value type: %d\n", type); abort (); } } struct testdata { mu_c_type_t type; char const *input; int err; union value val; }; struct testdata tests[] = { { mu_c_string, "now is the time", 0, { .v_string = "now is the time" } }, { mu_c_short, "115", 0, { .v_short = 115 } }, { mu_c_short, "-400", 0, { .v_short = -400 } }, { mu_c_short, "1a", EINVAL, }, { mu_c_ushort, "110", 0, { .v_ushort = 110 } }, { mu_c_ushort, "-110", ERANGE }, { mu_c_int, "10568", 0, { .v_int = 10568 } }, { mu_c_int, "-10568", 0, { .v_int = -10568 } }, { mu_c_uint, "10568", 0, { .v_uint = 10568 } }, { mu_c_long, "10568", 0, { .v_long = 10568 } }, { mu_c_long, "-10568", 0, { .v_long = -10568 } }, { mu_c_ulong, "10568", 0, { .v_ulong = 10568 } }, { mu_c_size, "10568", 0, { .v_size = 10568 } }, { mu_c_hsize, "0", 0, { .v_size = 0 } }, { mu_c_hsize, "10", 0, { .v_size = 10 } }, { mu_c_hsize, "10K", 0, { .v_size = 10240 } }, { mu_c_hsize, " 10 M ", 0, { .v_size = 10485760 } }, { mu_c_hsize, "10s", MU_ERR_PARSE }, { mu_c_hsize, "-1", MU_ERR_PARSE }, { mu_c_hsize, "", MU_ERR_PARSE }, { mu_c_hsize, " ", MU_ERR_PARSE }, { mu_c_hsize, " 1 M b ", MU_ERR_PARSE }, { mu_c_bool, "true", 0, { .v_int = 1 } }, { mu_c_bool, "false", 0, { .v_int = 0 } }, { mu_c_cidr, "127.0.0.0/8", 0, { .v_cidr = { .family = 2, .len = 4, .address = { 127, 0, 0, 0 }, .netmask = { 255 } } } }, #ifdef MAILUTILS_IPV6 { mu_c_cidr, "fe80::4a5b:39ff:fe09:97f0/64", 0, { .v_cidr = { .family = 10, .len = 16, .address = { 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4a, 0x5b, 0x39, 0xff, 0xfe, 0x9, 0x97, 0xf0 }, .netmask = { 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0 } } } }, #endif { mu_c_incr, NULL, 0, { .v_int = 1 } }, { mu_c_void } }; void print_test_id (int i, FILE *fp) { fprintf (fp, "%d: %s:%s: ", i, mu_c_type_str[tests[i].type], tests[i].input ? tests[i].input : "NULL"); } void usage (int code, FILE *fp, char const *argv0) { fprintf (fp, "usage: %s [-v]\n", argv0); exit (code); } int main (int argc, char **argv) { union value val; char *errmsg; int i; unsigned failures = 0; int verbose = 0; for (i = 1; i < argc; i++) { if (strcmp (argv[i], "-v") == 0) verbose = 1; else if (strcmp (argv[i], "-h") == 0) usage (0, stdout, argv[0]); else usage (1, stderr, argv[0]); } if (i != argc) usage (1, stderr, argv[0]); for (i = 0; tests[i].type != mu_c_void; i++) { int rc; memset (&val, 0, sizeof (val)); rc = mu_str_to_c (tests[i].input, tests[i].type, &val, &errmsg); if (rc) { if (tests[i].err == rc) { if (verbose) { print_test_id (i, stdout); fprintf (stdout, "XFAIL\n"); } } else { print_test_id (i, stderr); fprintf (stderr, "FAIL: error %s", mu_strerror (rc)); if (errmsg) fprintf (stderr, ": %s", errmsg); fputc ('\n', stderr); free (errmsg); ++failures; } } else if (tests[i].err) { print_test_id (i, stderr); fprintf (stderr, "FAIL: unexpected success\n"); ++failures; } else if (valcmp (tests[i].type, &tests[i].val, &val)) { fprintf (stderr, "%d: FAIL: %s value differ: ", i, mu_c_type_str[tests[i].type]); fprintf (stderr, "expected: "); valprint (stderr, tests[i].type, &tests[i].val); fprintf (stderr, ", but got: "); valprint (stderr, tests[i].type, &val); fputc ('\n', stderr); ++failures; } else if (verbose) { print_test_id (i, stdout); fprintf (stdout, "OK\n"); } } exit (!!failures); }