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