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 381604
sf_parse_int(const char **ipp, const char **errtxt, int *sign, int maxdig)
76
{
77 381604
        int64_t retval = 0;
78 381604
        int ndig = 0;
79
80 381604
        AN(ipp);
81 381604
        AN(*ipp);
82 381604
        if (errtxt != NULL)
83 381603
                *errtxt = NULL;
84 381604
        *sign = 1;
85 381604
        errno = 0;
86 381636
        while (vct_isows(*(*ipp)))
87 32
                (*ipp)++;
88 381602
        if(*(*ipp) == '-') {
89 59
                *sign = -1;
90 59
                (*ipp)++;
91 59
        }
92 1140210
        while (vct_isdigit(*(*ipp))) {
93 758619
                ndig++;
94 758619
                if (ndig > maxdig)
95 11
                        BAIL(err_fatnum);
96 758608
                retval *= 10;
97 758608
                retval += *(*ipp)++ - 0x30;
98
        }
99 381591
        if (ndig == 0)
100 2412
                BAIL(err_no_digits);
101 379186
        while (vct_isows(*(*ipp)))
102 7
                (*ipp)++;
103 379179
        return (retval);
104 381602
}
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 39
SF_Parse_Integer(const char **ipp, const char **errtxt)
116
{
117
        int64_t retval;
118
        int sign;
119
120 39
        retval = sf_parse_int(ipp, errtxt, &sign, 15);
121 39
        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 381551
SF_Parse_Number(const char **ipp, int strict, const char **errtxt)
134
{
135 381551
        double retval, scale = 1;
136
        int sign, ndig;
137
138 381551
        retval = (double)sf_parse_int(ipp, errtxt, &sign, 15);
139 381551
        if (strict && errno)
140 10
                return (0);
141 381541
        if (*(*ipp) != '.')
142 233658
                return (retval * sign);
143 147883
        if (retval < VRT_DECIMAL_MIN || retval > VRT_DECIMAL_MAX)
144 1
                BAIL(err_fatnum);
145 147882
        if (*errtxt == err_no_digits && (!vct_isdigit((*ipp)[1])))
146 3
                BAIL(err_no_digits);
147 147879
        *errtxt = NULL;
148 147879
        errno = 0;
149 147879
        do {
150 147879
                (*ipp)++;
151 564846
                for(ndig = 0; ndig < 3; ndig++) {
152 439064
                        scale *= .1;
153 439064
                        if (!vct_isdigit(*(*ipp)))
154 22097
                                break;
155 416967
                        retval += scale * (*(*ipp)++ - 0x30);
156 416967
                }
157 147879
                if (strict && ndig == 0)
158 2
                        BAIL(err_invalid_num);
159 147877
                if (strict && vct_isdigit(*(*ipp)))
160 3
                        BAIL(err_fatnum);
161 147886
                while (vct_isdigit(*(*ipp)))
162 12
                        (*ipp)++;
163 147874
        } while (0);
164 147892
        while (vct_isows(*(*ipp)))
165 18
                (*ipp)++;
166 147874
        return (retval * sign);
167 381551
}
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 220559
SF_Parse_Decimal(const char **ipp, int strict, const char **errtxt)
179
{
180
        double retval;
181
182 220559
        retval = SF_Parse_Number(ipp, strict, errtxt);
183 220559
        if (errno)
184 4
                return (retval);
185 220555
        if (retval < VRT_DECIMAL_MIN || retval > VRT_DECIMAL_MAX)
186 0
                BAIL(err_fatnum);
187 220555
        return (retval);
188 220559
}
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 896
VNUM(const char *p)
199
{
200
        const char *t;
201
        double r;
202
203 896
        r = SF_Parse_Number(&p, 0, &t);
204 896
        if (errno || *p != '\0')
205 64
                r = nan("");
206 896
        return (r);
207
}
208
209
/**********************************************************************/
210
211
vtim_dur
212 22420
VNUM_duration_unit(vtim_dur r, const char *b, const char *e)
213
{
214
        double sc;
215
216 22420
        if (e == NULL)
217 16555
                e = strchr(b, '\0');
218
219 22420
        while (b < e && vct_issp(*b))
220 0
                b++;
221 22420
        if (b == e)
222 4
                return (nan(""));
223
224 22416
        switch (*b++) {
225
        case 's':
226 15086
                sc = 1.0;
227 15086
                break;
228
        case 'm':
229 1090
                if (b < e && *b == 's') {
230 21
                        sc = 1e-3;
231 21
                        b++;
232 21
                } else
233 1069
                        sc = 60.0;
234 1090
                break;
235
        case 'h':
236 6196
                sc = 60.0 * 60.0;
237 6196
                break;
238
        case 'd':
239 18
                sc = 60.0 * 60.0 * 24.0;
240 18
                break;
241
        case 'w':
242 8
                sc = 60.0 * 60.0 * 24.0 * 7.0;
243 8
                break;
244
        case 'y':
245 11
                sc = 60.0 * 60.0 * 24.0 * 365.0;
246 11
                break;
247
        default:
248 7
                return (nan(""));
249
        }
250
251 22411
        while (b < e && vct_issp(*b))
252 2
                b++;
253
254 22409
        if (b < e)
255 2
                return (nan(""));
256
257 22407
        return (r * sc);
258 22420
}
259
260
vtim_dur
261 84
VNUM_duration(const char *p)
262
{
263
        const char *t;
264
        vtim_dur r;
265
266 84
        if (p == NULL)
267 14
                return (nan(""));
268
269 70
        r = SF_Parse_Number(&p, 0, &t);
270
271 70
        if (errno)
272 2
                return (nan(""));
273
274 68
        return (VNUM_duration_unit(r, p, NULL));
275 84
}
276
277
/**********************************************************************/
278
279
int64_t
280 159934
VNUM_bytes_unit(double r, const char *b, const char *e, uintmax_t rel,
281
    const char **errtxt)
282
{
283 159934
        double sc = 1.0, tmp;
284
285 159934
        AN(b);
286 159934
        AN(errtxt);
287 159934
        errno = 0;
288 159934
        if (e == NULL)
289 159887
                e = strchr(b, '\0');
290
291 159934
        while (b < e && vct_issp(*b))
292 0
                b++;
293 159934
        if (b == e) {
294 7235
                if (modf(r, &tmp) != 0.0) {
295 1
                        *errtxt = err_fractional_bytes;
296 1
                        errno = EINVAL;
297 1
                }
298 7235
                return ((int64_t)trunc(sc * r));
299
        }
300
301 152699
        if (rel != 0 && *b == '%') {
302 4
                r *= rel * 0.01;
303 4
                b++;
304 4
        } else {
305 152695
                switch (*b) {
306 58680
                case 'k': case 'K': sc = exp2(10); b++; break;
307 19216
                case 'm': case 'M': sc = exp2(20); b++; break;
308 15272
                case 'g': case 'G': sc = exp2(30); b++; break;
309 6
                case 't': case 'T': sc = exp2(40); b++; break;
310 4
                case 'p': case 'P': sc = exp2(50); b++; break;
311
                case 'b': case 'B':
312 59511
                        if (modf(r, &tmp) != 0.0) {
313 2
                                *errtxt = err_fractional_bytes;
314 2
                                errno = EINVAL;
315 2
                                return (0);
316
                        }
317 59509
                        break;
318
                default:
319 6
                        *errtxt = err_unknown_bytes;
320 6
                        errno = EINVAL;
321 6
                        return (0);
322
                }
323 152687
                if (b < e && (*b == 'b' || *b == 'B'))
324 59542
                        b++;
325
        }
326 152693
        while (b < e && vct_issp(*b))
327 2
                b++;
328 152691
        if (b < e) {
329 0
                *errtxt = err_unknown_bytes;
330 0
                errno = EINVAL;
331 0
                return (0);
332
        }
333 152691
        return ((int64_t)trunc(sc * r));
334 159934
}
335
336
const char *
337 159907
VNUM_2bytes(const char *p, uintmax_t *r, uintmax_t rel)
338
{
339
        double fval;
340
        const char *errtxt;
341
342 159907
        if (p == NULL || *p == '\0')
343 11
                return (err_invalid_num);
344
345 159896
        fval = SF_Parse_Number(&p, 1, &errtxt);
346 159896
        if (errno)
347 8
                return (errtxt);
348 159888
        if (fval < 0)
349 1
                return (err_invalid_num);
350
351 159887
        fval = VNUM_bytes_unit(fval, p, NULL, rel, &errtxt);
352 159887
        if (errno)
353 8
                return (errtxt);
354 159879
        *r = (uintmax_t)round(fval);
355 159879
        return (NULL);
356 159907
}
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 7013
vnum_uint(const char *b, const char *e, const char **p, unsigned base)
371
{
372
        ssize_t u;
373
        unsigned n;
374
375 7013
        AN(b);
376 7013
        AN(p);
377 7013
        if (e == NULL)
378 6777
                e = strchr(b, '\0');
379
380 7013
        u = 0;
381 7013
        if (!vct_ishex(*b) || hex_table[*b - '0'] >= base) {
382 15
                *p = b;
383 15
                return (-1);
384
        }
385
386 18399
        for (; b < e && vct_ishex(*b) && hex_table[*b - '0'] < base; b++) {
387 11402
                if (u > (SSIZE_MAX / base)) {
388 0
                        u = -2;
389 0
                        break;
390
                }
391 11402
                u *= base;
392 11402
                n = hex_table[*b - '0'];
393 11402
                if (u > (SSIZE_MAX - n)) {
394 1
                        u = -2;
395 1
                        break;
396
                }
397 11401
                u += n;
398 11401
        }
399
400 6998
        *p = b;
401 6998
        return (u);
402 7013
}
403
404
ssize_t
405 6777
VNUM_uint(const char *b, const char *e, const char **p)
406
{
407
408 6777
        return (vnum_uint(b, e, p, 10));
409
}
410
411
ssize_t
412 236
VNUM_hex(const char *b, const char *e, const char **p)
413
{
414
415 236
        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_vnum_2bytes[] = {
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
        /* 32bit limits */
533
        { "4294967295b",        (uintmax_t)0,   (uintmax_t)4294967295ULL},
534
        { "4294967294b",        (uintmax_t)0,   (uintmax_t)4294967294ULL},
535
536
        /* Check the error checks */
537
        { "",                   0,      0,      err_invalid_num },
538
        { "-1",                 0,      0,      err_invalid_num },
539
        { "1.3",                0,      0,      err_fractional_bytes},
540
        { "1.5011%",            0,      0,      err_fatnum },
541
        { "-",                  0,      0,      err_no_digits },
542
        { "m",                  0,      0,      err_no_digits },
543
        { "4%",                 0,      0,      err_unknown_bytes },
544
        { "3*",                 0,      0,      err_unknown_bytes },
545
546
        /* TODO: add more */
547
548
        { 0, 0, 0 },
549
};
550
551
static const char *vec[] = {
552
        " 1",
553
        " 12",
554
        " 12.",
555
        " 12.3",
556
        " 12.34",
557
        "N12.34e-3",
558
        "N12.34e3",
559
        "N12.34e+3",
560
        "N+12.34e-3",
561
        "N-12.34e3",
562
        "N.",
563
        "N.12.",
564
        "N12..",
565
        "N12.,",
566
        "N12e,",
567
        "N12e+,",
568
        "N12ee,",
569
        "N1..2",
570
        "NA",
571
        "N1A",
572
        "Ne-3",
573
        NULL
574
};
575
576
int
577 1
main(int argc, char *argv[])
578
{
579 1
        int ec = 0;
580
        struct test_case *tc;
581 1
        uintmax_t val = 0;
582
        const char **p;
583
        const char *e;
584
        double d1, d2;
585
        const struct test_sf_parse_int *tspi;
586
        const struct test_sf_parse_number *tspn;
587
        int64_t i64;
588
        volatile double dbl;
589
        int sign, consumed;
590
        const char *errtxt;
591
        const char *input;
592
        char buf1[30];
593
        char buf2[30];
594
595 1
        (void)argc;
596
597 1
        setbuf(stdout, NULL);
598 1
        setbuf(stderr, NULL);
599
600 13
        for (tspi = test_sf_parse_int; tspi->input != NULL; tspi++) {
601 12
                errtxt = "(unset)";
602 12
                input = tspi->input;
603 12
                i64 = sf_parse_int(&input, &errtxt, &sign, tspi->maxdig);
604 12
                consumed = input - tspi->input;
605 24
                if (i64 != tspi->retval ||
606 12
                    sign != tspi->sign ||
607 12
                    consumed != tspi->consumed ||
608 12
                    errtxt != tspi->errtxt) {
609 0
                        ec++;
610 0
                        printf("sf_parse_int(%s, maxdig=%d) failed\n",
611 0
                            tspi->input, tspi->maxdig);
612
#ifdef TEST_VERBOSE
613
                        printf("    retval\texpected %jd\tgot %jd\n",
614
                            (intmax_t)tspi->retval, (intmax_t)i64);
615
                        printf("    sign\texpected %d\tgot %d\n",
616
                            tspi->sign, sign);
617
                        printf("    consumed\texpected %d\tgot %d\n",
618
                            tspi->consumed, consumed);
619
                        printf("    errtxt\texpected %p\tgot %p\n",
620
                            tspi->errtxt, errtxt);
621
                        printf("    errtxt\texpected %s\tgot %s\n",
622
                            tspi->errtxt, errtxt);
623
#endif
624 0
                }
625 12
        }
626
627 25
        for (tspn = test_sf_parse_number; tspn->input != NULL; tspn++) {
628 24
                errtxt = "(unset)";
629 24
                input = tspn->input;
630 24
                dbl = SF_Parse_Number(&input, tspn->strict, &errtxt);
631 24
                consumed = input - tspn->input;
632 24
                bprintf(buf1, "%.4f", dbl);
633 24
                bprintf(buf2, "%.4f", tspn->retval);
634 48
                if (strcmp(buf1, buf2) ||
635 24
                    consumed != tspn->consumed ||
636 24
                    errtxt != tspn->errtxt) {
637 0
                        ec++;
638 0
                        printf("sf_parse_number(%s, strict=%d) failed\n",
639 0
                            tspn->input, tspn->strict);
640
#ifdef TEST_VERBOSE
641
                        printf("    retval\texpected %.4f\tgot %.4f\t(%e)\n",
642
                            tspn->retval, dbl, dbl - tspn->retval);
643
                        printf("    retval\texpected %a\tgot %a\n",
644
                            tspn->retval, dbl);
645
                        printf("    retval\texpected %s\tgot %s\n",
646
                            buf2, buf1);
647
                        printf("    retval\tdelta %e\n",
648
                            dbl - tspn->retval);
649
                        printf("    consumed\texpected %d\tgot %d\n",
650
                            tspn->consumed, consumed);
651
                        printf("    errtxt\texpected %p\tgot %p\n",
652
                            tspn->errtxt, errtxt);
653
                        printf("    errtxt\texpected %s\tgot %s\n",
654
                            tspn->errtxt, errtxt);
655
#endif
656 0
                }
657 24
        }
658
659 22
        for (p = vec; *p != NULL; p++) {
660 21
                e = *p;
661 21
                d1 = VNUM(e + 1);
662 21
                if (*e == 'N') {
663 16
                        if (!isnan(d1)) {
664 0
                                ec++;
665 0
                                printf("VNUM(%s) not NAN (%g)\n", e + 1, d1);
666 0
                        }
667 16
                } else {
668 5
                        d2 = atof(e + 1);
669 5
                        if (isnan(d1)) {
670 0
                                printf("VNUM(%s) is NAN (%g)\n", e + 1, d1);
671 0
                                ec++;
672 5
                        } else if (fabs((d1 - d2) / d2) > 1e-15) {
673 0
                                printf("VNUM(%s) differs from atof() (%g)\n",
674 0
                                    e + 1, d1);
675 0
                                ec++;
676 0
                        }
677
                }
678 21
        }
679
680 46
        for (tc = test_vnum_2bytes; tc->str; ++tc) {
681 45
                e = VNUM_2bytes(tc->str, &val, tc->rel);
682 45
                if (e != NULL)
683 11
                        val = 0;
684 45
                if (e == tc->err && val == tc->val)
685 45
                        continue;
686 0
                ++ec;
687 0
                printf("%s: VNUM_2bytes(\"%s\", %ju)\n",
688 0
                   *argv, tc->str, tc->rel);
689 0
                printf("\tExpected:\tstatus %s - value %ju\n",
690 0
                    tc->err ? tc->err : "Success", tc->val);
691 0
                printf("\tGot:\t\tstatus %s - value %ju\n",
692 0
                    e ? e : "Success", val);
693 0
        }
694 1
        if (!isnan(VNUM_duration(NULL))) {
695 0
                printf("%s: VNUM_Duration(NULL) fail\n", *argv);
696 0
                ++ec;
697 0
        }
698 1
        d1 = VNUM_duration(" 365.24219d ");
699 1
        d2 = 31556908.8;
700 1
        if (fabs(d1 - d2) > VNUM_EPSILON) {
701 0
                printf("%s: VNUM_Duration() wrong, %.3f delta = %e\n",
702 0
                    *argv, d1, d1 - d2);
703 0
                ++ec;
704 0
        }
705
        /* TODO: test invalid strings */
706 1
        if (!ec)
707 1
                printf("OK\n");
708 1
        return (ec > 0);
709
}
710
#endif