Coverage Report

Created: 2023-03-26 06:05

/src/fluent-bit/src/flb_strptime.c
Line
Count
Source (jump to first uncovered line)
1
/*  $OpenBSD: strptime.c,v 1.30 2019/05/12 12:49:52 schwarze Exp $ */
2
/*  $NetBSD: strptime.c,v 1.12 1998/01/20 21:39:40 mycroft Exp $  */
3
/*-
4
 * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.
5
 * All rights reserved.
6
 *
7
 * This code was contributed to The NetBSD Foundation by Klaus Klein.
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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
 * POSSIBILITY OF SUCH DAMAGE.
29
 */
30
31
/*
32
 * This file provides a portable implementation of strptime(2), based
33
 * on the work of OpenSBD project. Since various platforms implement
34
 * strptime differently, this one should work as a fallback.
35
 */
36
37
#include <ctype.h>
38
#include <locale.h>
39
#include <stdint.h>
40
#include <string.h>
41
42
#include <fluent-bit/flb_compat.h>
43
#include <fluent-bit/flb_langinfo.h>
44
#include <fluent-bit/flb_time.h>
45
46
135k
#define _ctloc(x)   (nl_langinfo(x))
47
48
/*
49
 * We do not implement alternate representations. However, we always
50
 * check whether a given modifier is allowed for a certain conversion.
51
 */
52
0
#define _ALT_E      0x01
53
0
#define _ALT_O      0x02
54
44.1k
#define _LEGAL_ALT(x)   { if (alt_format & ~(x)) return (0); }
55
56
/*
57
 * Copied from libc/time/private.h and libc/time/tzfile.h
58
 */
59
34.9k
#define TM_YEAR_BASE  1900
60
467
#define DAYSPERNYEAR  365
61
#define DAYSPERLYEAR  366
62
1.75k
#define DAYSPERWEEK   7
63
0
#define MONSPERYEAR   12
64
934
#define EPOCH_YEAR    1970
65
467
#define EPOCH_WDAY    4  /* Thursday */
66
68
#define SECSPERHOUR   3600
67
31
#define SECSPERMIN    60
68
69
3.46k
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
70
71
/*
72
 * We keep track of some of the fields we set in order to compute missing ones.
73
 */
74
6.54k
#define FIELD_TM_MON  (1 << 0)
75
2.78k
#define FIELD_TM_MDAY (1 << 1)
76
675
#define FIELD_TM_WDAY (1 << 2)
77
7.39k
#define FIELD_TM_YDAY (1 << 3)
78
13.1k
#define FIELD_TM_YEAR (1 << 4)
79
80
static char gmt[] = { "GMT" };
81
static char utc[] = { "UTC" };
82
/* RFC-822/RFC-2822 */
83
static const char * const nast[5] = {
84
       "EST",    "CST",    "MST",    "PST",    "\0\0\0"
85
};
86
static const char * const nadt[5] = {
87
       "EDT",    "CDT",    "MDT",    "PDT",    "\0\0\0"
88
};
89
90
static const int mon_lengths[2][MONSPERYEAR] = {
91
        { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
92
        { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
93
};
94
95
static nl_item day[] = {
96
        DAY_1, DAY_2, DAY_3, DAY_4, DAY_5, DAY_6, DAY_7
97
};
98
99
static nl_item mon[] = {
100
        MON_1, MON_2, MON_3, MON_4, MON_5, MON_6, MON_7, MON_8, MON_9,
101
        MON_10, MON_11, MON_12
102
};
103
104
static nl_item abday[] = {
105
        ABDAY_1, ABDAY_2, ABDAY_3, ABDAY_4, ABDAY_5, ABDAY_6, ABDAY_7
106
};
107
108
static nl_item abmon[] = {
109
        ABMON_1, ABMON_2, ABMON_3, ABMON_4, ABMON_5, ABMON_6, ABMON_7,
110
        ABMON_8, ABMON_9, ABMON_10, ABMON_11, ABMON_12
111
};
112
113
static  int _conv_num64(const unsigned char **, int64_t *, int64_t, int64_t);
114
static  int _conv_num(const unsigned char **, int *, int, int);
115
static  int leaps_thru_end_of(const int y);
116
static  char *_flb_strptime(const char *, const char *, struct flb_tm *, int);
117
static  const u_char *_find_string(const u_char *, int *, const char * const *,
118
      const char * const *, int);
119
120
/*
121
 * FreeBSD does not support `timezone` in time.h.
122
 * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=24590
123
 */
124
#ifdef __FreeBSD__
125
int flb_timezone(void)
126
{
127
    struct tm tm;
128
    time_t t = 0;
129
    tzset();
130
    localtime_r(&t, &tm);
131
    return -(tm.tm_gmtoff);
132
}
133
#define timezone (flb_timezone())
134
#endif
135
136
char *
137
flb_strptime(const char *buf, const char *fmt, struct flb_tm *tm)
138
21.8k
{
139
21.8k
  return(_flb_strptime(buf, fmt, tm, 1));
140
21.8k
}
141
142
static char *
143
_flb_strptime(const char *buf, const char *fmt, struct flb_tm *tm, int initialize)
144
21.8k
{
145
21.8k
  unsigned char c;
146
21.8k
  const unsigned char *bp, *ep;
147
21.8k
  size_t len = 0;
148
21.8k
  int alt_format, i, offs;
149
21.8k
  int neg = 0;
150
21.8k
  static int century, relyear, fields;
151
152
21.8k
  if (initialize) {
153
21.8k
    century = TM_YEAR_BASE;
154
21.8k
    relyear = -1;
155
21.8k
    fields = 0;
156
21.8k
  }
157
158
21.8k
  bp = (const unsigned char *)buf;
159
83.1k
  while ((c = *fmt) != '\0') {
160
    /* Clear `alternate' modifier prior to new conversion. */
161
79.5k
    alt_format = 0;
162
163
    /* Eat up white-space. */
164
79.5k
    if (isspace(c)) {
165
8.72k
      while (isspace(*bp))
166
4.66k
        bp++;
167
168
8.72k
      fmt++;
169
8.72k
      continue;
170
8.72k
    }
171
172
        /*
173
         * Having increased bp we need to ensure we are not
174
         * moving beyond bounds.
175
         */
176
70.8k
        if (*bp == '\0')
177
5.20k
           return (NULL);
178
179
65.6k
    if ((c = *fmt++) != '%')
180
21.2k
      goto literal;
181
182
183
44.4k
again:    switch (c = *fmt++) {
184
0
    case '%': /* "%%" is converted to "%". */
185
21.2k
literal:
186
21.2k
    if (c != *bp++)
187
2.33k
      return (NULL);
188
189
18.9k
    break;
190
191
    /*
192
     * "Alternative" modifiers. Just set the appropriate flag
193
     * and start over again.
194
     */
195
18.9k
    case 'E': /* "%E?" alternative conversion modifier. */
196
0
      _LEGAL_ALT(0);
197
0
      alt_format |= _ALT_E;
198
0
      goto again;
199
200
0
    case 'O': /* "%O?" alternative conversion modifier. */
201
0
      _LEGAL_ALT(0);
202
0
      alt_format |= _ALT_O;
203
0
      goto again;
204
205
    /*
206
     * "Complex" conversion rules, implemented through recursion.
207
     */
208
0
    case 'c': /* Date and time, using the locale's format. */
209
0
      _LEGAL_ALT(_ALT_E);
210
0
      if (!(bp = (const unsigned char *)_flb_strptime((const char *)bp, _ctloc(D_T_FMT), tm, 0)))
211
0
        return (NULL);
212
0
      break;
213
214
0
    case 'D': /* The date as "%m/%d/%y". */
215
0
      _LEGAL_ALT(0);
216
0
      if (!(bp = (const unsigned char *)_flb_strptime((const char *)bp, "%m/%d/%y", tm, 0)))
217
0
        return (NULL);
218
0
      break;
219
220
0
    case 'F': /* The date as "%Y-%m-%d". */
221
0
      _LEGAL_ALT(0);
222
0
      if (!(bp = (const unsigned char *)_flb_strptime((const char *)bp, "%Y-%m-%d", tm, 0)))
223
0
        return (NULL);
224
0
      continue;
225
226
0
    case 'R': /* The time as "%H:%M". */
227
0
      _LEGAL_ALT(0);
228
0
      if (!(bp = (const unsigned char *)_flb_strptime((const char *)bp, "%H:%M", tm, 0)))
229
0
        return (NULL);
230
0
      break;
231
232
0
    case 'r': /* The time as "%I:%M:%S %p". */
233
0
      _LEGAL_ALT(0);
234
0
      if (!(bp = (const unsigned char *)_flb_strptime((const char *)bp, "%I:%M:%S %p", tm, 0)))
235
0
        return (NULL);
236
0
      break;
237
238
0
    case 'T': /* The time as "%H:%M:%S". */
239
0
      _LEGAL_ALT(0);
240
0
      if (!(bp = (const unsigned char *)_flb_strptime((const char *)bp, "%H:%M:%S", tm, 0)))
241
0
        return (NULL);
242
0
      break;
243
244
0
    case 'X': /* The time, using the locale's format. */
245
0
      _LEGAL_ALT(_ALT_E);
246
0
      if (!(bp = (const unsigned char *)_flb_strptime((const char *)bp, _ctloc(T_FMT), tm, 0)))
247
0
        return (NULL);
248
0
      break;
249
250
0
    case 'x': /* The date, using the locale's format. */
251
0
      _LEGAL_ALT(_ALT_E);
252
0
      if (!(bp = (const unsigned char *)_flb_strptime((const char *)bp, _ctloc(D_FMT), tm, 0)))
253
0
        return (NULL);
254
0
      break;
255
256
    /*
257
     * "Elementary" conversion rules.
258
     */
259
0
    case 'A': /* The day of week, using the locale's form. */
260
1.58k
    case 'a':
261
1.58k
      _LEGAL_ALT(0);
262
12.1k
      for (i = 0; i < 7; i++) {
263
        /* Full name. */
264
10.7k
        len = strlen(_ctloc(day[i]));
265
10.7k
        if (strncasecmp(_ctloc(day[i]), (const char *)bp, len) == 0)
266
12
          break;
267
268
        /* Abbreviated name. */
269
10.7k
        len = strlen(_ctloc(abday[i]));
270
10.7k
        if (strncasecmp(_ctloc(abday[i]), (const char *)bp, len) == 0)
271
196
          break;
272
10.7k
      }
273
274
      /* Nothing matched. */
275
1.58k
      if (i == 7)
276
1.37k
        return (NULL);
277
278
208
      tm->tm.tm_wday = i;
279
208
      bp += len;
280
208
      fields |= FIELD_TM_WDAY;
281
208
      break;
282
283
0
    case 'B': /* The month, using the locale's form. */
284
2.06k
    case 'b':
285
2.06k
    case 'h':
286
2.06k
      _LEGAL_ALT(0);
287
24.8k
      for (i = 0; i < 12; i++) {
288
        /* Full name. */
289
23.1k
        len = strlen(_ctloc(mon[i]));
290
23.1k
        if (strncasecmp(_ctloc(mon[i]), (const char *)bp, len) == 0)
291
31
          break;
292
293
        /* Abbreviated name. */
294
23.0k
        len = strlen(_ctloc(abmon[i]));
295
23.0k
        if (strncasecmp(_ctloc(abmon[i]), (const char *)bp, len) == 0)
296
304
          break;
297
23.0k
      }
298
299
      /* Nothing matched. */
300
2.06k
      if (i == 12)
301
1.72k
        return (NULL);
302
303
335
      tm->tm.tm_mon = i;
304
335
      bp += len;
305
335
      fields |= FIELD_TM_MON;
306
335
      break;
307
308
0
    case 'C': /* The century number. */
309
0
      _LEGAL_ALT(_ALT_E);
310
0
      if (!(_conv_num(&bp, &i, 0, 99)))
311
0
        return (NULL);
312
313
0
      century = i * 100;
314
0
      break;
315
316
0
    case 'e': /* The day of month. */
317
0
      if (isspace(*bp))
318
0
        bp++;
319
      /* FALLTHROUGH */
320
2.13k
    case 'd':
321
2.13k
      _LEGAL_ALT(_ALT_O);
322
2.13k
      if (!(_conv_num(&bp, &tm->tm.tm_mday, 1, 31)))
323
284
        return (NULL);
324
1.85k
      fields |= FIELD_TM_MDAY;
325
1.85k
      break;
326
327
0
    case 'k': /* The hour (24-hour clock representation). */
328
0
      _LEGAL_ALT(0);
329
      /* FALLTHROUGH */
330
4.74k
    case 'H':
331
4.74k
      _LEGAL_ALT(_ALT_O);
332
4.74k
      if (!(_conv_num(&bp, &tm->tm.tm_hour, 0, 23)))
333
210
        return (NULL);
334
4.53k
      break;
335
336
4.53k
    case 'l': /* The hour (12-hour clock representation). */
337
0
      _LEGAL_ALT(0);
338
      /* FALLTHROUGH */
339
0
    case 'I':
340
0
      _LEGAL_ALT(_ALT_O);
341
0
      if (!(_conv_num(&bp, &tm->tm.tm_hour, 1, 12)))
342
0
        return (NULL);
343
0
      break;
344
345
0
    case 'j': /* The day of year. */
346
0
      _LEGAL_ALT(0);
347
0
      if (!(_conv_num(&bp, &tm->tm.tm_yday, 1, 366)))
348
0
        return (NULL);
349
0
      tm->tm.tm_yday--;
350
0
      fields |= FIELD_TM_YDAY;
351
0
      break;
352
353
7.71k
    case 'M': /* The minute. */
354
7.71k
      _LEGAL_ALT(_ALT_O);
355
7.71k
      if (!(_conv_num(&bp, &tm->tm.tm_min, 0, 59)))
356
126
        return (NULL);
357
7.59k
      break;
358
359
7.59k
    case 'm': /* The month. */
360
4.51k
      _LEGAL_ALT(_ALT_O);
361
4.51k
      if (!(_conv_num(&bp, &tm->tm.tm_mon, 1, 12)))
362
2.23k
        return (NULL);
363
2.27k
      tm->tm.tm_mon--;
364
2.27k
      fields |= FIELD_TM_MON;
365
2.27k
      break;
366
367
0
    case 'p': /* The locale's equivalent of AM/PM. */
368
0
      _LEGAL_ALT(0);
369
      /* AM? */
370
0
      len = strlen(_ctloc(AM_STR));
371
0
      if (strncasecmp(_ctloc(AM_STR), (const char *)bp, len) == 0) {
372
0
        if (tm->tm.tm_hour > 12) /* i.e., 13:00 AM ?! */
373
0
          return (NULL);
374
0
        else if (tm->tm.tm_hour == 12)
375
0
          tm->tm.tm_hour = 0;
376
377
0
        bp += len;
378
0
        break;
379
0
      }
380
      /* PM? */
381
0
      len = strlen(_ctloc(PM_STR));
382
0
      if (strncasecmp(_ctloc(PM_STR), (const char *)bp, len) == 0) {
383
0
        if (tm->tm.tm_hour > 12) /* i.e., 13:00 PM ?! */
384
0
          return (NULL);
385
0
        else if (tm->tm.tm_hour < 12)
386
0
          tm->tm.tm_hour += 12;
387
388
0
        bp += len;
389
0
        break;
390
0
      }
391
392
      /* Nothing matched. */
393
0
      return (NULL);
394
395
7.38k
    case 'S': /* The seconds. */
396
7.38k
      _LEGAL_ALT(_ALT_O);
397
7.38k
      if (!(_conv_num(&bp, &tm->tm.tm_sec, 0, 60)))
398
197
        return (NULL);
399
7.19k
      break;
400
7.19k
    case 's': /* Seconds since epoch */
401
0
      {
402
0
        int64_t i64;
403
0
        if (!(_conv_num64(&bp, &i64, 0, INT64_MAX)))
404
0
          return (NULL);
405
0
        if (!gmtime_r(&i64, &tm->tm))
406
0
          return (NULL);
407
0
        fields = 0xffff;   /* everything */
408
0
      }
409
0
      break;
410
0
    case 'U': /* The week of year, beginning on sunday. */
411
0
    case 'W': /* The week of year, beginning on monday. */
412
0
      _LEGAL_ALT(_ALT_O);
413
      /*
414
       * XXX This is bogus, as we can not assume any valid
415
       * information present in the tm structure at this
416
       * point to calculate a real value, so just check the
417
       * range for now.
418
       */
419
0
       if (!(_conv_num(&bp, &i, 0, 53)))
420
0
        return (NULL);
421
0
       break;
422
423
0
    case 'w': /* The day of week, beginning on sunday. */
424
0
      _LEGAL_ALT(_ALT_O);
425
0
      if (!(_conv_num(&bp, &tm->tm.tm_wday, 0, 6)))
426
0
        return (NULL);
427
0
      fields |= FIELD_TM_WDAY;
428
0
      break;
429
430
0
    case 'u': /* The day of week, monday = 1. */
431
0
      _LEGAL_ALT(_ALT_O);
432
0
      if (!(_conv_num(&bp, &i, 1, 7)))
433
0
        return (NULL);
434
0
      tm->tm.tm_wday = i % 7;
435
0
      fields |= FIELD_TM_WDAY;
436
0
      continue;
437
438
0
    case 'g': /* The year corresponding to the ISO week
439
         * number but without the century.
440
         */
441
0
      if (!(_conv_num(&bp, &i, 0, 99)))
442
0
        return (NULL);
443
0
      continue;
444
445
0
    case 'G': /* The year corresponding to the ISO week
446
         * number with century.
447
         */
448
0
      do
449
0
        bp++;
450
0
      while (isdigit(*bp));
451
0
      continue;
452
453
0
    case 'V': /* The ISO 8601:1988 week number as decimal */
454
0
      if (!(_conv_num(&bp, &i, 0, 53)))
455
0
        return (NULL);
456
0
      continue;
457
458
14.0k
    case 'Y': /* The year. */
459
14.0k
      _LEGAL_ALT(_ALT_E);
460
14.0k
      if (!(_conv_num(&bp, &i, 0, 9999)))
461
4.47k
        return (NULL);
462
463
9.58k
      relyear = -1;
464
9.58k
      tm->tm.tm_year = i - TM_YEAR_BASE;
465
9.58k
      fields |= FIELD_TM_YEAR;
466
9.58k
      break;
467
468
0
    case 'y': /* The year within the century (2 digits). */
469
0
      _LEGAL_ALT(_ALT_E | _ALT_O);
470
0
      if (!(_conv_num(&bp, &relyear, 0, 99)))
471
0
        return (NULL);
472
0
      break;
473
474
0
    case 'Z':
475
0
      tzset();
476
0
      if (strncmp((const char *)bp, gmt, 3) == 0) {
477
0
        tm->tm.tm_isdst = 0;
478
0
        flb_tm_gmtoff(tm) = 0;
479
#ifdef FLB_HAVE_ZONE
480
        tm->tm.tm_zone = gmt;
481
#endif
482
0
        bp += 3;
483
0
      } else if (strncmp((const char *)bp, utc, 3) == 0) {
484
0
        tm->tm.tm_isdst = 0;
485
0
        flb_tm_gmtoff(tm) = 0;
486
#ifdef FLB_HAVE_ZONE
487
        tm->tm.tm_zone = utc;
488
#endif
489
0
        bp += 3;
490
0
      } else {
491
0
        ep = _find_string(bp, &i,
492
0
             (const char * const *)tzname,
493
0
              NULL, 2);
494
0
        if (ep == NULL)
495
0
          return (NULL);
496
497
0
        tm->tm.tm_isdst = i;
498
0
        flb_tm_gmtoff(tm) = -(timezone);
499
#ifdef FLB_HAVE_ZONE
500
        tm->tm.tm_zone = tzname[i];
501
#endif
502
0
        bp = ep;
503
0
      }
504
0
      continue;
505
506
235
    case 'z':
507
      /*
508
       * We recognize all ISO 8601 formats:
509
       * Z  = Zulu time/UTC
510
       * [+-]hhmm
511
       * [+-]hh:mm
512
       * [+-]hh
513
       * We recognize all RFC-822/RFC-2822 formats:
514
       * UT|GMT
515
       *          North American : UTC offsets
516
       * E[DS]T = Eastern : -4 | -5
517
       * C[DS]T = Central : -5 | -6
518
       * M[DS]T = Mountain: -6 | -7
519
       * P[DS]T = Pacific : -7 | -8
520
       */
521
235
      while (isspace(*bp))
522
0
        bp++;
523
524
235
      switch (*bp++) {
525
16
      case 'G':
526
16
        if (*bp++ != 'M')
527
11
          return NULL;
528
        /*FALLTHROUGH*/
529
25
      case 'U':
530
25
        if (*bp++ != 'T')
531
19
          return NULL;
532
        /*FALLTHROUGH*/
533
33
      case 'Z':
534
33
        tm->tm.tm_isdst = 0;
535
33
        flb_tm_gmtoff(tm) = 0;
536
#ifdef FLB_HAVE_ZONE
537
        tm->tm.tm_zone = utc;
538
#endif
539
33
        continue;
540
8
      case '+':
541
8
        neg = 0;
542
8
        break;
543
67
      case '-':
544
67
        neg = 1;
545
67
        break;
546
97
      default:
547
97
        --bp;
548
97
        ep = _find_string(bp, &i, nast, NULL, 4);
549
97
        if (ep != NULL) {
550
5
        flb_tm_gmtoff(tm) = (-5 - i) * SECSPERHOUR;
551
#ifdef FLB_HAVE_ZONE
552
          tm->tm.tm_zone = (char *)nast[i];
553
#endif
554
5
          bp = ep;
555
5
          continue;
556
5
        }
557
92
        ep = _find_string(bp, &i, nadt, NULL, 4);
558
92
        if (ep != NULL) {
559
11
          tm->tm.tm_isdst = 1;
560
11
          flb_tm_gmtoff(tm) = (-4 - i) * SECSPERHOUR;
561
#ifdef FLB_HAVE_ZONE
562
          tm->tm.tm_zone = (char *)nadt[i];
563
#endif
564
11
          bp = ep;
565
11
          continue;
566
11
        }
567
81
        return NULL;
568
235
      }
569
75
      if (!isdigit(bp[0]) || !isdigit(bp[1]))
570
23
        return NULL;
571
52
      offs = ((bp[0]-'0') * 10 + (bp[1]-'0')) * SECSPERHOUR;
572
52
      bp += 2;
573
52
      if (*bp == ':')
574
12
        bp++;
575
52
      if (isdigit(*bp)) {
576
21
        offs += (*bp++ - '0') * 10 * SECSPERMIN;
577
21
        if (!isdigit(*bp))
578
11
          return NULL;
579
10
        offs += (*bp++ - '0') * SECSPERMIN;
580
10
      }
581
41
      if (neg)
582
38
        offs = -offs;
583
41
      tm->tm.tm_isdst = 0;  /* XXX */
584
41
      flb_tm_gmtoff(tm) = offs;
585
#ifdef FLB_HAVE_ZONE
586
      tm->tm.tm_zone = NULL;  /* XXX */
587
#endif
588
41
      continue;
589
590
    /*
591
     * Miscellaneous conversions.
592
     */
593
0
    case 'n': /* Any kind of white-space. */
594
0
    case 't':
595
0
      _LEGAL_ALT(0);
596
0
      while (isspace(*bp))
597
0
        bp++;
598
0
      break;
599
600
601
0
    default:  /* Unknown/unsupported conversion. */
602
0
      return (NULL);
603
44.4k
    }
604
605
606
44.4k
  }
607
608
  /*
609
   * We need to evaluate the two digit year spec (%y)
610
   * last as we can get a century spec (%C) at any time.
611
   */
612
3.56k
  if (relyear != -1) {
613
0
    if (century == TM_YEAR_BASE) {
614
0
      if (relyear <= 68)
615
0
        tm->tm.tm_year = relyear + 2000 - TM_YEAR_BASE;
616
0
      else
617
0
        tm->tm.tm_year = relyear + 1900 - TM_YEAR_BASE;
618
0
    } else {
619
0
      tm->tm.tm_year = relyear + century - TM_YEAR_BASE;
620
0
    }
621
0
    fields |= FIELD_TM_YEAR;
622
0
  }
623
624
  /* Compute some missing values when possible. */
625
3.56k
  if (fields & FIELD_TM_YEAR) {
626
3.46k
    const int year = (unsigned int)tm->tm.tm_year + (unsigned int)TM_YEAR_BASE;
627
3.46k
    const int *mon_lens = mon_lengths[isleap(year)];
628
3.46k
    if (!(fields & FIELD_TM_YDAY) &&
629
3.46k
        (fields & FIELD_TM_MON) && (fields & FIELD_TM_MDAY)) {
630
467
      tm->tm.tm_yday = tm->tm.tm_mday - 1;
631
1.81k
      for (i = 0; i < tm->tm.tm_mon; i++)
632
1.35k
        tm->tm.tm_yday += mon_lens[i];
633
467
      fields |= FIELD_TM_YDAY;
634
467
    }
635
3.46k
    if (fields & FIELD_TM_YDAY) {
636
467
      int days = tm->tm.tm_yday;
637
467
      if (!(fields & FIELD_TM_WDAY)) {
638
467
        tm->tm.tm_wday = EPOCH_WDAY +
639
467
            ((year - EPOCH_YEAR) % DAYSPERWEEK) *
640
467
            (DAYSPERNYEAR % DAYSPERWEEK) +
641
467
            leaps_thru_end_of(year - 1) -
642
467
            leaps_thru_end_of(EPOCH_YEAR - 1) +
643
467
            tm->tm.tm_yday;
644
467
        tm->tm.tm_wday %= DAYSPERWEEK;
645
467
        if (tm->tm.tm_wday < 0)
646
356
          tm->tm.tm_wday += DAYSPERWEEK;
647
467
      }
648
467
      if (!(fields & FIELD_TM_MON)) {
649
0
        tm->tm.tm_mon = 0;
650
0
        while (tm->tm.tm_mon < MONSPERYEAR && days >= mon_lens[tm->tm.tm_mon])
651
0
          days -= mon_lens[tm->tm.tm_mon++];
652
0
      }
653
467
      if (!(fields & FIELD_TM_MDAY))
654
0
        tm->tm.tm_mday = days + 1;
655
467
    }
656
3.46k
  }
657
658
3.56k
  return ((char *)bp);
659
21.8k
}
660
661
662
static int
663
_conv_num(const unsigned char **buf, int *dest, int llim, int ulim)
664
40.5k
{
665
40.5k
  int result = 0;
666
40.5k
  int rulim = ulim;
667
668
40.5k
  if (**buf < '0' || **buf > '9')
669
6.96k
    return (0);
670
671
  /* we use rulim to break out of the loop when we run out of digits */
672
47.9k
  do {
673
47.9k
    result *= 10;
674
47.9k
    result += *(*buf)++ - '0';
675
47.9k
    rulim /= 10;
676
47.9k
  } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
677
678
33.5k
  if (result < llim || result > ulim)
679
558
    return (0);
680
681
33.0k
  *dest = result;
682
33.0k
  return (1);
683
33.5k
}
684
685
static int
686
_conv_num64(const unsigned char **buf, int64_t *dest, int64_t llim, int64_t ulim)
687
0
{
688
0
  int64_t result = 0;
689
0
  int64_t rulim = ulim;
690
691
0
  if (**buf < '0' || **buf > '9')
692
0
    return (0);
693
694
  /* we use rulim to break out of the loop when we run out of digits */
695
0
  do {
696
    /* Avoid overflow: result > ((2**64)/2.0) / 10.0 */
697
0
    if (result > 922337203685477580) {
698
0
      return (0);
699
0
    }
700
0
    result *= 10;
701
702
    /* Avoid overflow: result > ((2**64)/2.0) - 48 */
703
0
    if (result > 9223372036854775760) {
704
0
      return (0);
705
0
    }
706
0
    result += *(*buf)++ - '0';
707
0
    rulim /= 10;
708
        /* watch out for overflows. If value gets above
709
         * ((2**64)/2.0)/10.0 then we will overflow. So instead
710
         * we return 0 */
711
0
        if (result >= 922337203685477580) {
712
0
            return (0);
713
0
        }
714
0
  } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
715
716
0
  if (result < llim || result > ulim)
717
0
    return (0);
718
719
0
  *dest = result;
720
0
  return (1);
721
0
}
722
723
static const u_char *
724
_find_string(const u_char *bp, int *tgt, const char * const *n1,
725
    const char * const *n2, int c)
726
189
{
727
189
  int i;
728
189
  unsigned int len;
729
730
  /* check full name - then abbreviated ones */
731
362
  for (; n1 != NULL; n1 = n2, n2 = NULL) {
732
903
    for (i = 0; i < c; i++, n1++) {
733
730
      len = strlen(*n1);
734
730
      if (strncasecmp(*n1, (const char *)bp, len) == 0) {
735
16
        *tgt = i;
736
16
        return bp + len;
737
16
      }
738
730
    }
739
189
  }
740
741
  /* Nothing matched */
742
173
  return NULL;
743
189
}
744
745
static int
746
leaps_thru_end_of(const int y)
747
1.12k
{
748
1.12k
  return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
749
1.12k
    -(leaps_thru_end_of(-(y + 1)) + 1);
750
1.12k
}