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 8836067
sf_parse_int(const char **ipp, const char **errtxt, int *sign, int maxdig)
76
{
77 8836067
        int64_t retval = 0;
78 8836067
        int ndig = 0;
79
80 8836067
        AN(ipp);
81 8836067
        AN(*ipp);
82 8836067
        if (errtxt != NULL)
83 8836062
                *errtxt = NULL;
84 8836067
        *sign = 1;
85 8836067
        errno = 0;
86 8836803
        while (vct_isows(*(*ipp)))
87 736
                (*ipp)++;
88 8836057
        if(*(*ipp) == '-') {
89 1357
                *sign = -1;
90 1357
                (*ipp)++;
91 1357
        }
92 26400938
        while (vct_isdigit(*(*ipp))) {
93 17565134
                ndig++;
94 17565134
                if (ndig > maxdig)
95 253
                        BAIL(err_fatnum);
96 17564881
                retval *= 10;
97 17564881
                retval += *(*ipp)++ - 0x30;
98
        }
99 8835804
        if (ndig == 0)
100 55820
                BAIL(err_no_digits);
101 8780145
        while (vct_isows(*(*ipp)))
102 161
                (*ipp)++;
103 8779984
        return (retval);
104 8836057
}
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 897
SF_Parse_Integer(const char **ipp, const char **errtxt)
116
{
117
        int64_t retval;
118
        int sign;
119
120 897
        retval = sf_parse_int(ipp, errtxt, &sign, 15);
121 897
        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 8834884
SF_Parse_Number(const char **ipp, int strict, const char **errtxt)
134
{
135 8834884
        double retval, scale = 1;
136
        int sign, ndig;
137
138 8834884
        retval = (double)sf_parse_int(ipp, errtxt, &sign, 15);
139 8834884
        if (strict && errno)
140 230
                return (0);
141 8834654
        if (*(*ipp) != '.')
142 5410236
                return (retval * sign);
143 3424418
        if (retval < VRT_DECIMAL_MIN || retval > VRT_DECIMAL_MAX)
144 25
                BAIL(err_fatnum);
145 3424395
        if (*errtxt == err_no_digits && (!vct_isdigit((*ipp)[1])))
146 69
                BAIL(err_no_digits);
147 3424326
        *errtxt = NULL;
148 3424326
        errno = 0;
149 3424326
        do {
150 3424326
                (*ipp)++;
151 13079788
                for(ndig = 0; ndig < 3; ndig++) {
152 10167112
                        scale *= .1;
153 10167112
                        if (!vct_isdigit(*(*ipp)))
154 511650
                                break;
155 9655462
                        retval += scale * (*(*ipp)++ - 0x30);
156 9655462
                }
157 3424326
                if (strict && ndig == 0)
158 46
                        BAIL(err_invalid_num);
159 3424280
                if (strict && vct_isdigit(*(*ipp)))
160 69
                        BAIL(err_fatnum);
161 3424487
                while (vct_isdigit(*(*ipp)))
162 276
                        (*ipp)++;
163 3424211
        } while (0);
164 3424625
        while (vct_isows(*(*ipp)))
165 414
                (*ipp)++;
166 3424211
        return (retval * sign);
167 8834884
}
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 5107403
SF_Parse_Decimal(const char **ipp, int strict, const char **errtxt)
179
{
180
        double retval;
181
182 5107403
        retval = SF_Parse_Number(ipp, strict, errtxt);
183 5107403
        if (errno)
184 92
                return (retval);
185 5107311
        if (retval < VRT_DECIMAL_MIN || retval > VRT_DECIMAL_MAX)
186 0
                BAIL(err_fatnum);
187 5107311
        return (retval);
188 5107403
}
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 20673
VNUM(const char *p)
199
{
200
        const char *t;
201
        double r;
202
203 20673
        r = SF_Parse_Number(&p, 0, &t);
204 20673
        if (errno || *p != '\0')
205 1472
                r = nan("");
206 20673
        return (r);
207
}
208
209
/**********************************************************************/
210
211
vtim_dur
212 519151
VNUM_duration_unit(vtim_dur r, const char *b, const char *e)
213
{
214
        double sc;
215
216 519151
        if (e == NULL)
217 383382
                e = strchr(b, '\0');
218
219 519151
        while (b < e && vct_issp(*b))
220 0
                b++;
221 519151
        if (b == e)
222 92
                return (nan(""));
223
224 519059
        switch (*b++) {
225
        case 's':
226 349342
                sc = 1.0;
227 349342
                break;
228
        case 'm':
229 25231
                if (b < e && *b == 's') {
230 483
                        sc = 1e-3;
231 483
                        b++;
232 483
                } else
233 24748
                        sc = 60.0;
234 25231
                break;
235
        case 'h':
236 143474
                sc = 60.0 * 60.0;
237 143474
                break;
238
        case 'd':
239 414
                sc = 60.0 * 60.0 * 24.0;
240 414
                break;
241
        case 'w':
242 184
                sc = 60.0 * 60.0 * 24.0 * 7.0;
243 184
                break;
244
        case 'y':
245 253
                sc = 60.0 * 60.0 * 24.0 * 365.0;
246 253
                break;
247
        default:
248 161
                return (nan(""));
249
        }
250
251 518944
        while (b < e && vct_issp(*b))
252 46
                b++;
253
254 518898
        if (b < e)
255 46
                return (nan(""));
256
257 518852
        return (r * sc);
258 519151
}
259
260
vtim_dur
261 1973
VNUM_duration(const char *p)
262
{
263
        const char *t;
264
        vtim_dur r;
265
266 1973
        if (p == NULL)
267 322
                return (nan(""));
268
269 1651
        r = SF_Parse_Number(&p, 0, &t);
270
271 1651
        if (errno)
272 46
                return (nan(""));
273
274 1605
        return (VNUM_duration_unit(r, p, NULL));
275 1973
}
276
277
/**********************************************************************/
278
279
int64_t
280 3703023
VNUM_bytes_unit(double r, const char *b, const char *e, uintmax_t rel,
281
    const char **errtxt)
282
{
283 3703023
        double sc = 1.0, tmp;
284
285 3703023
        AN(b);
286 3703023
        AN(errtxt);
287 3703023
        errno = 0;
288 3703023
        if (e == NULL)
289 3701942
                e = strchr(b, '\0');
290
291 3703023
        while (b < e && vct_issp(*b))
292 0
                b++;
293 3703023
        if (b == e) {
294 167601
                if (modf(r, &tmp) != 0.0) {
295 23
                        *errtxt = err_fractional_bytes;
296 23
                        errno = EINVAL;
297 23
                }
298 167601
                return ((int64_t)trunc(sc * r));
299
        }
300
301 3535422
        if (rel != 0 && *b == '%') {
302 92
                r *= rel * 0.01;
303 92
                b++;
304 92
        } else {
305 3535330
                switch (*b) {
306 1358794
                case 'k': case 'K': sc = exp2(10); b++; break;
307 444705
                case 'm': case 'M': sc = exp2(20); b++; break;
308 353533
                case 'g': case 'G': sc = exp2(30); b++; break;
309 138
                case 't': case 'T': sc = exp2(40); b++; break;
310 92
                case 'p': case 'P': sc = exp2(50); b++; break;
311
                case 'b': case 'B':
312 1377907
                        if (modf(r, &tmp) != 0.0) {
313 46
                                *errtxt = err_fractional_bytes;
314 46
                                errno = EINVAL;
315 46
                                return (0);
316
                        }
317 1377861
                        break;
318
                default:
319 161
                        *errtxt = err_unknown_bytes;
320 161
                        errno = EINVAL;
321 161
                        return (0);
322
                }
323 3535123
                if (b < e && (*b == 'b' || *b == 'B'))
324 1378666
                        b++;
325
        }
326 3535261
        while (b < e && vct_issp(*b))
327 46
                b++;
328 3535215
        if (b < e) {
329 0
                *errtxt = err_unknown_bytes;
330 0
                errno = EINVAL;
331 0
                return (0);
332
        }
333 3535215
        return ((int64_t)trunc(sc * r));
334 3703023
}
335
336
const char *
337 3702402
VNUM_2bytes(const char *p, uintmax_t *r, uintmax_t rel)
338
{
339
        double fval;
340
        const char *errtxt;
341
342 3702402
        if (p == NULL || *p == '\0')
343 253
                return (err_invalid_num);
344
345 3702149
        fval = SF_Parse_Number(&p, 1, &errtxt);
346 3702149
        if (errno)
347 184
                return (errtxt);
348 3701965
        if (fval < 0)
349 23
                return (err_invalid_num);
350
351 3701942
        fval = VNUM_bytes_unit(fval, p, NULL, rel, &errtxt);
352 3701942
        if (errno)
353 207
                return (errtxt);
354 3701735
        *r = (uintmax_t)round(fval);
355 3701735
        return (NULL);
356 3702402
}
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 161875
vnum_uint(const char *b, const char *e, const char **p, unsigned base)
371
{
372
        ssize_t u;
373
        unsigned n;
374
375 161875
        AN(b);
376 161875
        AN(p);
377 161875
        if (e == NULL)
378 156401
                e = strchr(b, '\0');
379
380 161875
        u = 0;
381 161875
        if (!vct_ishex(*b) || hex_table[*b - '0'] >= base) {
382 371
                *p = b;
383 371
                return (-1);
384
        }
385
386 424760
        for (; b < e && vct_ishex(*b) && hex_table[*b - '0'] < base; b++) {
387 263279
                if (u > (SSIZE_MAX / base)) {
388 0
                        u = -2;
389 0
                        break;
390
                }
391 263279
                u *= base;
392 263279
                n = hex_table[*b - '0'];
393 263279
                if (u > (SSIZE_MAX - n)) {
394 23
                        u = -2;
395 23
                        break;
396
                }
397 263256
                u += n;
398 263256
        }
399
400 161504
        *p = b;
401 161504
        return (u);
402 161875
}
403
404
ssize_t
405 156401
VNUM_uint(const char *b, const char *e, const char **p)
406
{
407
408 156401
        return (vnum_uint(b, e, p, 10));
409
}
410
411
ssize_t
412 5474
VNUM_hex(const char *b, const char *e, const char **p)
413
{
414
415 5474
        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 23
main(int argc, char *argv[])
578
{
579 23
        int ec = 0;
580
        struct test_case *tc;
581 23
        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 23
        (void)argc;
596
597 23
        setbuf(stdout, NULL);
598 23
        setbuf(stderr, NULL);
599
600 299
        for (tspi = test_sf_parse_int; tspi->input != NULL; tspi++) {
601 276
                errtxt = "(unset)";
602 276
                input = tspi->input;
603 276
                i64 = sf_parse_int(&input, &errtxt, &sign, tspi->maxdig);
604 276
                consumed = input - tspi->input;
605 552
                if (i64 != tspi->retval ||
606 276
                    sign != tspi->sign ||
607 276
                    consumed != tspi->consumed ||
608 276
                    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 276
        }
626
627 575
        for (tspn = test_sf_parse_number; tspn->input != NULL; tspn++) {
628 552
                errtxt = "(unset)";
629 552
                input = tspn->input;
630 552
                dbl = SF_Parse_Number(&input, tspn->strict, &errtxt);
631 552
                consumed = input - tspn->input;
632 552
                bprintf(buf1, "%.4f", dbl);
633 552
                bprintf(buf2, "%.4f", tspn->retval);
634 1104
                if (strcmp(buf1, buf2) ||
635 552
                    consumed != tspn->consumed ||
636 552
                    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 552
        }
658
659 506
        for (p = vec; *p != NULL; p++) {
660 483
                e = *p;
661 483
                d1 = VNUM(e + 1);
662 483
                if (*e == 'N') {
663 368
                        if (!isnan(d1)) {
664 0
                                ec++;
665 0
                                printf("VNUM(%s) not NAN (%g)\n", e + 1, d1);
666 0
                        }
667 368
                } else {
668 115
                        d2 = atof(e + 1);
669 115
                        if (isnan(d1)) {
670 0
                                printf("VNUM(%s) is NAN (%g)\n", e + 1, d1);
671 0
                                ec++;
672 115
                        } 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 483
        }
679
680 1058
        for (tc = test_vnum_2bytes; tc->str; ++tc) {
681 1035
                e = VNUM_2bytes(tc->str, &val, tc->rel);
682 1035
                if (e != NULL)
683 253
                        val = 0;
684 1035
                if (e == tc->err && val == tc->val)
685 1035
                        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 23
        if (!isnan(VNUM_duration(NULL))) {
695 0
                printf("%s: VNUM_Duration(NULL) fail\n", *argv);
696 0
                ++ec;
697 0
        }
698 23
        d1 = VNUM_duration(" 365.24219d ");
699 23
        d2 = 31556908.8;
700 23
        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 23
        if (!ec)
707 23
                printf("OK\n");
708 23
        return (ec > 0);
709
}
710
#endif