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 15563551
sf_parse_int(const char **ipp, const char **errtxt, int *sign, int maxdig)
76
{
77 15563551
        int64_t retval = 0;
78 15563551
        int ndig = 0;
79
80 15563551
        AN(ipp);
81 15563551
        AN(*ipp);
82 15563551
        if (errtxt != NULL)
83 15563542
                *errtxt = NULL;
84 15563551
        *sign = 1;
85 15563551
        errno = 0;
86 15564831
        while (vct_isows(*(*ipp)))
87 1280
                (*ipp)++;
88 15563535
        if(*(*ipp) == '-') {
89 2360
                *sign = -1;
90 2360
                (*ipp)++;
91 2360
        }
92 46337233
        while (vct_isdigit(*(*ipp))) {
93 30774138
                ndig++;
94 30774138
                if (ndig > maxdig)
95 440
                        BAIL(err_fatnum);
96 30773698
                retval *= 10;
97 30773698
                retval += *(*ipp)++ - 0x30;
98
        }
99 15563095
        if (ndig == 0)
100 97240
                BAIL(err_no_digits);
101 15466135
        while (vct_isows(*(*ipp)))
102 280
                (*ipp)++;
103 15465855
        return (retval);
104 15563535
}
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 1560
SF_Parse_Integer(const char **ipp, const char **errtxt)
116
{
117
        int64_t retval;
118
        int sign;
119
120 1560
        retval = sf_parse_int(ipp, errtxt, &sign, 15);
121 1560
        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 15561499
SF_Parse_Number(const char **ipp, int strict, const char **errtxt)
134
{
135 15561499
        double retval, scale = 1;
136
        int sign, ndig;
137
138 15561499
        retval = (double)sf_parse_int(ipp, errtxt, &sign, 15);
139 15561499
        if (strict && errno)
140 400
                return (0);
141 15561099
        if (*(*ipp) != '.')
142 9469068
                return (retval * sign);
143 6092031
        if (retval < VRT_DECIMAL_MIN || retval > VRT_DECIMAL_MAX)
144 46
                BAIL(err_fatnum);
145 6091989
        if (*errtxt == err_no_digits && (!vct_isdigit((*ipp)[1])))
146 120
                BAIL(err_no_digits);
147 6091869
        *errtxt = NULL;
148 6091869
        errno = 0;
149 6091869
        do {
150 6091869
                (*ipp)++;
151 23291216
                for(ndig = 0; ndig < 3; ndig++) {
152 18091013
                        scale *= .1;
153 18091013
                        if (!vct_isdigit(*(*ipp)))
154 891666
                                break;
155 17199347
                        retval += scale * (*(*ipp)++ - 0x30);
156 17199347
                }
157 6091869
                if (strict && ndig == 0)
158 80
                        BAIL(err_invalid_num);
159 6091789
                if (strict && vct_isdigit(*(*ipp)))
160 120
                        BAIL(err_fatnum);
161 6092149
                while (vct_isdigit(*(*ipp)))
162 480
                        (*ipp)++;
163 6091669
        } while (0);
164 6092389
        while (vct_isows(*(*ipp)))
165 720
                (*ipp)++;
166 6091669
        return (retval * sign);
167 15561497
}
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 9066280
SF_Parse_Decimal(const char **ipp, int strict, const char **errtxt)
179
{
180
        double retval;
181
182 9066280
        retval = SF_Parse_Number(ipp, strict, errtxt);
183 9066280
        if (errno)
184 160
                return (retval);
185 9066120
        if (retval < VRT_DECIMAL_MIN || retval > VRT_DECIMAL_MAX)
186 0
                BAIL(err_fatnum);
187 9066120
        return (retval);
188 9066280
}
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 36111
VNUM(const char *p)
199
{
200
        const char *t;
201
        double r;
202
203 36111
        r = SF_Parse_Number(&p, 0, &t);
204 36111
        if (errno || *p != '\0')
205 2560
                r = nan("");
206 36111
        return (r);
207
}
208
209
/**********************************************************************/
210
211
vtim_dur
212 833752
VNUM_duration_unit(vtim_dur r, const char *b, const char *e)
213
{
214
        double sc;
215
216 833752
        if (e == NULL)
217 709832
                e = strchr(b, '\0');
218
219 833752
        while (b < e && vct_issp(*b))
220 0
                b++;
221 833752
        if (b == e)
222 160
                return (nan(""));
223
224 833592
        switch (*b++) {
225
        case 's':
226 496192
                sc = 1.0;
227 496192
                break;
228
        case 'm':
229 85600
                if (b < e && *b == 's') {
230 920
                        sc = 1e-3;
231 920
                        b++;
232 920
                } else
233 84680
                        sc = 60.0;
234 85600
                break;
235
        case 'h':
236 250040
                sc = 60.0 * 60.0;
237 250040
                break;
238
        case 'd':
239 720
                sc = 60.0 * 60.0 * 24.0;
240 720
                break;
241
        case 'w':
242 320
                sc = 60.0 * 60.0 * 24.0 * 7.0;
243 320
                break;
244
        case 'y':
245 440
                sc = 60.0 * 60.0 * 24.0 * 365.0;
246 440
                break;
247
        default:
248 280
                return (nan(""));
249
        }
250
251 833392
        while (b < e && vct_issp(*b))
252 80
                b++;
253
254 833312
        if (b < e)
255 80
                return (nan(""));
256
257 833232
        return (r * sc);
258 833752
}
259
260
vtim_dur
261 3432
VNUM_duration(const char *p)
262
{
263
        const char *t;
264
        vtim_dur r;
265
266 3432
        if (p == NULL)
267 560
                return (nan(""));
268
269 2872
        r = SF_Parse_Number(&p, 0, &t);
270
271 2872
        if (errno)
272 80
                return (nan(""));
273
274 2792
        return (VNUM_duration_unit(r, p, NULL));
275 3432
}
276
277
/**********************************************************************/
278
279
int64_t
280 6452520
VNUM_bytes_unit(double r, const char *b, const char *e, uintmax_t rel,
281
    const char **errtxt)
282
{
283 6452520
        double sc = 1.0, tmp;
284
285 6452520
        AN(b);
286 6452520
        AN(errtxt);
287 6452520
        errno = 0;
288 6452520
        if (e == NULL)
289 6450640
                e = strchr(b, '\0');
290
291 6452520
        while (b < e && vct_issp(*b))
292 0
                b++;
293 6452520
        if (b == e) {
294 292040
                if (modf(r, &tmp) != 0.0) {
295 40
                        *errtxt = err_fractional_bytes;
296 40
                        errno = EINVAL;
297 40
                }
298 292040
                return ((int64_t)trunc(sc * r));
299
        }
300
301 6160480
        if (rel != 0 && *b == '%') {
302 160
                r *= rel * 0.01;
303 160
                b++;
304 160
        } else {
305 6160320
                switch (*b) {
306 2367680
                case 'k': case 'K': sc = exp2(10); b++; break;
307 774920
                case 'm': case 'M': sc = exp2(20); b++; break;
308 616040
                case 'g': case 'G': sc = exp2(30); b++; break;
309 240
                case 't': case 'T': sc = exp2(40); b++; break;
310 160
                case 'p': case 'P': sc = exp2(50); b++; break;
311
                case 'b': case 'B':
312 2401000
                        if (modf(r, &tmp) != 0.0) {
313 80
                                *errtxt = err_fractional_bytes;
314 80
                                errno = EINVAL;
315 80
                                return (0);
316
                        }
317 2400920
                        break;
318
                default:
319 280
                        *errtxt = err_unknown_bytes;
320 280
                        errno = EINVAL;
321 280
                        return (0);
322
                }
323 6159960
                if (b < e && (*b == 'b' || *b == 'B'))
324 2402320
                        b++;
325
        }
326 6160200
        while (b < e && vct_issp(*b))
327 80
                b++;
328 6160120
        if (b < e) {
329 0
                *errtxt = err_unknown_bytes;
330 0
                errno = EINVAL;
331 0
                return (0);
332
        }
333 6160120
        return ((int64_t)trunc(sc * r));
334 6452520
}
335
336
const char *
337 6451440
VNUM_2bytes(const char *p, uintmax_t *r, uintmax_t rel)
338
{
339
        double fval;
340
        const char *errtxt;
341
342 6451440
        if (p == NULL || *p == '\0')
343 440
                return (err_invalid_num);
344
345 6451000
        fval = SF_Parse_Number(&p, 1, &errtxt);
346 6451000
        if (errno)
347 320
                return (errtxt);
348 6450680
        if (fval < 0)
349 40
                return (err_invalid_num);
350
351 6450640
        fval = VNUM_bytes_unit(fval, p, NULL, rel, &errtxt);
352 6450640
        if (errno)
353 360
                return (errtxt);
354 6450280
        *r = (uintmax_t)round(fval);
355 6450280
        return (NULL);
356 6451440
}
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 282086
vnum_uint(const char *b, const char *e, const char **p, unsigned base)
371
{
372
        ssize_t u;
373
        unsigned n;
374
375 282086
        AN(b);
376 282086
        AN(p);
377 282086
        if (e == NULL)
378 272566
                e = strchr(b, '\0');
379
380 282086
        u = 0;
381 282086
        if (!vct_ishex(*b) || hex_table[*b - '0'] >= base) {
382 641
                *p = b;
383 641
                return (-1);
384
        }
385
386 740010
        for (; b < e && vct_ishex(*b) && hex_table[*b - '0'] < base; b++) {
387 458605
                if (u > (SSIZE_MAX / base)) {
388 0
                        u = -2;
389 0
                        break;
390
                }
391 458605
                u *= base;
392 458605
                n = hex_table[*b - '0'];
393 458605
                if (u > (SSIZE_MAX - n)) {
394 40
                        u = -2;
395 40
                        break;
396
                }
397 458565
                u += n;
398 458565
        }
399
400 281445
        *p = b;
401 281445
        return (u);
402 282086
}
403
404
ssize_t
405 272567
VNUM_uint(const char *b, const char *e, const char **p)
406
{
407
408 272567
        return (vnum_uint(b, e, p, 10));
409
}
410
411
ssize_t
412 9520
VNUM_hex(const char *b, const char *e, const char **p)
413
{
414
415 9520
        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 40
main(int argc, char *argv[])
578
{
579 40
        int ec = 0;
580
        struct test_case *tc;
581 40
        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 40
        (void)argc;
596
597 40
        setbuf(stdout, NULL);
598 40
        setbuf(stderr, NULL);
599
600 520
        for (tspi = test_sf_parse_int; tspi->input != NULL; tspi++) {
601 480
                errtxt = "(unset)";
602 480
                input = tspi->input;
603 480
                i64 = sf_parse_int(&input, &errtxt, &sign, tspi->maxdig);
604 480
                consumed = input - tspi->input;
605 960
                if (i64 != tspi->retval ||
606 480
                    sign != tspi->sign ||
607 480
                    consumed != tspi->consumed ||
608 480
                    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 480
        }
626
627 1000
        for (tspn = test_sf_parse_number; tspn->input != NULL; tspn++) {
628 960
                errtxt = "(unset)";
629 960
                input = tspn->input;
630 960
                dbl = SF_Parse_Number(&input, tspn->strict, &errtxt);
631 960
                consumed = input - tspn->input;
632 960
                bprintf(buf1, "%.4f", dbl);
633 960
                bprintf(buf2, "%.4f", tspn->retval);
634 1920
                if (strcmp(buf1, buf2) ||
635 960
                    consumed != tspn->consumed ||
636 960
                    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 960
        }
658
659 880
        for (p = vec; *p != NULL; p++) {
660 840
                e = *p;
661 840
                d1 = VNUM(e + 1);
662 840
                if (*e == 'N') {
663 640
                        if (!isnan(d1)) {
664 0
                                ec++;
665 0
                                printf("VNUM(%s) not NAN (%g)\n", e + 1, d1);
666 0
                        }
667 640
                } else {
668 200
                        d2 = atof(e + 1);
669 200
                        if (isnan(d1)) {
670 0
                                printf("VNUM(%s) is NAN (%g)\n", e + 1, d1);
671 0
                                ec++;
672 200
                        } 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 840
        }
679
680 1840
        for (tc = test_vnum_2bytes; tc->str; ++tc) {
681 1800
                e = VNUM_2bytes(tc->str, &val, tc->rel);
682 1800
                if (e != NULL)
683 440
                        val = 0;
684 1800
                if (e == tc->err && val == tc->val)
685 1800
                        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 40
        if (!isnan(VNUM_duration(NULL))) {
695 0
                printf("%s: VNUM_Duration(NULL) fail\n", *argv);
696 0
                ++ec;
697 0
        }
698 40
        d1 = VNUM_duration(" 365.24219d ");
699 40
        d2 = 31556908.8;
700 40
        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 40
        if (!ec)
707 40
                printf("OK\n");
708 40
        return (ec > 0);
709
}
710
#endif