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