1/*-
2 * Copyright (c) 2014 Gary Mills
3 * Copyright 2011, Nexenta Systems, Inc.  All rights reserved.
4 * Copyright (c) 1994 Powerdog Industries.  All rights reserved.
5 *
6 * Copyright (c) 2011 The FreeBSD Foundation
7 * All rights reserved.
8 * Portions of this software were developed by David Chisnall
9 * under sponsorship from the FreeBSD Foundation.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer
18 *    in the documentation and/or other materials provided with the
19 *    distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
30 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
31 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 * The views and conclusions contained in the software and documentation
34 * are those of the authors and should not be interpreted as representing
35 * official policies, either expressed or implied, of Powerdog Industries.
36 */
37
38#include <sys/cdefs.h>
39#ifndef lint
40#ifndef NOID
41static char copyright[] __unused =
42"@(#) Copyright (c) 1994 Powerdog Industries.  All rights reserved.";
43static char sccsid[] __unused = "@(#)strptime.c	0.1 (Powerdog) 94/03/27";
44#endif /* !defined NOID */
45#endif /* not lint */
46__FBSDID("$FreeBSD: releng/10.2/lib/libc/stdtime/strptime.c 273290 2014-10-19 21:16:24Z ache $");
47
48#include "namespace.h"
49#include <time.h>
50#include <ctype.h>
51#include <errno.h>
52#include <stdlib.h>
53#include <string.h>
54#include <pthread.h>
55#include "un-namespace.h"
56#include "libc_private.h"
57#include "timelocal.h"
58#include "tzfile.h"
59
60static char * _strptime(const char *, const char *, struct tm *, int *, locale_t);
61
62#define	asizeof(a)	(sizeof(a) / sizeof((a)[0]))
63
64#define	FLAG_NONE	(1 << 0)
65#define	FLAG_YEAR	(1 << 1)
66#define	FLAG_MONTH	(1 << 2)
67#define	FLAG_YDAY	(1 << 3)
68#define	FLAG_MDAY	(1 << 4)
69#define	FLAG_WDAY	(1 << 5)
70
71/*
72 * Calculate the week day of the first day of a year. Valid for
73 * the Gregorian calendar, which began Sept 14, 1752 in the UK
74 * and its colonies. Ref:
75 * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week
76 */
77
78static int
79first_wday_of(int year)
80{
81	return (((2 * (3 - (year / 100) % 4)) + (year % 100) +
82		((year % 100) / 4) + (isleap(year) ? 6 : 0) + 1) % 7);
83}
84
85static char *
86_strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp,
87		locale_t locale)
88{
89	char	c;
90	const char *ptr;
91	int	day_offset = -1, wday_offset;
92	int week_offset;
93	int	i, len;
94	int flags;
95	int Ealternative, Oalternative;
96	const struct lc_time_T *tptr = __get_current_time_locale(locale);
97	static int start_of_month[2][13] = {
98		{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
99		{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
100	};
101
102	flags = FLAG_NONE;
103
104	ptr = fmt;
105	while (*ptr != 0) {
106		c = *ptr++;
107
108		if (c != '%') {
109			if (isspace_l((unsigned char)c, locale))
110				while (*buf != 0 &&
111				       isspace_l((unsigned char)*buf, locale))
112					buf++;
113			else if (c != *buf++)
114				return (NULL);
115			continue;
116		}
117
118		Ealternative = 0;
119		Oalternative = 0;
120label:
121		c = *ptr++;
122		switch (c) {
123		case '%':
124			if (*buf++ != '%')
125				return (NULL);
126			break;
127
128		case '+':
129			buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale);
130			if (buf == NULL)
131				return (NULL);
132			flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
133			break;
134
135		case 'C':
136			if (!isdigit_l((unsigned char)*buf, locale))
137				return (NULL);
138
139			/* XXX This will break for 3-digit centuries. */
140			len = 2;
141			for (i = 0; len && *buf != 0 &&
142			     isdigit_l((unsigned char)*buf, locale); buf++) {
143				i *= 10;
144				i += *buf - '0';
145				len--;
146			}
147			if (i < 19)
148				return (NULL);
149
150			tm->tm_year = i * 100 - TM_YEAR_BASE;
151			flags |= FLAG_YEAR;
152
153			break;
154
155		case 'c':
156			buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale);
157			if (buf == NULL)
158				return (NULL);
159			flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
160			break;
161
162		case 'D':
163			buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale);
164			if (buf == NULL)
165				return (NULL);
166			flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
167			break;
168
169		case 'E':
170			if (Ealternative || Oalternative)
171				break;
172			Ealternative++;
173			goto label;
174
175		case 'O':
176			if (Ealternative || Oalternative)
177				break;
178			Oalternative++;
179			goto label;
180
181		case 'F':
182			buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale);
183			if (buf == NULL)
184				return (NULL);
185			flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
186			break;
187
188		case 'R':
189			buf = _strptime(buf, "%H:%M", tm, GMTp, locale);
190			if (buf == NULL)
191				return (NULL);
192			break;
193
194		case 'r':
195			buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale);
196			if (buf == NULL)
197				return (NULL);
198			break;
199
200		case 'T':
201			buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale);
202			if (buf == NULL)
203				return (NULL);
204			break;
205
206		case 'X':
207			buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale);
208			if (buf == NULL)
209				return (NULL);
210			break;
211
212		case 'x':
213			buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale);
214			if (buf == NULL)
215				return (NULL);
216			flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
217			break;
218
219		case 'j':
220			if (!isdigit_l((unsigned char)*buf, locale))
221				return (NULL);
222
223			len = 3;
224			for (i = 0; len && *buf != 0 &&
225			     isdigit_l((unsigned char)*buf, locale); buf++){
226				i *= 10;
227				i += *buf - '0';
228				len--;
229			}
230			if (i < 1 || i > 366)
231				return (NULL);
232
233			tm->tm_yday = i - 1;
234			flags |= FLAG_YDAY;
235
236			break;
237
238		case 'M':
239		case 'S':
240			if (*buf == 0 ||
241				isspace_l((unsigned char)*buf, locale))
242				break;
243
244			if (!isdigit_l((unsigned char)*buf, locale))
245				return (NULL);
246
247			len = 2;
248			for (i = 0; len && *buf != 0 &&
249				isdigit_l((unsigned char)*buf, locale); buf++){
250				i *= 10;
251				i += *buf - '0';
252				len--;
253			}
254
255			if (c == 'M') {
256				if (i > 59)
257					return (NULL);
258				tm->tm_min = i;
259			} else {
260				if (i > 60)
261					return (NULL);
262				tm->tm_sec = i;
263			}
264
265			break;
266
267		case 'H':
268		case 'I':
269		case 'k':
270		case 'l':
271			/*
272			 * Of these, %l is the only specifier explicitly
273			 * documented as not being zero-padded.  However,
274			 * there is no harm in allowing zero-padding.
275			 *
276			 * XXX The %l specifier may gobble one too many
277			 * digits if used incorrectly.
278			 */
279			if (!isdigit_l((unsigned char)*buf, locale))
280				return (NULL);
281
282			len = 2;
283			for (i = 0; len && *buf != 0 &&
284			     isdigit_l((unsigned char)*buf, locale); buf++) {
285				i *= 10;
286				i += *buf - '0';
287				len--;
288			}
289			if (c == 'H' || c == 'k') {
290				if (i > 23)
291					return (NULL);
292			} else if (i > 12)
293				return (NULL);
294
295			tm->tm_hour = i;
296
297			break;
298
299		case 'p':
300			/*
301			 * XXX This is bogus if parsed before hour-related
302			 * specifiers.
303			 */
304			len = strlen(tptr->am);
305			if (strncasecmp_l(buf, tptr->am, len, locale) == 0) {
306				if (tm->tm_hour > 12)
307					return (NULL);
308				if (tm->tm_hour == 12)
309					tm->tm_hour = 0;
310				buf += len;
311				break;
312			}
313
314			len = strlen(tptr->pm);
315			if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) {
316				if (tm->tm_hour > 12)
317					return (NULL);
318				if (tm->tm_hour != 12)
319					tm->tm_hour += 12;
320				buf += len;
321				break;
322			}
323
324			return (NULL);
325
326		case 'A':
327		case 'a':
328			for (i = 0; i < asizeof(tptr->weekday); i++) {
329				len = strlen(tptr->weekday[i]);
330				if (strncasecmp_l(buf, tptr->weekday[i],
331						len, locale) == 0)
332					break;
333				len = strlen(tptr->wday[i]);
334				if (strncasecmp_l(buf, tptr->wday[i],
335						len, locale) == 0)
336					break;
337			}
338			if (i == asizeof(tptr->weekday))
339				return (NULL);
340
341			buf += len;
342			tm->tm_wday = i;
343			flags |= FLAG_WDAY;
344			break;
345
346		case 'U':
347		case 'W':
348			/*
349			 * XXX This is bogus, as we can not assume any valid
350			 * information present in the tm structure at this
351			 * point to calculate a real value, so just check the
352			 * range for now.
353			 */
354			if (!isdigit_l((unsigned char)*buf, locale))
355				return (NULL);
356
357			len = 2;
358			for (i = 0; len && *buf != 0 &&
359			     isdigit_l((unsigned char)*buf, locale); buf++) {
360				i *= 10;
361				i += *buf - '0';
362				len--;
363			}
364			if (i > 53)
365				return (NULL);
366
367			if (c == 'U')
368				day_offset = TM_SUNDAY;
369			else
370				day_offset = TM_MONDAY;
371
372
373			week_offset = i;
374
375			break;
376
377		case 'w':
378			if (!isdigit_l((unsigned char)*buf, locale))
379				return (NULL);
380
381			i = *buf - '0';
382			if (i > 6)
383				return (NULL);
384
385			tm->tm_wday = i;
386			flags |= FLAG_WDAY;
387
388			break;
389
390		case 'e':
391			/*
392			 * With %e format, our strftime(3) adds a blank space
393			 * before single digits.
394			 */
395			if (*buf != 0 &&
396			    isspace_l((unsigned char)*buf, locale))
397			       buf++;
398			/* FALLTHROUGH */
399		case 'd':
400			/*
401			 * The %e specifier was once explicitly documented as
402			 * not being zero-padded but was later changed to
403			 * equivalent to %d.  There is no harm in allowing
404			 * such padding.
405			 *
406			 * XXX The %e specifier may gobble one too many
407			 * digits if used incorrectly.
408			 */
409			if (!isdigit_l((unsigned char)*buf, locale))
410				return (NULL);
411
412			len = 2;
413			for (i = 0; len && *buf != 0 &&
414			     isdigit_l((unsigned char)*buf, locale); buf++) {
415				i *= 10;
416				i += *buf - '0';
417				len--;
418			}
419			if (i > 31)
420				return (NULL);
421
422			tm->tm_mday = i;
423			flags |= FLAG_MDAY;
424
425			break;
426
427		case 'B':
428		case 'b':
429		case 'h':
430			for (i = 0; i < asizeof(tptr->month); i++) {
431				if (Oalternative) {
432					if (c == 'B') {
433						len = strlen(tptr->alt_month[i]);
434						if (strncasecmp_l(buf,
435								tptr->alt_month[i],
436								len, locale) == 0)
437							break;
438					}
439				} else {
440					len = strlen(tptr->month[i]);
441					if (strncasecmp_l(buf, tptr->month[i],
442							len, locale) == 0)
443						break;
444				}
445			}
446			/*
447			 * Try the abbreviated month name if the full name
448			 * wasn't found and Oalternative was not requested.
449			 */
450			if (i == asizeof(tptr->month) && !Oalternative) {
451				for (i = 0; i < asizeof(tptr->month); i++) {
452					len = strlen(tptr->mon[i]);
453					if (strncasecmp_l(buf, tptr->mon[i],
454							len, locale) == 0)
455						break;
456				}
457			}
458			if (i == asizeof(tptr->month))
459				return (NULL);
460
461			tm->tm_mon = i;
462			buf += len;
463			flags |= FLAG_MONTH;
464
465			break;
466
467		case 'm':
468			if (!isdigit_l((unsigned char)*buf, locale))
469				return (NULL);
470
471			len = 2;
472			for (i = 0; len && *buf != 0 &&
473			     isdigit_l((unsigned char)*buf, locale); buf++) {
474				i *= 10;
475				i += *buf - '0';
476				len--;
477			}
478			if (i < 1 || i > 12)
479				return (NULL);
480
481			tm->tm_mon = i - 1;
482			flags |= FLAG_MONTH;
483
484			break;
485
486		case 's':
487			{
488			char *cp;
489			int sverrno;
490			long n;
491			time_t t;
492
493			sverrno = errno;
494			errno = 0;
495			n = strtol_l(buf, &cp, 10, locale);
496			if (errno == ERANGE || (long)(t = n) != n) {
497				errno = sverrno;
498				return (NULL);
499			}
500			errno = sverrno;
501			buf = cp;
502			if (gmtime_r(&t, tm) == NULL)
503				return (NULL);
504			*GMTp = 1;
505			flags |= FLAG_YDAY | FLAG_WDAY | FLAG_MONTH |
506			    FLAG_MDAY | FLAG_YEAR;
507			}
508			break;
509
510		case 'Y':
511		case 'y':
512			if (*buf == 0 ||
513			    isspace_l((unsigned char)*buf, locale))
514				break;
515
516			if (!isdigit_l((unsigned char)*buf, locale))
517				return (NULL);
518
519			len = (c == 'Y') ? 4 : 2;
520			for (i = 0; len && *buf != 0 &&
521			     isdigit_l((unsigned char)*buf, locale); buf++) {
522				i *= 10;
523				i += *buf - '0';
524				len--;
525			}
526			if (c == 'Y')
527				i -= TM_YEAR_BASE;
528			if (c == 'y' && i < 69)
529				i += 100;
530			if (i < 0)
531				return (NULL);
532
533			tm->tm_year = i;
534			flags |= FLAG_YEAR;
535
536			break;
537
538		case 'Z':
539			{
540			const char *cp;
541			char *zonestr;
542
543			for (cp = buf; *cp &&
544			     isupper_l((unsigned char)*cp, locale); ++cp) {
545				/*empty*/}
546			if (cp - buf) {
547				zonestr = alloca(cp - buf + 1);
548				strncpy(zonestr, buf, cp - buf);
549				zonestr[cp - buf] = '\0';
550				tzset();
551				if (0 == strcmp(zonestr, "GMT") ||
552				    0 == strcmp(zonestr, "UTC")) {
553				    *GMTp = 1;
554				} else if (0 == strcmp(zonestr, tzname[0])) {
555				    tm->tm_isdst = 0;
556				} else if (0 == strcmp(zonestr, tzname[1])) {
557				    tm->tm_isdst = 1;
558				} else {
559				    return (NULL);
560				}
561				buf += cp - buf;
562			}
563			}
564			break;
565
566		case 'z':
567			{
568			int sign = 1;
569
570			if (*buf != '+') {
571				if (*buf == '-')
572					sign = -1;
573				else
574					return (NULL);
575			}
576
577			buf++;
578			i = 0;
579			for (len = 4; len > 0; len--) {
580				if (isdigit_l((unsigned char)*buf, locale)) {
581					i *= 10;
582					i += *buf - '0';
583					buf++;
584				} else
585					return (NULL);
586			}
587
588			tm->tm_hour -= sign * (i / 100);
589			tm->tm_min  -= sign * (i % 100);
590			*GMTp = 1;
591			}
592			break;
593
594		case 'n':
595		case 't':
596			while (isspace_l((unsigned char)*buf, locale))
597				buf++;
598			break;
599
600		default:
601			return (NULL);
602		}
603	}
604
605	if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) {
606		if ((flags & (FLAG_MONTH | FLAG_MDAY)) ==
607		    (FLAG_MONTH | FLAG_MDAY)) {
608			tm->tm_yday = start_of_month[isleap(tm->tm_year +
609			    TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1);
610			flags |= FLAG_YDAY;
611		} else if (day_offset != -1) {
612			/* Set the date to the first Sunday (or Monday)
613			 * of the specified week of the year.
614			 */
615			if (!(flags & FLAG_WDAY)) {
616				tm->tm_wday = day_offset;
617				flags |= FLAG_WDAY;
618			}
619			tm->tm_yday = (7 -
620			    first_wday_of(tm->tm_year + TM_YEAR_BASE) +
621			    day_offset) % 7 + (week_offset - 1) * 7 +
622			    tm->tm_wday - day_offset;
623			flags |= FLAG_YDAY;
624		}
625	}
626
627	if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) {
628		if (!(flags & FLAG_MONTH)) {
629			i = 0;
630			while (tm->tm_yday >=
631			    start_of_month[isleap(tm->tm_year +
632			    TM_YEAR_BASE)][i])
633				i++;
634			if (i > 12) {
635				i = 1;
636				tm->tm_yday -=
637				    start_of_month[isleap(tm->tm_year +
638				    TM_YEAR_BASE)][12];
639				tm->tm_year++;
640			}
641			tm->tm_mon = i - 1;
642			flags |= FLAG_MONTH;
643		}
644		if (!(flags & FLAG_MDAY)) {
645			tm->tm_mday = tm->tm_yday -
646			    start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)]
647			    [tm->tm_mon] + 1;
648			flags |= FLAG_MDAY;
649		}
650		if (!(flags & FLAG_WDAY)) {
651			i = 0;
652			wday_offset = first_wday_of(tm->tm_year);
653			while (i++ <= tm->tm_yday) {
654				if (wday_offset++ >= 6)
655					wday_offset = 0;
656			}
657			tm->tm_wday = wday_offset;
658			flags |= FLAG_WDAY;
659		}
660	}
661
662	return ((char *)buf);
663}
664
665char *
666strptime_l(const char * __restrict buf, const char * __restrict fmt,
667    struct tm * __restrict tm, locale_t loc)
668{
669	char *ret;
670	int gmt;
671	FIX_LOCALE(loc);
672
673	gmt = 0;
674	ret = _strptime(buf, fmt, tm, &gmt, loc);
675	if (ret && gmt) {
676		time_t t = timegm(tm);
677
678		localtime_r(&t, tm);
679	}
680
681	return (ret);
682}
683
684char *
685strptime(const char * __restrict buf, const char * __restrict fmt,
686    struct tm * __restrict tm)
687{
688	return strptime_l(buf, fmt, tm, __get_locale());
689}
690