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 6487267
sf_parse_int(const char **ipp, const char **errtxt, int *sign, int maxdig)
76
{
77 6487267
        int64_t retval = 0;
78 6487267
        int ndig = 0;
79
80 6487267
        AN(ipp);
81 6487267
        AN(*ipp);
82 6487267
        if (errtxt != NULL)
83 6487262
                *errtxt = NULL;
84 6487267
        *sign = 1;
85 6487267
        errno = 0;
86 6487811
        while (vct_isows(*(*ipp)))
87 544
                (*ipp)++;
88 6487259
        if(*(*ipp) == '-') {
89 1003
                *sign = -1;
90 1003
                (*ipp)++;
91 1003
        }
92 19383606
        while (vct_isdigit(*(*ipp))) {
93 12896534
                ndig++;
94 12896534
                if (ndig > maxdig)
95 187
                        BAIL(err_fatnum);
96 12896347
                retval *= 10;
97 12896347
                retval += *(*ipp)++ - 0x30;
98
        }
99 6487072
        if (ndig == 0)
100 41018
                BAIL(err_no_digits);
101 6446173
        while (vct_isows(*(*ipp)))
102 119
                (*ipp)++;
103 6446054
        return (retval);
104 6487259
}
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 663
SF_Parse_Integer(const char **ipp, const char **errtxt)
116
{
117
        int64_t retval;
118
        int sign;
119
120 663
        retval = sf_parse_int(ipp, errtxt, &sign, 15);
121 663
        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 6486394
SF_Parse_Number(const char **ipp, int strict, const char **errtxt)
134
{
135 6486394
        double retval, scale = 1;
136
        int sign, ndig;
137
138 6486394
        retval = (double)sf_parse_int(ipp, errtxt, &sign, 15);
139 6486394
        if (strict && errno)
140 170
                return (0);
141 6486224
        if (*(*ipp) != '.')
142 3972202
                return (retval * sign);
143 2514022
        if (retval < VRT_DECIMAL_MIN || retval > VRT_DECIMAL_MAX)
144 19
                BAIL(err_fatnum);
145 2514003
        if (*errtxt == err_no_digits && (!vct_isdigit((*ipp)[1])))
146 51
                BAIL(err_no_digits);
147 2513952
        *errtxt = NULL;
148 2513952
        errno = 0;
149 2513952
        do {
150 2513952
                (*ipp)++;
151 9602401
                for(ndig = 0; ndig < 3; ndig++) {
152 7464108
                        scale *= .1;
153 7464108
                        if (!vct_isdigit(*(*ipp)))
154 375659
                                break;
155 7088449
                        retval += scale * (*(*ipp)++ - 0x30);
156 7088449
                }
157 2513952
                if (strict && ndig == 0)
158 34
                        BAIL(err_invalid_num);
159 2513918
                if (strict && vct_isdigit(*(*ipp)))
160 51
                        BAIL(err_fatnum);
161 2514071
                while (vct_isdigit(*(*ipp)))
162 204
                        (*ipp)++;
163 2513867
        } while (0);
164 2514173
        while (vct_isows(*(*ipp)))
165 306
                (*ipp)++;
166 2513867
        return (retval * sign);
167 6486392
}
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 3749503
SF_Parse_Decimal(const char **ipp, int strict, const char **errtxt)
179
{
180
        double retval;
181
182 3749503
        retval = SF_Parse_Number(ipp, strict, errtxt);
183 3749503
        if (errno)
184 68
                return (retval);
185 3749435
        if (retval < VRT_DECIMAL_MIN || retval > VRT_DECIMAL_MAX)
186 0
                BAIL(err_fatnum);
187 3749435
        return (retval);
188 3749503
}
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 15226
VNUM(const char *p)
199
{
200
        const char *t;
201
        double r;
202
203 15226
        r = SF_Parse_Number(&p, 0, &t);
204 15226
        if (errno || *p != '\0')
205 1088
                r = nan("");
206 15226
        return (r);
207
}
208
209
/**********************************************************************/
210
211
vtim_dur
212 381157
VNUM_duration_unit(vtim_dur r, const char *b, const char *e)
213
{
214
        double sc;
215
216 381157
        if (e == NULL)
217 281452
                e = strchr(b, '\0');
218
219 381157
        while (b < e && vct_issp(*b))
220 0
                b++;
221 381157
        if (b == e)
222 68
                return (nan(""));
223
224 381089
        switch (*b++) {
225
        case 's':
226 256479
                sc = 1.0;
227 256479
                break;
228
        case 'm':
229 18530
                if (b < e && *b == 's') {
230 357
                        sc = 1e-3;
231 357
                        b++;
232 357
                } else
233 18173
                        sc = 60.0;
234 18530
                break;
235
        case 'h':
236 105332
                sc = 60.0 * 60.0;
237 105332
                break;
238
        case 'd':
239 306
                sc = 60.0 * 60.0 * 24.0;
240 306
                break;
241
        case 'w':
242 136
                sc = 60.0 * 60.0 * 24.0 * 7.0;
243 136
                break;
244
        case 'y':
245 187
                sc = 60.0 * 60.0 * 24.0 * 365.0;
246 187
                break;
247
        default:
248 119
                return (nan(""));
249
        }
250
251 381004
        while (b < e && vct_issp(*b))
252 34
                b++;
253
254 380970
        if (b < e)
255 34
                return (nan(""));
256
257 380936
        return (r * sc);
258 381157
}
259
260
vtim_dur
261 1445
VNUM_duration(const char *p)
262
{
263
        const char *t;
264
        vtim_dur r;
265
266 1445
        if (p == NULL)
267 238
                return (nan(""));
268
269 1207
        r = SF_Parse_Number(&p, 0, &t);
270
271 1207
        if (errno)
272 34
                return (nan(""));
273
274 1173
        return (VNUM_duration_unit(r, p, NULL));
275 1445
}
276
277
/**********************************************************************/
278
279
int64_t
280 2718878
VNUM_bytes_unit(double r, const char *b, const char *e, uintmax_t rel,
281
    const char **errtxt)
282
{
283 2718878
        double sc = 1.0, tmp;
284
285 2718878
        AN(b);
286 2718878
        AN(errtxt);
287 2718878
        errno = 0;
288 2718878
        if (e == NULL)
289 2718079
                e = strchr(b, '\0');
290
291 2718878
        while (b < e && vct_issp(*b))
292 0
                b++;
293 2718878
        if (b == e) {
294 122995
                if (modf(r, &tmp) != 0.0) {
295 17
                        *errtxt = err_fractional_bytes;
296 17
                        errno = EINVAL;
297 17
                }
298 122995
                return ((int64_t)trunc(sc * r));
299
        }
300
301 2595883
        if (rel != 0 && *b == '%') {
302 68
                r *= rel * 0.01;
303 68
                b++;
304 68
        } else {
305 2595815
                switch (*b) {
306 997560
                case 'k': case 'K': sc = exp2(10); b++; break;
307 326672
                case 'm': case 'M': sc = exp2(20); b++; break;
308 259624
                case 'g': case 'G': sc = exp2(30); b++; break;
309 102
                case 't': case 'T': sc = exp2(40); b++; break;
310 68
                case 'p': case 'P': sc = exp2(50); b++; break;
311
                case 'b': case 'B':
312 1011687
                        if (modf(r, &tmp) != 0.0) {
313 34
                                *errtxt = err_fractional_bytes;
314 34
                                errno = EINVAL;
315 34
                                return (0);
316
                        }
317 1011653
                        break;
318
                default:
319 102
                        *errtxt = err_unknown_bytes;
320 102
                        errno = EINVAL;
321 102
                        return (0);
322
                }
323 2595679
                if (b < e && (*b == 'b' || *b == 'B'))
324 1012214
                        b++;
325
        }
326 2595781
        while (b < e && vct_issp(*b))
327 34
                b++;
328 2595747
        if (b < e) {
329 0
                *errtxt = err_unknown_bytes;
330 0
                errno = EINVAL;
331 0
                return (0);
332
        }
333 2595747
        return ((int64_t)trunc(sc * r));
334 2718878
}
335
336
const char *
337 2718419
VNUM_2bytes(const char *p, uintmax_t *r, uintmax_t rel)
338
{
339
        double fval;
340
        const char *errtxt;
341
342 2718419
        if (p == NULL || *p == '\0')
343 187
                return (err_invalid_num);
344
345 2718232
        fval = SF_Parse_Number(&p, 1, &errtxt);
346 2718232
        if (errno)
347 136
                return (errtxt);
348 2718096
        if (fval < 0)
349 17
                return (err_invalid_num);
350
351 2718079
        fval = VNUM_bytes_unit(fval, p, NULL, rel, &errtxt);
352 2718079
        if (errno)
353 136
                return (errtxt);
354 2717943
        *r = (uintmax_t)round(fval);
355 2717943
        return (NULL);
356 2718419
}
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 119239
vnum_uint(const char *b, const char *e, const char **p, unsigned base)
371
{
372
        ssize_t u;
373
        unsigned n;
374
375 119239
        AN(b);
376 119239
        AN(p);
377 119239
        if (e == NULL)
378 115226
                e = strchr(b, '\0');
379
380 119239
        u = 0;
381 119239
        if (!vct_ishex(*b) || hex_table[*b - '0'] >= base) {
382 272
                *p = b;
383 272
                return (-1);
384
        }
385
386 312781
        for (; b < e && vct_ishex(*b) && hex_table[*b - '0'] < base; b++) {
387 193831
                if (u > (SSIZE_MAX / base)) {
388 0
                        u = -2;
389 0
                        break;
390
                }
391 193831
                u *= base;
392 193831
                n = hex_table[*b - '0'];
393 193831
                if (u > (SSIZE_MAX - n)) {
394 17
                        u = -2;
395 17
                        break;
396
                }
397 193814
                u += n;
398 193814
        }
399
400 118967
        *p = b;
401 118967
        return (u);
402 119239
}
403
404
ssize_t
405 115227
VNUM_uint(const char *b, const char *e, const char **p)
406
{
407
408 115227
        return (vnum_uint(b, e, p, 10));
409
}
410
411
ssize_t
412 4012
VNUM_hex(const char *b, const char *e, const char **p)
413
{
414
415 4012
        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 17
main(int argc, char *argv[])
578
{
579 17
        int ec = 0;
580
        struct test_case *tc;
581 17
        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 17
        (void)argc;
596
597 17
        setbuf(stdout, NULL);
598 17
        setbuf(stderr, NULL);
599
600 221
        for (tspi = test_sf_parse_int; tspi->input != NULL; tspi++) {
601 204
                errtxt = "(unset)";
602 204
                input = tspi->input;
603 204
                i64 = sf_parse_int(&input, &errtxt, &sign, tspi->maxdig);
604 204
                consumed = input - tspi->input;
605 408
                if (i64 != tspi->retval ||
606 204
                    sign != tspi->sign ||
607 204
                    consumed != tspi->consumed ||
608 204
                    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 204
        }
626
627 425
        for (tspn = test_sf_parse_number; tspn->input != NULL; tspn++) {
628 408
                errtxt = "(unset)";
629 408
                input = tspn->input;
630 408
                dbl = SF_Parse_Number(&input, tspn->strict, &errtxt);
631 408
                consumed = input - tspn->input;
632 408
                bprintf(buf1, "%.4f", dbl);
633 408
                bprintf(buf2, "%.4f", tspn->retval);
634 816
                if (strcmp(buf1, buf2) ||
635 408
                    consumed != tspn->consumed ||
636 408
                    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 408
        }
658
659 374
        for (p = vec; *p != NULL; p++) {
660 357
                e = *p;
661 357
                d1 = VNUM(e + 1);
662 357
                if (*e == 'N') {
663 272
                        if (!isnan(d1)) {
664 0
                                ec++;
665 0
                                printf("VNUM(%s) not NAN (%g)\n", e + 1, d1);
666 0
                        }
667 272
                } else {
668 85
                        d2 = atof(e + 1);
669 85
                        if (isnan(d1)) {
670 0
                                printf("VNUM(%s) is NAN (%g)\n", e + 1, d1);
671 0
                                ec++;
672 85
                        } 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 357
        }
679
680 782
        for (tc = test_vnum_2bytes; tc->str; ++tc) {
681 765
                e = VNUM_2bytes(tc->str, &val, tc->rel);
682 765
                if (e != NULL)
683 187
                        val = 0;
684 765
                if (e == tc->err && val == tc->val)
685 765
                        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 17
        if (!isnan(VNUM_duration(NULL))) {
695 0
                printf("%s: VNUM_Duration(NULL) fail\n", *argv);
696 0
                ++ec;
697 0
        }
698 17
        d1 = VNUM_duration(" 365.24219d ");
699 17
        d2 = 31556908.8;
700 17
        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 17
        if (!ec)
707 17
                printf("OK\n");
708 17
        return (ec > 0);
709
}
710
#endif