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