varnish-cache/lib/libvarnish/vnum.c
1
/*-
2
 * Copyright (c) 2008-2009 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 *
30
 * Deal with numbers.
31
 *
32
 */
33
34
#include "config.h"
35
36
#include <math.h>
37
#include <stdint.h>
38
#include <stdio.h>
39
#include <stdlib.h>
40
#include <string.h>
41
42
#include "vdef.h"
43
44
#include "vnum.h"
45
#include "vas.h"
46
#include "vct.h"
47
48
/* The distinction between these two is used internally */
49
static const char err_invalid_num[] = "Invalid number";
50
static const char err_no_digits[] = "Invalid number";
51
52
static const char err_fatnum[] = "Too many digits";
53
54
static const char err_unknown_bytes[] =
55
    "Unknown BYTES unit of measurement ([KMGTP][B])";
56
57
static const char err_fractional_bytes[] = "Fractional BYTES not allowed";
58
59
#define BAIL(txt)                                               \
60
        do {                                                    \
61
                if (errtxt != NULL)                             \
62
                        *errtxt = (txt);                        \
63
                errno = EINVAL;                                 \
64
                return (retval);                                \
65
        } while (0)
66
67
/*
68
 * Internal function for parsing an integer with a limited
69
 * number of digits.
70
 */
71
72
static int64_t
73 11749535
sf_parse_int(const char **ipp, const char **errtxt, int *sign, int maxdig)
74
{
75 11749535
        int64_t retval = 0;
76 11749535
        int ndig = 0;
77
78 11749535
        AN(ipp);
79 11749532
        AN(*ipp);
80 11749532
        if (errtxt != NULL)
81 11749533
                *errtxt = NULL;
82 11749531
        *sign = 1;
83 11749531
        errno = 0;
84 11750811
        while (vct_isows(*(*ipp)))
85 1280
                (*ipp)++;
86 11749531
        if(*(*ipp) == '-') {
87 2360
                *sign = -1;
88 2360
                (*ipp)++;
89 2360
        }
90 34193909
        while (vct_isdigit(*(*ipp))) {
91 22444816
                ndig++;
92 22444816
                if (ndig > maxdig)
93 440
                        BAIL(err_fatnum);
94 22444376
                retval *= 10;
95 22444376
                retval += *(*ipp)++ - 0x30;
96
        }
97 11749092
        if (ndig == 0)
98 84637
                BAIL(err_no_digits);
99 11664737
        while (vct_isows(*(*ipp)))
100 280
                (*ipp)++;
101 11664455
        return (retval);
102 11749533
}
103
104
/**********************************************************************
105
 * Parse a RFC8941 `sf-integer`.
106
 *
107
 * If `errno` is non-zero the conversion failed.
108
 * If `errtxt` is provided it summarily tells why.
109
 * The input argument points to the first character not consumed.
110
 */
111
112
int64_t
113 800
SF_Parse_Integer(const char **ipp, const char **errtxt)
114
{
115
        int64_t retval;
116
        int sign;
117
118 800
        retval = sf_parse_int(ipp, errtxt, &sign, 15);
119 800
        return(retval * sign);
120
}
121
122
/**********************************************************************
123
 * Parse either a RFC8941 `sf-integer` or `sf-decimal`.
124
 *
125
 * If `errno` is non-zero the conversion failed.
126
 * If `errtxt` is provided it summarily tells why.
127
 * The input argument points to the first character not consumed.
128
 */
129
130
double
131 11748230
SF_Parse_Number(const char **ipp, int strict, const char **errtxt)
132
{
133 11748230
        double retval, scale = 1;
134
        int sign, ndig;
135
136 11748230
        retval = (double)sf_parse_int(ipp, errtxt, &sign, 15);
137 11748230
        if (strict && errno)
138 320
                return (0);
139 11747929
        if (*(*ipp) != '.')
140 7032678
                return (retval * sign);
141 4715253
        if (retval < VRT_DECIMAL_MIN || retval > VRT_DECIMAL_MAX)
142 40
                BAIL(err_fatnum);
143 4715212
        if (*errtxt == err_no_digits && (!vct_isdigit((*ipp)[1])))
144 120
                BAIL(err_no_digits);
145 4715095
        *errtxt = NULL;
146 4715095
        errno = 0;
147 4715095
        do {
148 4715094
                (*ipp)++;
149 17822865
                for(ndig = 0; ndig < 3; ndig++) {
150 13984817
                        scale *= .1;
151 13984817
                        if (!vct_isdigit(*(*ipp)))
152 877055
                                break;
153 13107771
                        retval += scale * (*(*ipp)++ - 0x30);
154 13107771
                }
155 4715095
                if (strict && ndig == 0)
156 80
                        BAIL(err_invalid_num);
157 4715015
                if (strict && vct_isdigit(*(*ipp)))
158 120
                        BAIL(err_fatnum);
159 4715613
                while (vct_isdigit(*(*ipp)))
160 720
                        (*ipp)++;
161 4714890
        } while (0);
162 4715730
        while (vct_isows(*(*ipp)))
163 840
                (*ipp)++;
164 4714892
        return (retval * sign);
165 11748248
}
166
167
/**********************************************************************
168
 * Parse a RFC8941 `sf-decimal`.
169
 *
170
 * If `errno` is non-zero the conversion failed.
171
 * If `errtxt` is provided it summarily tells why.
172
 * The input argument points to the first character not consumed.
173
 */
174
175
double
176 6454400
SF_Parse_Decimal(const char **ipp, int strict, const char **errtxt)
177
{
178
        double retval;
179
180 6454400
        retval = SF_Parse_Number(ipp, strict, errtxt);
181 6454400
        if (errno)
182 240
                return(retval);
183 6454160
        if (retval < VRT_DECIMAL_MIN || retval > VRT_DECIMAL_MAX)
184 0
                BAIL(err_fatnum);
185 6454160
        return (retval);
186 6454400
}
187
188
/**********************************************************************
189
 * Parse a "Varnish number".
190
 *
191
 * Varnish numbers are the union of RFC8941 sf-integer and sf-decimal.
192
 * If `errno` is non-zero the conversion failed and NAN is returned.
193
 */
194
195
double
196 32955
VNUM(const char *p)
197
{
198
        const char *t;
199
        double r;
200
201 32955
        r = SF_Parse_Number(&p, 0, &t);
202 32955
        if (errno || *p != '\0')
203 2120
                r = nan("");
204 32968
        return (r);
205
}
206
207
/**********************************************************************/
208
209
vtim_dur
210 202800
VNUM_duration_unit(vtim_dur r, const char *b, const char *e)
211
{
212
        double sc;
213
214 202800
        if (e == NULL)
215 2280
                e = strchr(b, '\0');
216
217 202800
        while (b < e && vct_issp(*b))
218 0
                b++;
219 202800
        if (b == e)
220 120
                return (nan(""));
221
222 202680
        switch (*b++) {
223
        case 's':
224 198640
                sc = 1.0;
225 198640
                break;
226
        case 'm':
227 2000
                if (b < e && *b == 's') {
228 520
                        sc = 1e-3;
229 520
                        b++;
230 520
                } else
231 1480
                        sc = 60.0;
232 2000
                break;
233
        case 'h':
234 720
                sc = 60.0 * 60.0;
235 720
                break;
236
        case 'd':
237 600
                sc = 60.0 * 60.0 * 24.0;
238 600
                break;
239
        case 'w':
240 320
                sc = 60.0 * 60.0 * 24.0 * 7.0;
241 320
                break;
242
        case 'y':
243 200
                sc = 60.0 * 60.0 * 24.0 * 365.0;
244 200
                break;
245
        default:
246 200
                return (nan(""));
247
        }
248
249 202560
        while (b < e && vct_issp(*b))
250 80
                b++;
251
252 202480
        if (b < e)
253 80
                return (nan(""));
254
255 202400
        return (r * sc);
256 202800
}
257
258
vtim_dur
259 2760
VNUM_duration(const char *p)
260
{
261
        const char *t;
262
        vtim_dur r;
263
264 2760
        if (p == NULL)
265 400
                return (nan(""));
266
267 2360
        r = SF_Parse_Number(&p, 0, &t);
268
269 2360
        if (errno)
270 80
                return (nan(""));
271
272 2280
        return (VNUM_duration_unit(r, p, NULL));
273 2760
}
274
275
/**********************************************************************/
276
277
int64_t
278 5239160
VNUM_bytes_unit(double r, const char *b, const char *e, uintmax_t rel,
279
    const char **errtxt)
280
{
281 5239160
        double sc = 1.0, tmp;
282
283 5239160
        AN(b);
284 5239160
        AN(errtxt);
285 5239160
        errno = 0;
286 5239160
        if (e == NULL)
287 5237480
                e = strchr(b, '\0');
288
289 5239160
        while (b < e && vct_issp(*b))
290 0
                b++;
291 5239160
        if (b == e) {
292 323160
                if (modf(r, &tmp) != 0.0) {
293 40
                        *errtxt = err_fractional_bytes;
294 40
                        errno = EINVAL;
295 40
                }
296 323160
                return ((int64_t)trunc(sc * r));
297
        }
298
299 4916000
        if (rel != 0 && *b == '%') {
300 160
                r *= rel * 0.01;
301 160
                b++;
302 160
        } else {
303 4915840
                switch (*b) {
304 1860760
                case 'k': case 'K': sc = exp2(10); b++; break;
305 704840
                case 'm': case 'M': sc = exp2(20); b++; break;
306 674280
                case 'g': case 'G': sc = exp2(30); b++; break;
307 240
                case 't': case 'T': sc = exp2(40); b++; break;
308 160
                case 'p': case 'P': sc = exp2(50); b++; break;
309
                case 'b': case 'B':
310 1675320
                        if (modf(r, &tmp) != 0.0) {
311 80
                                *errtxt = err_fractional_bytes;
312 80
                                errno = EINVAL;
313 80
                                return (0);
314
                        }
315 1675240
                        break;
316
                default:
317 240
                        *errtxt = err_unknown_bytes;
318 240
                        errno = EINVAL;
319 240
                        return (0);
320
                }
321 4915520
                if (b < e && (*b == 'b' || *b == 'B'))
322 1676440
                        b++;
323
        }
324 4915760
        while (b < e && vct_issp(*b))
325 80
                b++;
326 4915680
        if (b < e) {
327 0
                *errtxt = err_unknown_bytes;
328 0
                errno = EINVAL;
329 0
                return (0);
330
        }
331 4915680
        return ((int64_t)trunc(sc * r));
332 5239160
}
333
334
const char *
335 5238200
VNUM_2bytes(const char *p, uintmax_t *r, uintmax_t rel)
336
{
337
        double fval;
338
        const char *errtxt;
339
340 5238200
        if (p == NULL || *p == '\0')
341 440
                return (err_invalid_num);
342
343 5237760
        fval = SF_Parse_Number(&p, 1, &errtxt);
344 5237760
        if (errno)
345 240
                return(errtxt);
346 5237520
        if (fval < 0)
347 40
                return(err_invalid_num);
348
349 5237480
        fval = VNUM_bytes_unit(fval, p, NULL, rel, &errtxt);
350 5237480
        if (errno)
351 320
                return (errtxt);
352 5237160
        *r = (uintmax_t)round(fval);
353 5237160
        return (NULL);
354 5238200
}
355
356
#ifdef NUM_C_TEST
357
/*
358
 * Compile with:
359
 *     cc -o foo -DNUM_C_TEST -DTEST_VERBOSE \
360
 *         -I../.. -I../../include vnum.c vas.c vct.c -lm
361
 */
362
363
static const struct test_sf_parse_int {
364
        const char *input;
365
        int maxdig;
366
        int64_t retval;
367
        int consumed;
368
        int sign;
369
        const char *errtxt;
370
} test_sf_parse_int[] = {
371
        { "1234",       3,  123, 3,  1, err_fatnum },
372
        { "1234",       4, 1234, 4,  1, NULL },
373
        { "1234",       5, 1234, 4,  1, NULL },
374
        { "-",          5,    0, 1, -1, err_no_digits },
375
        { "  ",         5,    0, 2,  1, err_no_digits },
376
        { "-1234",      3,  123, 4, -1, err_fatnum },
377
        { "-1234",      4, 1234, 5, -1, NULL },
378
        { "-1234",      5, 1234, 5, -1, NULL },
379
        { " -1234",     5, 1234, 6, -1, NULL },
380
        { " -1234 ",    5, 1234, 7, -1, NULL },
381
        { " -12 34 ",   5,   12, 5, -1, NULL },
382
        { " - 12 34 ",  5,    0, 2, -1, err_no_digits },
383
        { NULL},
384
};
385
386
static const struct test_sf_parse_number {
387
        const char *input;
388
        int strict;
389
        double retval;
390
        int consumed;
391
        const char *errtxt;
392
} test_sf_parse_number[] = {
393
        { "1234",               1,          1234.000,  4, NULL },
394
        { " 1234",              1,          1234.000,  5, NULL },
395
        { " 1234 ",             1,          1234.000,  6, NULL },
396
        { " 1234. ",            1,          1234.000,  6, err_invalid_num },
397
        { " 123456789012.0 ",   1,  123456789012.000, 16, NULL },
398
        { " 1234567890123.0 ",  1, 1234567890123.000, 14, err_fatnum },
399
        { " 123456789012.123 ", 1,  123456789012.123, 18, NULL },
400
        { " 123456789012.1234 ",1,  123456789012.123, 17, err_fatnum },
401
        { " -0.123456 ",        1,              .123,  7, err_fatnum },
402
        { " -.123456 ",         1,             0.,     2, err_no_digits },
403
        { " .123456 ",          1,             0.,     1, err_no_digits },
404
        { " 0. ",               1,             0.,     3, err_invalid_num },
405
        { " .0 ",               1,             0.,     1, err_no_digits },
406
407
        { " 123456789012.1234 ",0,  123456789012.123, 19, NULL },
408
        { " -0.123456 ",        0,             -.123, 11, NULL },
409
        { " -.123456 ",         0,             -.123, 10, NULL },
410
        { " .123456 ",          0,              .123,  9, NULL },
411
        { " 0. ",               0,             0.,     4, NULL },
412
        { " .0 ",               0,             0.,     4, NULL },
413
        { " -0. ",              0,            -0.,     5, NULL },
414
        { " -.0 ",              0,            -0.,     5, NULL },
415
        { " - ",                0,            -0.,     2, err_no_digits },
416
        { " -. ",               0,             0.,     2, err_no_digits },
417
        { " . ",                0,             0.,     1, err_no_digits },
418
        { NULL},
419
};
420
421
static struct test_case {
422
        const char *str;
423
        uintmax_t rel;
424
        uintmax_t val;
425
        const char *err;
426
} test_cases[] = {
427
        { "1",                  (uintmax_t)0,   (uintmax_t)1 },
428
        { "1B",                 (uintmax_t)0,   (uintmax_t)1<<0 },
429
        { "1 B",                (uintmax_t)0,   (uintmax_t)1<<0 },
430
        { "1.3B",               0,      0,      err_fractional_bytes },
431
        { "1.7B",               0,      0,      err_fractional_bytes },
432
433
        { "1024",               (uintmax_t)0,   (uintmax_t)1024 },
434
        { "1k",                 (uintmax_t)0,   (uintmax_t)1<<10 },
435
        { "1kB",                (uintmax_t)0,   (uintmax_t)1<<10 },
436
        { "0.75kB",             (uintmax_t)0,   (uintmax_t)768 },
437
        { "1.3kB",              (uintmax_t)0,   (uintmax_t)1331 },
438
        { "1.70kB",             (uintmax_t)0,   (uintmax_t)1740 },
439
440
        { "1048576",            (uintmax_t)0,   (uintmax_t)1048576 },
441
        { "1M",                 (uintmax_t)0,   (uintmax_t)1<<20 },
442
        { "1MB",                (uintmax_t)0,   (uintmax_t)1<<20 },
443
        { "1.3MB",              (uintmax_t)0,   (uintmax_t)1363148 },
444
        { "1.700MB",            (uintmax_t)0,   (uintmax_t)1782579 },
445
446
        { "1073741824",         (uintmax_t)0,   (uintmax_t)1073741824 },
447
        { "1G",                 (uintmax_t)0,   (uintmax_t)1<<30 },
448
        { "1GB",                (uintmax_t)0,   (uintmax_t)1<<30 },
449
        { "1.3GB",              (uintmax_t)0,   (uintmax_t)1395864371 },
450
        { "1.7GB",              (uintmax_t)0,   (uintmax_t)1825361100 },
451
452
        { "1099511627776",      (uintmax_t)0,   (uintmax_t)1099511627776ULL },
453
        { "1T",                 (uintmax_t)0,   (uintmax_t)1<<40 },
454
        { "1TB",                (uintmax_t)0,   (uintmax_t)1<<40 },
455
        { "1.3TB",              (uintmax_t)0,   (uintmax_t)1429365116108ULL },
456
        { "1.7\tTB",            (uintmax_t)0,   (uintmax_t)1869169767219ULL },
457
458
        { "999999999999999",    (uintmax_t)0,   (uintmax_t)999999999999999ULL},
459
460
        { "1125899906842624",   0,      0,      err_fatnum },
461
        { "1P\t",               (uintmax_t)0,   (uintmax_t)1125899906842624ULL},
462
        { "1PB ",               (uintmax_t)0,   (uintmax_t)1125899906842624ULL},
463
        { "1.3 PB",             (uintmax_t)0,   (uintmax_t)1463669878895411ULL},
464
465
        { "1.5%",               (uintmax_t)1024,        (uintmax_t)15 },
466
        { "1.501%",             (uintmax_t)1024,        (uintmax_t)15 },
467
        { "2%",                 (uintmax_t)1024,        (uintmax_t)20 },
468
        { "3%",                 (uintmax_t)1024,        (uintmax_t)30 },
469
470
        /* Check the error checks */
471
        { "",                   0,      0,      err_invalid_num },
472
        { "-1",                 0,      0,      err_invalid_num },
473
        { "1.3",                0,      0,      err_fractional_bytes},
474
        { "1.5011%",            0,      0,      err_fatnum },
475
        { "-",                  0,      0,      err_no_digits },
476
        { "m",                  0,      0,      err_no_digits },
477
        { "4%",                 0,      0,      err_unknown_bytes },
478
        { "3*",                 0,      0,      err_unknown_bytes },
479
480
        /* TODO: add more */
481
482
        { 0, 0, 0 },
483
};
484
485
static const char *vec[] = {
486
        " 1",
487
        " 12",
488
        " 12.",
489
        " 12.3",
490
        " 12.34",
491
        "N12.34e-3",
492
        "N12.34e3",
493
        "N12.34e+3",
494
        "N+12.34e-3",
495
        "N-12.34e3",
496
        "N.",
497
        "N.12.",
498
        "N12..",
499
        "N12.,",
500
        "N12e,",
501
        "N12e+,",
502
        "N12ee,",
503
        "N1..2",
504
        "NA",
505
        "N1A",
506
        "Ne-3",
507
        NULL
508
};
509
510
int
511 40
main(int argc, char *argv[])
512
{
513 40
        int ec = 0;
514
        struct test_case *tc;
515
        uintmax_t val;
516
        const char **p;
517
        const char *e;
518
        double d1, d2;
519
        const struct test_sf_parse_int *tspi;
520
        const struct test_sf_parse_number *tspn;
521
        int64_t i64;
522
        volatile double dbl;
523
        int sign, consumed;
524
        const char *errtxt;
525
        const char *input;
526
        char buf1[30];
527
        char buf2[30];
528
529 40
        (void)argc;
530
531 40
        setbuf(stdout, NULL);
532 40
        setbuf(stderr, NULL);
533
534 520
        for (tspi = test_sf_parse_int; tspi->input != NULL; tspi++) {
535 480
                errtxt = "(unset)";
536 480
                input = tspi->input;
537 480
                i64 = sf_parse_int(&input, &errtxt, &sign, tspi->maxdig);
538 480
                consumed = input - tspi->input;
539 960
                if (i64 != tspi->retval ||
540 480
                    sign != tspi->sign ||
541 480
                    consumed != tspi->consumed ||
542 480
                    errtxt != tspi->errtxt) {
543 0
                        ec++;
544 0
                        printf("sf_parse_int(%s, maxdig=%d) failed\n",
545 0
                            tspi->input, tspi->maxdig);
546
#ifdef TEST_VERBOSE
547
                        printf("    retval\texpected %jd\tgot %jd\n",
548
                            (intmax_t)tspi->retval, (intmax_t)i64);
549
                        printf("    sign\texpected %d\tgot %d\n",
550
                            tspi->sign, sign);
551
                        printf("    consumed\texpected %d\tgot %d\n",
552
                            tspi->consumed, consumed);
553
                        printf("    errtxt\texpected %p\tgot %p\n",
554
                            tspi->errtxt, errtxt);
555
                        printf("    errtxt\texpected %s\tgot %s\n",
556
                            tspi->errtxt, errtxt);
557
#endif
558 0
                }
559 480
        }
560
561 1000
        for (tspn = test_sf_parse_number; tspn->input != NULL; tspn++) {
562 960
                errtxt = "(unset)";
563 960
                input = tspn->input;
564 960
                dbl = SF_Parse_Number(&input, tspn->strict, &errtxt);
565 960
                consumed = input - tspn->input;
566 960
                bprintf(buf1, "%.4f", dbl);
567 960
                bprintf(buf2, "%.4f", tspn->retval);
568 1920
                if (strcmp(buf1, buf2) ||
569 960
                    consumed != tspn->consumed ||
570 960
                    errtxt != tspn->errtxt) {
571 0
                        ec++;
572 0
                        printf("sf_parse_number(%s, strict=%d) failed\n",
573 0
                            tspn->input, tspn->strict);
574
#ifdef TEST_VERBOSE
575
                        printf("    retval\texpected %.4f\tgot %.4f\t(%e)\n",
576
                            tspn->retval, dbl, dbl - tspn->retval);
577
                        printf("    retval\texpected %a\tgot %a\n",
578
                            tspn->retval, dbl);
579
                        printf("    retval\texpected %s\tgot %s\n",
580
                            buf2, buf1);
581
                        printf("    retval\tdelta %e\n",
582
                            dbl - tspn->retval);
583
                        printf("    consumed\texpected %d\tgot %d\n",
584
                            tspn->consumed, consumed);
585
                        printf("    errtxt\texpected %p\tgot %p\n",
586
                            tspn->errtxt, errtxt);
587
                        printf("    errtxt\texpected %s\tgot %s\n",
588
                            tspn->errtxt, errtxt);
589
#endif
590 0
                }
591 960
        }
592
593 880
        for (p = vec; *p != NULL; p++) {
594 840
                e = *p;
595 840
                d1 = VNUM(e + 1);
596 840
                if (*e == 'N') {
597 640
                        if (!isnan(d1)) {
598 0
                                ec++;
599 0
                                printf("VNUM(%s) not NAN (%g)\n", e + 1, d1);
600 0
                        }
601 640
                } else {
602 200
                        d2 = atof(e + 1);
603 200
                        if (isnan(d1)) {
604 0
                                printf("VNUM(%s) is NAN (%g)\n", e + 1, d1);
605 0
                                ec++;
606 200
                        } else if (fabs((d1 - d2) / d2) > 1e-15) {
607 0
                                printf("VNUM(%s) differs from atof() (%g)\n",
608 0
                                    e + 1, d1);
609 0
                                ec++;
610 0
                        }
611
                }
612 840
        }
613
614 1760
        for (tc = test_cases; tc->str; ++tc) {
615 1720
                e = VNUM_2bytes(tc->str, &val, tc->rel);
616 1720
                if (e != NULL)
617 440
                        val = 0;
618 1720
                if (e == tc->err && val == tc->val)
619 1720
                        continue;
620 0
                ++ec;
621 0
                printf("%s: VNUM_2bytes(\"%s\", %ju)\n",
622 0
                   *argv, tc->str, tc->rel);
623 0
                printf("\tExpected:\tstatus %s - value %ju\n",
624 0
                    tc->err ? tc->err : "Success", tc->val);
625 0
                printf("\tGot:\t\tstatus %s - value %ju\n",
626 0
                    e ? e : "Success", val);
627 0
        }
628 40
        if (!isnan(VNUM_duration(NULL))) {
629 0
                printf("%s: VNUM_Duration(NULL) fail\n", *argv);
630 0
                ++ec;
631 0
        }
632 40
        d1 = VNUM_duration(" 365.24219d ");
633 40
        if (d1 != 31556908.8) {
634 0
                printf("%s: VNUM_Duration() wrong, %.3f delta = %e\n",
635 0
                    *argv, d1, d1 - 31556908.8);
636 0
                ++ec;
637 0
        }
638
        /* TODO: test invalid strings */
639 40
        if (!ec)
640 40
                printf("OK\n");
641 40
        return (ec > 0);
642
}
643
#endif