varnish-cache/lib/libvarnish/vtim.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2011 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 *
30
 * Semi-trivial functions to handle HTTP header timestamps according to
31
 * RFC 2616 section 3.3.
32
 *
33
 * We must parse four different formats:
34
 *       000000000011111111112222222222
35
 *       012345678901234567890123456789
36
 *       ------------------------------
37
 *      "Sun, 06 Nov 1994 08:49:37 GMT"         RFC822 & RFC1123
38
 *      "Sunday, 06-Nov-94 08:49:37 GMT"        RFC850
39
 *      "Sun Nov  6 08:49:37 1994"              ANSI-C asctime()
40
 *      "1994-11-06T08:49:37"                   ISO 8601
41
 *
42
 * And always output the RFC1123 format.
43
 *
44
 * So why are these functions hand-built ?
45
 *
46
 * Because the people behind POSIX were short-sighted morons who didn't think
47
 * anybody would ever need to deal with timestamps in multiple different
48
 * timezones at the same time -- for that matter, convert timestamps to
49
 * broken down UTC/GMT time.
50
 *
51
 * We could, and used to, get by by smashing our TZ variable to "UTC" but
52
 * that ruins the LOCALE for VMODs.
53
 *
54
 */
55
56
#include "config.h"
57
58
#include <time.h>
59
#include <sys/time.h>
60
61
#include <math.h>
62
#include <stdint.h>
63
#include <stdio.h>
64
#include <stdlib.h>
65
#include <string.h>
66
#ifdef __MACH__
67
#include <mach/mach_time.h>
68
#endif
69
70
#include "vdef.h"
71
72
#include "vas.h"
73
#include "vtim.h"
74
75
/* relax vtim parsing */
76
unsigned VTIM_postel = 0;
77
78
static const char * const weekday_name[] = {
79
        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
80
};
81
82
static const char * const more_weekday[] = {
83
        "day", "day", "sday", "nesday", "rsday", "day", "urday"
84
};
85
86
static const char * const month_name[] = {
87
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
88
        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
89
};
90
91
static const int days_in_month[] = {
92
        31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
93
};
94
95
static const int days_before_month[] = {
96
        0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
97
};
98
99
#ifdef __MACH__
100
// http://stackoverflow.com/a/21352348
101
static uint64_t mt_base;
102
static double   mt_scale;
103
104
static void
105
mach_time_init(void)
106
{
107
        mach_timebase_info_data_t timebase;
108
109
        mt_base = mach_absolute_time();
110
111
        AZ(mach_timebase_info(&timebase));
112
        mt_scale = (double)timebase.numer / (double)timebase.denom * 1e-9;
113
}
114
115
static __attribute__((constructor)) void
116
init(void)
117
{
118
        mach_time_init();
119
}
120
#endif
121
122
/*
123
 * On older Solaris-incarnations, gethrtime() was faster than
124
 * clock_gettime(CLOCK_MONOTONIC). Our configure script prefers
125
 * clock_gettime if it is consistently at least twice as fast as
126
 * gethrtime(), which is the case on modern Solaris descendents.
127
 */
128
129
vtim_mono
130 29259155
VTIM_mono(void)
131
{
132
#if defined(HAVE_GETHRTIME) && defined(USE_GETHRTIME)
133
        return (gethrtime() * 1e-9);
134
#else
135
        struct timespec ts;
136
137 29259155
        AZ(clock_gettime(CLOCK_MONOTONIC, &ts));
138 29259155
        return (ts.tv_sec + 1e-9 * ts.tv_nsec);
139
#endif
140
}
141
142
vtim_real
143 11074982
VTIM_real(void)
144
{
145
#ifdef HAVE_CLOCK_GETTIME
146
        struct timespec ts;
147
148 11074982
        AZ(clock_gettime(CLOCK_REALTIME, &ts));
149 11074982
        return (ts.tv_sec + 1e-9 * ts.tv_nsec);
150
#else
151
        struct timeval tv;
152
153
        AZ(gettimeofday(&tv, NULL));
154
        return (tv.tv_sec + 1e-6 * tv.tv_usec);
155
#endif
156
}
157
158
void
159 247065545
VTIM_format(vtim_real t, char *p)
160
{
161
        struct tm tm;
162
        time_t tt;
163
164 247065545
        AN(p);
165 247065545
        *p = '\0';
166
167 247065545
        if (t < (vtim_real)INTMAX_MIN || t > (vtim_real)INTMAX_MAX)
168 42
                return;
169
170 247065503
        tt = (time_t)(intmax_t)t;
171 247065503
        if (gmtime_r(&tt, &tm) == NULL)
172 0
                return;
173
174 247065503
        AN(snprintf(p, VTIM_FORMAT_SIZE,
175
            "%s, %02d %s %4d %02d:%02d:%02d GMT",
176
            weekday_name[tm.tm_wday],
177
            tm.tm_mday, month_name[tm.tm_mon], tm.tm_year + 1900,
178
            tm.tm_hour, tm.tm_min, tm.tm_sec));
179 247065545
}
180
181
#ifdef TEST_DRIVER
182
#define FAIL()  \
183
        do { printf("\nFAIL <<%d>>\n", __LINE__); return (0); } while (0)
184
#else
185
#define FAIL()  \
186
        do { return (0); } while (0)
187
#endif
188
189
#define DIGIT(mult, fld)                                        \
190
        do {                                                    \
191
                if (*p < '0' || *p > '9')                       \
192
                        FAIL();                                 \
193
                fld += (*p - '0') * mult;                       \
194
                p++;                                            \
195
        } while(0)
196
197
#define MUSTBE(chr)                                             \
198
        do {                                                    \
199
                if (*p != chr)                                  \
200
                        FAIL();                                 \
201
                p++;                                            \
202
        } while(0)
203
204
#define WEEKDAY()                                               \
205
        do {                                                    \
206
                int i;                                          \
207
                for (i = 0; i < 7; i++) {                       \
208
                        if (!memcmp(p, weekday_name[i], 3)) {   \
209
                                weekday = i;                    \
210
                                break;                          \
211
                        }                                       \
212
                }                                               \
213
                if (i == 7)                                     \
214
                        FAIL();                                 \
215
                p += 3;                                         \
216
        } while(0)
217
218
219
#define MONTH()                                                 \
220
        do {                                                    \
221
                int i;                                          \
222
                for (i = 0; i < 12; i++) {                      \
223
                        if (!memcmp(p, month_name[i], 3)) {     \
224
                                month = i + 1;                  \
225
                                break;                          \
226
                        }                                       \
227
                }                                               \
228
                if (i == 12)                                    \
229
                        FAIL();                                 \
230
                p += 3;                                         \
231
        } while(0)
232
233
#define TIMESTAMP()                                             \
234
        do {                                                    \
235
                DIGIT(10, hour);                                \
236
                DIGIT(1, hour);                                 \
237
                MUSTBE(':');                                    \
238
                DIGIT(10, min);                                 \
239
                DIGIT(1, min);                                  \
240
                MUSTBE(':');                                    \
241
                DIGIT(10, sec);                                 \
242
                DIGIT(1, sec);                                  \
243
        } while(0)
244
245
vtim_real
246 775793720
VTIM_parse(const char *p)
247
{
248
        vtim_real t;
249 775793720
        int month = 0, year = 0, weekday = -1, mday = 0;
250 775793720
        int hour = 0, min = 0, sec = 0;
251
        int d, leap;
252
253 775793720
        if (p == NULL || *p == '\0')
254 40
                FAIL();
255
256 775793760
        while (*p == ' ')
257 80
                p++;
258
259 775793680
        if (*p >= '0' && *p <= '9') {
260
                /* ISO8601 -- "1994-11-06T08:49:37" */
261 246845640
                DIGIT(1000, year);
262 246845640
                DIGIT(100, year);
263 246845640
                DIGIT(10, year);
264 246845640
                DIGIT(1, year);
265 246845640
                MUSTBE('-');
266 246845080
                DIGIT(10, month);
267 246845080
                DIGIT(1, month);
268 246845080
                MUSTBE('-');
269 246845080
                DIGIT(10, mday);
270 246845080
                DIGIT(1, mday);
271 246845080
                MUSTBE('T');
272 246845080
                TIMESTAMP();
273 246845080
        } else {
274 2115512859
                WEEKDAY();
275 528947880
                assert(weekday >= 0 && weekday <= 6);
276 528947880
                if (*p == ',') {
277
                        /* RFC822 & RFC1123 - "Sun, 06 Nov 1994 08:49:37 GMT" */
278 246946800
                        p++;
279 246946800
                        MUSTBE(' ');
280 246946800
                        if (VTIM_postel && *p && p[1] == ' ')
281 160
                                DIGIT(1, mday);
282
                        else {
283 246946640
                                DIGIT(10, mday);
284 246946640
                                DIGIT(1, mday);
285
                        }
286 246946800
                        MUSTBE(' ');
287 1611014307
                        MONTH();
288 246946760
                        MUSTBE(' ');
289 246946760
                        DIGIT(1000, year);
290 246946760
                        DIGIT(100, year);
291 246946760
                        DIGIT(10, year);
292 246946760
                        DIGIT(1, year);
293 246946760
                        MUSTBE(' ');
294 246946760
                        TIMESTAMP();
295 246946760
                        MUSTBE(' ');
296 246946760
                        MUSTBE('G');
297 246946720
                        MUSTBE('M');
298 246946720
                        MUSTBE('T');
299 528947800
                } else if (*p == ' ') {
300
                        /* ANSI-C asctime() -- "Sun Nov  6 08:49:37 1994" */
301 246885880
                        p++;
302 1610321600
                        MONTH();
303 246885720
                        MUSTBE(' ');
304 246885720
                        if (*p != ' ')
305 173851280
                                DIGIT(10, mday);
306
                        else
307 73034440
                                p++;
308 246885720
                        DIGIT(1, mday);
309 246885720
                        MUSTBE(' ');
310 246885720
                        TIMESTAMP();
311 246885720
                        MUSTBE(' ');
312 246885720
                        DIGIT(1000, year);
313 246885720
                        DIGIT(100, year);
314 246885720
                        DIGIT(10, year);
315 246885720
                        DIGIT(1, year);
316 317116120
                } else if (!memcmp(p, more_weekday[weekday],
317 35115200
                    strlen(more_weekday[weekday]))) {
318
                        /* RFC850 -- "Sunday, 06-Nov-94 08:49:37 GMT" */
319 35115040
                        p += strlen(more_weekday[weekday]);
320 35115040
                        MUSTBE(',');
321 35115040
                        MUSTBE(' ');
322 35115040
                        DIGIT(10, mday);
323 35115040
                        DIGIT(1, mday);
324 35115040
                        MUSTBE('-');
325 229236680
                        MONTH();
326 35115040
                        MUSTBE('-');
327 35115040
                        DIGIT(10, year);
328 35115040
                        DIGIT(1, year);
329 35115040
                        year += 1900;
330 35115040
                        if (year < 1969)
331 24202080
                                year += 100;
332 35115040
                        MUSTBE(' ');
333 35115040
                        TIMESTAMP();
334 35115040
                        MUSTBE(' ');
335 35115040
                        MUSTBE('G');
336 35115040
                        MUSTBE('M');
337 35115040
                        MUSTBE('T');
338 35115040
                } else
339 160
                        FAIL();
340
        }
341
342 775792720
        while (*p == ' ')
343 160
                p++;
344
345 775792560
        if (*p != '\0')
346 160
                FAIL();
347
348 775792400
        if (sec < 0 || sec > 60)        /* Leapseconds! */
349 160
                FAIL();
350 775792240
        if (min < 0 || min > 59)
351 160
                FAIL();
352 775792080
        if (hour < 0 || hour > 23)
353 160
                FAIL();
354 775791920
        if (month < 1 || month > 12)
355 160
                FAIL();
356 775791760
        if (mday < 1 || mday > days_in_month[month - 1])
357 160
                FAIL();
358 775791600
        if (year < 1899)
359 0
                FAIL();
360
361 775791600
        leap =
362 970187120
            ((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0);
363
364 775791600
        if (month == 2 && mday > 28 && !leap)
365 160
                FAIL();
366
367 775791440
        if (sec == 60)                  /* Ignore Leapseconds */
368 160
                sec--;
369
370 775791440
        t = ((hour * 60.) + min) * 60. + sec;
371
372 775791440
        d = (mday - 1) + days_before_month[month - 1];
373
374 775791440
        if (month > 2 && leap)
375 157262080
                d++;
376
377 775791440
        d += (year % 100) * 365;        /* There are 365 days in a year */
378
379 775791440
        if ((year % 100) > 0)           /* And a leap day every four years */
380 767022320
                d += (((year % 100) - 1) / 4);
381
382 775791440
        d += ((year / 100) - 20) *      /* Days relative to y2000 */
383
            (100 * 365 + 24);           /* 24 leapdays per year in a century */
384
385 775791440
        d += ((year - 1) / 400) - 4;    /* And one more every 400 years */
386
387
        /*
388
         * Now check weekday, if we have one.
389
         * 6 is because 2000-01-01 was a saturday.
390
         * 10000 is to make sure the modulus argument is always positive
391
         */
392 775791440
        if (weekday != -1 && (d + 6 + 7 * 10000) % 7 != weekday)
393 320
                FAIL();
394
395 775791120
        t += d * 86400.;
396
397 775791120
        t += 10957. * 86400.;           /* 10957 days frm UNIX epoch to y2000 */
398
399 775791120
        return (t);
400 775793720
}
401
402
void
403 4005868
VTIM_sleep(vtim_dur t)
404
{
405
        struct timespec ts;
406
407 4005868
        ts = VTIM_timespec(t);
408
409 4005868
        (void)nanosleep(&ts, NULL);
410 4005868
}
411
412
/*
413
 * VTIM_timeval and VTIM_timespec may need variants with different signatures
414
 * when vtim_real / vtim_mono typedefs are changed
415
 */
416
417
struct timeval
418 360647
VTIM_timeval(vtim_dur t)
419
{
420
        struct timeval tv;
421
422 360647
        AZ(isnan(t));
423 360647
        tv.tv_sec = (time_t)trunc(t);
424 360647
        tv.tv_usec = (int)(1e6 * (t - tv.tv_sec));
425 360647
        return (tv);
426
}
427
428
struct timespec
429 4785702
VTIM_timespec(vtim_dur t)
430
{
431
        struct timespec tv;
432
433 4785702
        AZ(isnan(t));
434 4785702
        tv.tv_sec = (time_t)trunc(t);
435 4785702
        tv.tv_nsec = (int)(1e9 * (t - tv.tv_sec));
436 4785702
        return (tv);
437
}
438
439
struct timeval
440 360647
VTIM_timeval_sock(vtim_dur t)
441
{
442
443 360647
        return (VTIM_timeval(isinf(t) ? 0. : vmax(t, 1e-3)));
444
}
445
446
int
447 2171430
VTIM_poll_tmo(vtim_dur tmo)
448
{
449
450 2171430
        if (isinf(tmo))
451 120
                return (-1);
452 2171310
        assert(!isnan(tmo));
453 2171310
        return (vmax_t(int, 0, ((int)(tmo * 1e3))));
454 2171430
}
455
456
#ifdef TEST_DRIVER
457
458
#pragma GCC diagnostic ignored "-Wformat-y2k"
459
460
#include <stdint.h>
461
462
static void
463 160
tst(const char *s, time_t good)
464
{
465
        time_t t;
466
        char buf[BUFSIZ];
467
468 160
        t = VTIM_parse(s);
469 160
        VTIM_format(t, buf);
470 160
        printf("%-30s -> %12jd -> %s\n", s, (intmax_t)t, buf);
471 160
        if (t != good) {
472 0
                printf("Parse error! Got: %jd should have %jd diff %jd\n",
473 0
                    (intmax_t)t, (intmax_t)good, (intmax_t)(t - good));
474 0
                exit(4);
475
        }
476 160
}
477
478
/* XXX keep as double for the time being */
479
static int
480 80
tst_delta_check(const char *name, double begin, double end, vtim_dur ref)
481
{
482 80
        const double tol_max = 1.1;
483 80
        const double tol_min = 1;
484
485 80
        printf("%s delta for %fs sleep: %f\n", name, ref, (end - begin));
486
487 80
        if ((end - begin) > tol_max * ref) {
488 0
                printf("%s delta above tolerance: ((%f - %f) = %f) > %f\n",
489 0
                    name, end, begin, (end - begin), tol_max);
490 0
                return (1);
491 80
        } else if ((end - begin) < tol_min * ref) {
492 0
                printf("%s delta below tolerance: ((%f - %f) = %f) < %f\n",
493 0
                    name, end, begin, (end - begin), tol_min);
494 0
                return (1);
495
        }
496 80
        return (0);
497 80
}
498
499
static void
500 40
tst_delta(void)
501
{
502
        vtim_mono m_begin, m_end;
503
        vtim_real r_begin, r_end;
504 40
        const vtim_dur ref = 1;
505 40
        int err = 0;
506
507 40
        r_begin = VTIM_real();
508 40
        m_begin = VTIM_mono();
509 40
        VTIM_sleep(ref);
510 40
        r_end = VTIM_real();
511 40
        m_end = VTIM_mono();
512
513 40
        err += tst_delta_check("VTIM_mono", m_begin, m_end, ref);
514 40
        err += tst_delta_check("VTIM_real", r_begin, r_end, ref);
515
516 40
        if (err) {
517 0
                printf("%d time delta test errors\n", err);
518 0
                exit(4);
519
        }
520 40
}
521
522
static void
523 40
bench(void)
524
{
525
        vtim_mono s, e;
526
        vtim_mono t_m;
527
        vtim_real t_r;
528
        unsigned long t_i;
529
        int i;
530
        char buf[64];
531
532 40
        t_m = 0;
533 40
        t_r = 0;
534 40
        s = VTIM_mono();
535 4000040
        for (i=0; i<100000; i++)
536 4000000
                t_r += VTIM_real();
537 40
        e = VTIM_mono();
538 40
        printf("real: %fs / %d = %fns - tst val %f\n",
539 40
            e - s, i, 1e9 * (e - s) / i, t_r);
540
541 40
        t_i = 0;
542 40
        s = VTIM_mono();
543 4000040
        for (i=0; i<100000; i++)
544 4000000
                t_m += VTIM_mono();
545 40
        e = VTIM_mono();
546 40
        printf("mono: %fs / %d = %fns - tst val %f\n",
547 40
            e - s, i, 1e9 * (e - s) / i, t_m);
548
549 40
        t_i = 0;
550 40
        s = VTIM_mono();
551 4000040
        for (i=0; i<100000; i++) {
552 4000000
                snprintf(buf, sizeof(buf), "%.6f", s);
553 4000000
                t_i += buf[4];
554 4000000
        }
555 40
        e = VTIM_mono();
556 40
        printf("printf %%.6f: %fs / %d = %fns - tst val %lu %s\n",
557 40
            e - s, i, 1e9 * (e - s) / i, t_i, buf);
558
559 40
        t_i = 0;
560 40
        s = VTIM_mono();
561 4000040
        for (i=0; i<100000; i++) {
562 8000000
                snprintf(buf, sizeof(buf), "%ju.%06ju",
563 4000000
                    (uintmax_t)floor(s),
564 4000000
                    (uintmax_t)floor((s * 1e6)) % 1000000UL);
565 4000000
                t_i += buf[4];
566 4000000
        }
567 40
        e = VTIM_mono();
568 40
        printf("printf %%ju.%%06ju: %fs / %d = %fns - tst val %lu %s\n",
569 40
            e - s, i, 1e9 * (e - s) / i, t_i, buf);
570 40
}
571
572
static void
573 775607880
parse_check(time_t t, const char *s)
574
{
575
        vtim_real tt;
576
        char buf[BUFSIZ];
577
578 775607880
        tt = VTIM_parse(s);
579 775607880
        if (tt != t) {
580 0
                VTIM_format(tt, buf);
581 0
                printf("  fm: %12jd <%s>\n", (intmax_t)t, s);
582 0
                printf("  to: %12.0f <%s>\n", tt, buf);
583 0
                exit(2);
584
        }
585 775607880
}
586
587
#define TTEST_MIN (sizeof(time_t) >= 8 ? -2209852800LL : INT32_MIN)
588
#define TTEST_MAX (sizeof(time_t) >= 8 ? 20000000000LL : INT32_MAX)
589
590
int
591 40
main(int argc, char **argv)
592
{
593
        time_t t;
594
        intmax_t iter;
595
        struct tm tm;
596
        char buf[BUFSIZ];
597
        char buf1[BUFSIZ];
598
599 40
        (void)argc;
600 40
        (void)argv;
601
602 40
        AZ(setenv("TZ", "UTC", 1));
603
604 40
        bench();
605
606
        /* Brute force test against libc version */
607 246844760
        for (iter = TTEST_MIN; iter < TTEST_MAX; iter += 3599) {
608 246844720
                t = (time_t)iter;
609 246844720
                gmtime_r(&t, &tm);
610 246844720
                strftime(buf1, sizeof buf1, "%a, %d %b %Y %T GMT", &tm);
611 246844720
                VTIM_format(t, buf);
612 246844720
                if (strcmp(buf, buf1)) {
613 0
                        printf("libc: <%s> Vtim <%s> %jd\n",
614 0
                            buf1, buf, (intmax_t)t);
615 0
                        exit(2);
616
                }
617 246844720
                parse_check(t, buf1);
618
619 246844720
                strftime(buf1, sizeof buf1, "%a %b %e %T %Y", &tm);
620 246844720
                parse_check(t, buf1);
621
622 246844720
                strftime(buf1, sizeof buf1, "%Y-%m-%dT%T", &tm);
623 246844720
                parse_check(t, buf1);
624
625 246844720
                if (tm.tm_year >= 69 && tm.tm_year < 169) {
626 35073720
                        strftime(buf1, sizeof buf1, "%A, %d-%b-%y %T GMT", &tm);
627 35073720
                        parse_check(t, buf1);
628 35073720
                }
629 246844720
        }
630
631
        /* Examples from RFC2616 section 3.3.1 */
632 40
        tst("Sun, 06 Nov 1994 08:49:37 GMT", 784111777);
633 40
        tst("Sunday, 06-Nov-94 08:49:37 GMT", 784111777);
634 40
        tst("Sun Nov  6 08:49:37 1994", 784111777);
635
636 40
        tst("1994-11-06T08:49:37", 784111777);
637
638 40
        tst_delta();
639
640 40
        return (0);
641
}
642
643
#endif