vinyl-cache/lib/libvinyl/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 1646195
sf_parse_int(const char **ipp, const char **errtxt, int *sign, int maxdig)
76
{
77 1646195
        int64_t retval = 0;
78 1646195
        int ndig = 0;
79
80 1646195
        AN(ipp);
81 1646195
        AN(*ipp);
82 1646195
        if (errtxt != NULL)
83 1646194
                *errtxt = NULL;
84 1646195
        *sign = 1;
85 1646195
        errno = 0;
86 1646323
        while (vct_isows(*(*ipp)))
87 128
                (*ipp)++;
88 1646193
        if(*(*ipp) == '-') {
89 264
                *sign = -1;
90 264
                (*ipp)++;
91 264
        }
92 4901026
        while (vct_isdigit(*(*ipp))) {
93 3254877
                ndig++;
94 3254877
                if (ndig > maxdig)
95 44
                        BAIL(err_fatnum);
96 3254833
                retval *= 10;
97 3254833
                retval += *(*ipp)++ - 0x30;
98
        }
99 1646149
        if (ndig == 0)
100 10228
                BAIL(err_no_digits);
101 1635949
        while (vct_isows(*(*ipp)))
102 28
                (*ipp)++;
103 1635921
        return (retval);
104 1646193
}
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 220
SF_Parse_Integer(const char **ipp, const char **errtxt)
116
{
117
        int64_t retval;
118
        int sign;
119
120 220
        retval = sf_parse_int(ipp, errtxt, &sign, 15);
121 220
        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 1645925
SF_Parse_Number(const char **ipp, int strict, const char **errtxt)
134
{
135 1645925
        double retval, scale = 1;
136
        int sign, ndig;
137
138 1645925
        retval = (double)sf_parse_int(ipp, errtxt, &sign, 15);
139 1645925
        if (strict && errno)
140 40
                return (0);
141 1645885
        if (*(*ipp) != '.')
142 1001475
                return (retval * sign);
143 644410
        if (retval < VRT_DECIMAL_MIN || retval > VRT_DECIMAL_MAX)
144 4
                BAIL(err_fatnum);
145 644406
        if (*errtxt == err_no_digits && (!vct_isdigit((*ipp)[1])))
146 12
                BAIL(err_no_digits);
147 644394
        *errtxt = NULL;
148 644394
        errno = 0;
149 644394
        do {
150 644394
                (*ipp)++;
151 2463824
                for(ndig = 0; ndig < 3; ndig++) {
152 1913724
                        scale *= .1;
153 1913724
                        if (!vct_isdigit(*(*ipp)))
154 94294
                                break;
155 1819430
                        retval += scale * (*(*ipp)++ - 0x30);
156 1819430
                }
157 644394
                if (strict && ndig == 0)
158 8
                        BAIL(err_invalid_num);
159 644386
                if (strict && vct_isdigit(*(*ipp)))
160 12
                        BAIL(err_fatnum);
161 644422
                while (vct_isdigit(*(*ipp)))
162 48
                        (*ipp)++;
163 644374
        } while (0);
164 644446
        while (vct_isows(*(*ipp)))
165 72
                (*ipp)++;
166 644374
        return (retval * sign);
167 1645925
}
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 959048
SF_Parse_Decimal(const char **ipp, int strict, const char **errtxt)
179
{
180
        double retval;
181
182 959048
        retval = SF_Parse_Number(ipp, strict, errtxt);
183 959048
        if (errno)
184 16
                return (retval);
185 959032
        if (retval < VRT_DECIMAL_MIN || retval > VRT_DECIMAL_MAX)
186 0
                BAIL(err_fatnum);
187 959032
        return (retval);
188 959048
}
189
190
/**********************************************************************
191
 * Parse a "Vinyl number".
192
 *
193
 * Vinyl 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 3782
VNUM(const char *p)
199
{
200
        const char *t;
201
        double r;
202
203 3782
        r = SF_Parse_Number(&p, 0, &t);
204 3782
        if (errno || *p != '\0')
205 256
                r = nan("");
206 3782
        return (r);
207
}
208
209
/**********************************************************************/
210
211
vtim_dur
212 88216
VNUM_duration_unit(vtim_dur r, const char *b, const char *e)
213
{
214
        double sc;
215
216 88216
        if (e == NULL)
217 75096
                e = strchr(b, '\0');
218
219 88216
        while (b < e && vct_issp(*b))
220 0
                b++;
221 88216
        if (b == e)
222 16
                return (nan(""));
223
224 88200
        switch (*b++) {
225
        case 's':
226 52468
                sc = 1.0;
227 52468
                break;
228
        case 'm':
229 9084
                if (b < e && *b == 's') {
230 104
                        sc = 1e-3;
231 104
                        b++;
232 104
                } else
233 8980
                        sc = 60.0;
234 9084
                break;
235
        case 'h':
236 26472
                sc = 60.0 * 60.0;
237 26472
                break;
238
        case 'd':
239 72
                sc = 60.0 * 60.0 * 24.0;
240 72
                break;
241
        case 'w':
242 32
                sc = 60.0 * 60.0 * 24.0 * 7.0;
243 32
                break;
244
        case 'y':
245 44
                sc = 60.0 * 60.0 * 24.0 * 365.0;
246 44
                break;
247
        default:
248 28
                return (nan(""));
249
        }
250
251 88180
        while (b < e && vct_issp(*b))
252 8
                b++;
253
254 88172
        if (b < e)
255 8
                return (nan(""));
256
257 88164
        return (r * sc);
258 88216
}
259
260
vtim_dur
261 368
VNUM_duration(const char *p)
262
{
263
        const char *t;
264
        vtim_dur r;
265
266 368
        if (p == NULL)
267 56
                return (nan(""));
268
269 312
        r = SF_Parse_Number(&p, 0, &t);
270
271 312
        if (errno)
272 8
                return (nan(""));
273
274 304
        return (VNUM_duration_unit(r, p, NULL));
275 368
}
276
277
/**********************************************************************/
278
279
int64_t
280 682436
VNUM_bytes_unit(double r, const char *b, const char *e, uintmax_t rel,
281
    const char **errtxt)
282
{
283 682436
        double sc = 1.0, tmp;
284
285 682436
        AN(b);
286 682436
        AN(errtxt);
287 682436
        errno = 0;
288 682436
        if (e == NULL)
289 682220
                e = strchr(b, '\0');
290
291 682436
        while (b < e && vct_issp(*b))
292 0
                b++;
293 682436
        if (b == e) {
294 30892
                if (modf(r, &tmp) != 0.0) {
295 4
                        *errtxt = err_fractional_bytes;
296 4
                        errno = EINVAL;
297 4
                }
298 30892
                return ((int64_t)trunc(sc * r));
299
        }
300
301 651544
        if (rel != 0 && *b == '%') {
302 16
                r *= rel * 0.01;
303 16
                b++;
304 16
        } else {
305 651528
                switch (*b) {
306 250444
                case 'k': case 'K': sc = exp2(10); b++; break;
307 81908
                case 'm': case 'M': sc = exp2(20); b++; break;
308 65148
                case 'g': case 'G': sc = exp2(30); b++; break;
309 24
                case 't': case 'T': sc = exp2(40); b++; break;
310 16
                case 'p': case 'P': sc = exp2(50); b++; break;
311
                case 'b': case 'B':
312 253960
                        if (modf(r, &tmp) != 0.0) {
313 8
                                *errtxt = err_fractional_bytes;
314 8
                                errno = EINVAL;
315 8
                                return (0);
316
                        }
317 253952
                        break;
318
                default:
319 28
                        *errtxt = err_unknown_bytes;
320 28
                        errno = EINVAL;
321 28
                        return (0);
322
                }
323 651492
                if (b < e && (*b == 'b' || *b == 'B'))
324 254108
                        b++;
325
        }
326 651516
        while (b < e && vct_issp(*b))
327 8
                b++;
328 651508
        if (b < e) {
329 0
                *errtxt = err_unknown_bytes;
330 0
                errno = EINVAL;
331 0
                return (0);
332
        }
333 651508
        return ((int64_t)trunc(sc * r));
334 682436
}
335
336
const char *
337 682304
VNUM_2bytes(const char *p, uintmax_t *r, uintmax_t rel)
338
{
339
        double fval;
340
        const char *errtxt;
341
342 682304
        if (p == NULL || *p == '\0')
343 48
                return (err_invalid_num);
344
345 682256
        fval = SF_Parse_Number(&p, 1, &errtxt);
346 682256
        if (errno)
347 32
                return (errtxt);
348 682224
        if (fval < 0)
349 4
                return (err_invalid_num);
350
351 682220
        fval = VNUM_bytes_unit(fval, p, NULL, rel, &errtxt);
352 682220
        if (errno)
353 36
                return (errtxt);
354 682184
        *r = (uintmax_t)round(fval);
355 682184
        return (NULL);
356 682304
}
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 27138
vnum_uint(const char *b, const char *e, const char **p, unsigned base)
371
{
372
        ssize_t u;
373
        unsigned n;
374
375 27138
        AN(b);
376 27138
        AN(p);
377 27138
        if (e == NULL)
378 26185
                e = strchr(b, '\0');
379
380 27138
        u = 0;
381 27138
        if (!vct_ishex(*b) || hex_table[*b - '0'] >= base) {
382 69
                *p = b;
383 69
                return (-1);
384
        }
385
386 69124
        for (; b < e && vct_ishex(*b) && hex_table[*b - '0'] < base; b++) {
387 42059
                if (u > (SSIZE_MAX / base)) {
388 0
                        u = -2;
389 0
                        break;
390
                }
391 42059
                u *= base;
392 42059
                n = hex_table[*b - '0'];
393 42059
                if (u > (SSIZE_MAX - n)) {
394 4
                        u = -2;
395 4
                        break;
396
                }
397 42055
                u += n;
398 42055
        }
399
400 27069
        *p = b;
401 27069
        return (u);
402 27138
}
403
404
ssize_t
405 26186
VNUM_uint(const char *b, const char *e, const char **p)
406
{
407
408 26186
        return (vnum_uint(b, e, p, 10));
409
}
410
411
ssize_t
412 952
VNUM_hex(const char *b, const char *e, const char **p)
413
{
414
415 952
        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 4
main(int argc, char *argv[])
578
{
579 4
        int ec = 0;
580
        struct test_case *tc;
581 4
        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 4
        (void)argc;
596
597 4
        setbuf(stdout, NULL);
598 4
        setbuf(stderr, NULL);
599
600 52
        for (tspi = test_sf_parse_int; tspi->input != NULL; tspi++) {
601 48
                errtxt = "(unset)";
602 48
                input = tspi->input;
603 48
                i64 = sf_parse_int(&input, &errtxt, &sign, tspi->maxdig);
604 48
                consumed = input - tspi->input;
605 96
                if (i64 != tspi->retval ||
606 48
                    sign != tspi->sign ||
607 48
                    consumed != tspi->consumed ||
608 48
                    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 48
        }
626
627 100
        for (tspn = test_sf_parse_number; tspn->input != NULL; tspn++) {
628 96
                errtxt = "(unset)";
629 96
                input = tspn->input;
630 96
                dbl = SF_Parse_Number(&input, tspn->strict, &errtxt);
631 96
                consumed = input - tspn->input;
632 96
                bprintf(buf1, "%.4f", dbl);
633 96
                bprintf(buf2, "%.4f", tspn->retval);
634 192
                if (vstrcmp(buf1, buf2) ||
635 96
                    consumed != tspn->consumed ||
636 96
                    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 96
        }
658
659 88
        for (p = vec; *p != NULL; p++) {
660 84
                e = *p;
661 84
                d1 = VNUM(e + 1);
662 84
                if (*e == 'N') {
663 64
                        if (!isnan(d1)) {
664 0
                                ec++;
665 0
                                printf("VNUM(%s) not NAN (%g)\n", e + 1, d1);
666 0
                        }
667 64
                } else {
668 20
                        d2 = atof(e + 1);
669 20
                        if (isnan(d1)) {
670 0
                                printf("VNUM(%s) is NAN (%g)\n", e + 1, d1);
671 0
                                ec++;
672 20
                        } 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 84
        }
679
680 184
        for (tc = test_vnum_2bytes; tc->str; ++tc) {
681 180
                e = VNUM_2bytes(tc->str, &val, tc->rel);
682 180
                if (e != NULL)
683 44
                        val = 0;
684 180
                if (e == tc->err && val == tc->val)
685 180
                        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 4
        if (!isnan(VNUM_duration(NULL))) {
695 0
                printf("%s: VNUM_Duration(NULL) fail\n", *argv);
696 0
                ++ec;
697 0
        }
698 4
        d1 = VNUM_duration(" 365.24219d ");
699 4
        d2 = 31556908.8;
700 4
        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 4
        if (!ec)
707 4
                printf("OK\n");
708 4
        return (ec > 0);
709
}
710
#endif