14297a3b0SGarrett D'Amore /*
2a11c1571SGary Mills  * Copyright (c) 2014 Gary Mills
32d08521bSGarrett D'Amore  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
425f48f67SGordon Ross  * Copyright 2011, Nexenta Systems, Inc.  All rights reserved.
54297a3b0SGarrett D'Amore  * Copyright (c) 1994 Powerdog Industries.  All rights reserved.
64297a3b0SGarrett D'Amore  *
74297a3b0SGarrett D'Amore  * Redistribution and use in source and binary forms, with or without
84297a3b0SGarrett D'Amore  * modification, are permitted provided that the following conditions
94297a3b0SGarrett D'Amore  * are met:
105aec55ebSGarrett D'Amore  *
114297a3b0SGarrett D'Amore  * 1. Redistributions of source code must retain the above copyright
124297a3b0SGarrett D'Amore  *    notice, this list of conditions and the following disclaimer.
135aec55ebSGarrett D'Amore  *
144297a3b0SGarrett D'Amore  * 2. Redistributions in binary form must reproduce the above copyright
154297a3b0SGarrett D'Amore  *    notice, this list of conditions and the following disclaimer
164297a3b0SGarrett D'Amore  *    in the documentation and/or other materials provided with the
174297a3b0SGarrett D'Amore  *    distribution.
184297a3b0SGarrett D'Amore  *
194297a3b0SGarrett D'Amore  * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
204297a3b0SGarrett D'Amore  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
214297a3b0SGarrett D'Amore  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
224297a3b0SGarrett D'Amore  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
234297a3b0SGarrett D'Amore  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
244297a3b0SGarrett D'Amore  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
254297a3b0SGarrett D'Amore  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
264297a3b0SGarrett D'Amore  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
274297a3b0SGarrett D'Amore  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
284297a3b0SGarrett D'Amore  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
294297a3b0SGarrett D'Amore  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
305aec55ebSGarrett D'Amore  *
315aec55ebSGarrett D'Amore  * The views and conclusions contained in the software and documentation
325aec55ebSGarrett D'Amore  * are those of the authors and should not be interpreted as representing
335aec55ebSGarrett D'Amore  * official policies, either expressed or implied, of Powerdog Industries.
344297a3b0SGarrett D'Amore  */
355aec55ebSGarrett D'Amore 
364297a3b0SGarrett D'Amore #include "lint.h"
374297a3b0SGarrett D'Amore #include <time.h>
384297a3b0SGarrett D'Amore #include <ctype.h>
394297a3b0SGarrett D'Amore #include <errno.h>
404297a3b0SGarrett D'Amore #include <stdlib.h>
414297a3b0SGarrett D'Amore #include <string.h>
424297a3b0SGarrett D'Amore #include <pthread.h>
434297a3b0SGarrett D'Amore #include <alloca.h>
442d08521bSGarrett D'Amore #include <locale.h>
454297a3b0SGarrett D'Amore #include "timelocal.h"
462d08521bSGarrett D'Amore #include "localeimpl.h"
474297a3b0SGarrett D'Amore 
484297a3b0SGarrett D'Amore #define	asizeof(a)	(sizeof (a) / sizeof ((a)[0]))
494297a3b0SGarrett D'Amore 
506eaad1d3SGarrett D'Amore #define	F_GMT		(1 << 0)
516eaad1d3SGarrett D'Amore #define	F_RECURSE	(1 << 2)
526eaad1d3SGarrett D'Amore 
534297a3b0SGarrett D'Amore static char *
__strptime(const char * _RESTRICT_KYWD buf,const char * _RESTRICT_KYWD fmt,struct tm * _RESTRICT_KYWD tm,int * _RESTRICT_KYWD flagsp,locale_t _RESTRICT_KYWD loc)542d08521bSGarrett D'Amore __strptime(const char *_RESTRICT_KYWD buf, const char *_RESTRICT_KYWD fmt,
552d08521bSGarrett D'Amore     struct tm *_RESTRICT_KYWD tm, int *_RESTRICT_KYWD flagsp,
562d08521bSGarrett D'Amore     locale_t _RESTRICT_KYWD loc)
574297a3b0SGarrett D'Amore {
584297a3b0SGarrett D'Amore 	char	c;
594297a3b0SGarrett D'Amore 	const char *ptr;
606eaad1d3SGarrett D'Amore 	int	i, len, recurse = 0;
614297a3b0SGarrett D'Amore 	int Ealternative, Oalternative;
622d08521bSGarrett D'Amore 	const struct lc_time *tptr = loc->time;
634297a3b0SGarrett D'Amore 
646eaad1d3SGarrett D'Amore 	if (*flagsp & F_RECURSE)
656eaad1d3SGarrett D'Amore 		recurse = 1;
666eaad1d3SGarrett D'Amore 	*flagsp |= F_RECURSE;
676eaad1d3SGarrett D'Amore 
684297a3b0SGarrett D'Amore 	ptr = fmt;
694297a3b0SGarrett D'Amore 	while (*ptr != 0) {
704297a3b0SGarrett D'Amore 		if (*buf == 0)
714297a3b0SGarrett D'Amore 			break;
724297a3b0SGarrett D'Amore 
734297a3b0SGarrett D'Amore 		c = *ptr++;
744297a3b0SGarrett D'Amore 
754297a3b0SGarrett D'Amore 		if (c != '%') {
766eaad1d3SGarrett D'Amore 			if (isspace(c))
776eaad1d3SGarrett D'Amore 				while (isspace(*buf))
784297a3b0SGarrett D'Amore 					buf++;
794297a3b0SGarrett D'Amore 			else if (c != *buf++)
806eaad1d3SGarrett D'Amore 				return (NULL);
814297a3b0SGarrett D'Amore 			continue;
824297a3b0SGarrett D'Amore 		}
834297a3b0SGarrett D'Amore 
844297a3b0SGarrett D'Amore 		Ealternative = 0;
854297a3b0SGarrett D'Amore 		Oalternative = 0;
864297a3b0SGarrett D'Amore label:
874297a3b0SGarrett D'Amore 		c = *ptr++;
884297a3b0SGarrett D'Amore 		switch (c) {
894297a3b0SGarrett D'Amore 		case 0:
904297a3b0SGarrett D'Amore 		case '%':
914297a3b0SGarrett D'Amore 			if (*buf++ != '%')
926eaad1d3SGarrett D'Amore 				return (NULL);
934297a3b0SGarrett D'Amore 			break;
944297a3b0SGarrett D'Amore 
954297a3b0SGarrett D'Amore 		case '+':
962d08521bSGarrett D'Amore 			buf = __strptime(buf, tptr->date_fmt, tm, flagsp, loc);
976eaad1d3SGarrett D'Amore 			if (buf == NULL)
986eaad1d3SGarrett D'Amore 				return (NULL);
994297a3b0SGarrett D'Amore 			break;
1004297a3b0SGarrett D'Amore 
1014297a3b0SGarrett D'Amore 		case 'C':
1026eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
1036eaad1d3SGarrett D'Amore 				return (NULL);
1044297a3b0SGarrett D'Amore 
1054297a3b0SGarrett D'Amore 			/* XXX This will break for 3-digit centuries. */
1064297a3b0SGarrett D'Amore 			len = 2;
1076eaad1d3SGarrett D'Amore 			for (i = 0; len && isdigit(*buf); buf++) {
1084297a3b0SGarrett D'Amore 				i *= 10;
1094297a3b0SGarrett D'Amore 				i += *buf - '0';
1104297a3b0SGarrett D'Amore 				len--;
1114297a3b0SGarrett D'Amore 			}
1124297a3b0SGarrett D'Amore 			if (i < 19)
1136eaad1d3SGarrett D'Amore 				return (NULL);
1144297a3b0SGarrett D'Amore 
1154297a3b0SGarrett D'Amore 			tm->tm_year = i * 100 - 1900;
1164297a3b0SGarrett D'Amore 			break;
1174297a3b0SGarrett D'Amore 
1184297a3b0SGarrett D'Amore 		case 'c':
1192d08521bSGarrett D'Amore 			buf = __strptime(buf, tptr->c_fmt, tm, flagsp, loc);
1206eaad1d3SGarrett D'Amore 			if (buf == NULL)
1216eaad1d3SGarrett D'Amore 				return (NULL);
1224297a3b0SGarrett D'Amore 			break;
1234297a3b0SGarrett D'Amore 
1244297a3b0SGarrett D'Amore 		case 'D':
1252d08521bSGarrett D'Amore 			buf = __strptime(buf, "%m/%d/%y", tm, flagsp, loc);
1266eaad1d3SGarrett D'Amore 			if (buf == NULL)
1276eaad1d3SGarrett D'Amore 				return (NULL);
1284297a3b0SGarrett D'Amore 			break;
1294297a3b0SGarrett D'Amore 
1304297a3b0SGarrett D'Amore 		case 'E':
1314297a3b0SGarrett D'Amore 			if (Ealternative || Oalternative)
1324297a3b0SGarrett D'Amore 				break;
1334297a3b0SGarrett D'Amore 			Ealternative++;
1344297a3b0SGarrett D'Amore 			goto label;
1354297a3b0SGarrett D'Amore 
1364297a3b0SGarrett D'Amore 		case 'O':
1374297a3b0SGarrett D'Amore 			if (Ealternative || Oalternative)
1384297a3b0SGarrett D'Amore 				break;
1394297a3b0SGarrett D'Amore 			Oalternative++;
1404297a3b0SGarrett D'Amore 			goto label;
1414297a3b0SGarrett D'Amore 
1424297a3b0SGarrett D'Amore 		case 'F':
1432d08521bSGarrett D'Amore 			buf = __strptime(buf, "%Y-%m-%d", tm, flagsp, loc);
1446eaad1d3SGarrett D'Amore 			if (buf == NULL)
1456eaad1d3SGarrett D'Amore 				return (NULL);
1464297a3b0SGarrett D'Amore 			break;
1474297a3b0SGarrett D'Amore 
1484297a3b0SGarrett D'Amore 		case 'R':
1492d08521bSGarrett D'Amore 			buf = __strptime(buf, "%H:%M", tm, flagsp, loc);
1506eaad1d3SGarrett D'Amore 			if (buf == NULL)
1516eaad1d3SGarrett D'Amore 				return (NULL);
1524297a3b0SGarrett D'Amore 			break;
1534297a3b0SGarrett D'Amore 
1544297a3b0SGarrett D'Amore 		case 'r':
1552d08521bSGarrett D'Amore 			buf = __strptime(buf, tptr->ampm_fmt, tm, flagsp, loc);
1566eaad1d3SGarrett D'Amore 			if (buf == NULL)
1576eaad1d3SGarrett D'Amore 				return (NULL);
1584297a3b0SGarrett D'Amore 			break;
1594297a3b0SGarrett D'Amore 
1604297a3b0SGarrett D'Amore 		case 'T':
1612d08521bSGarrett D'Amore 			buf = __strptime(buf, "%H:%M:%S", tm, flagsp, loc);
1626eaad1d3SGarrett D'Amore 			if (buf == NULL)
1636eaad1d3SGarrett D'Amore 				return (NULL);
1644297a3b0SGarrett D'Amore 			break;
1654297a3b0SGarrett D'Amore 
1664297a3b0SGarrett D'Amore 		case 'X':
1672d08521bSGarrett D'Amore 			buf = __strptime(buf, tptr->X_fmt, tm, flagsp, loc);
1686eaad1d3SGarrett D'Amore 			if (buf == NULL)
1696eaad1d3SGarrett D'Amore 				return (NULL);
1704297a3b0SGarrett D'Amore 			break;
1714297a3b0SGarrett D'Amore 
1724297a3b0SGarrett D'Amore 		case 'x':
1732d08521bSGarrett D'Amore 			buf = __strptime(buf, tptr->x_fmt, tm, flagsp, loc);
1746eaad1d3SGarrett D'Amore 			if (buf == NULL)
1756eaad1d3SGarrett D'Amore 				return (NULL);
1764297a3b0SGarrett D'Amore 			break;
1774297a3b0SGarrett D'Amore 
1784297a3b0SGarrett D'Amore 		case 'j':
1796eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
1806eaad1d3SGarrett D'Amore 				return (NULL);
1814297a3b0SGarrett D'Amore 
1824297a3b0SGarrett D'Amore 			len = 3;
1836eaad1d3SGarrett D'Amore 			for (i = 0; len && isdigit(*buf); buf++) {
1844297a3b0SGarrett D'Amore 				i *= 10;
1854297a3b0SGarrett D'Amore 				i += *buf - '0';
1864297a3b0SGarrett D'Amore 				len--;
1874297a3b0SGarrett D'Amore 			}
1884297a3b0SGarrett D'Amore 			if (i < 1 || i > 366)
1896eaad1d3SGarrett D'Amore 				return (NULL);
1904297a3b0SGarrett D'Amore 
1914297a3b0SGarrett D'Amore 			tm->tm_yday = i - 1;
1924297a3b0SGarrett D'Amore 			break;
1934297a3b0SGarrett D'Amore 
1944297a3b0SGarrett D'Amore 		case 'M':
1954297a3b0SGarrett D'Amore 		case 'S':
1966eaad1d3SGarrett D'Amore 			if (*buf == 0 || isspace(*buf))
1974297a3b0SGarrett D'Amore 				break;
1984297a3b0SGarrett D'Amore 
1996eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
2006eaad1d3SGarrett D'Amore 				return (NULL);
2014297a3b0SGarrett D'Amore 
2024297a3b0SGarrett D'Amore 			len = 2;
2036eaad1d3SGarrett D'Amore 			for (i = 0; len && isdigit(*buf); buf++) {
2044297a3b0SGarrett D'Amore 				i *= 10;
2054297a3b0SGarrett D'Amore 				i += *buf - '0';
2064297a3b0SGarrett D'Amore 				len--;
2074297a3b0SGarrett D'Amore 			}
2084297a3b0SGarrett D'Amore 
2094297a3b0SGarrett D'Amore 			if (c == 'M') {
2104297a3b0SGarrett D'Amore 				if (i > 59)
2116eaad1d3SGarrett D'Amore 					return (NULL);
2124297a3b0SGarrett D'Amore 				tm->tm_min = i;
2134297a3b0SGarrett D'Amore 			} else {
2144297a3b0SGarrett D'Amore 				if (i > 60)
2156eaad1d3SGarrett D'Amore 					return (NULL);
2164297a3b0SGarrett D'Amore 				tm->tm_sec = i;
2174297a3b0SGarrett D'Amore 			}
2184297a3b0SGarrett D'Amore 
2194297a3b0SGarrett D'Amore 			break;
2204297a3b0SGarrett D'Amore 
2214297a3b0SGarrett D'Amore 		case 'H':
2224297a3b0SGarrett D'Amore 		case 'I':
2234297a3b0SGarrett D'Amore 		case 'k':
2244297a3b0SGarrett D'Amore 		case 'l':
2254297a3b0SGarrett D'Amore 			/*
2264297a3b0SGarrett D'Amore 			 * Of these, %l is the only specifier explicitly
2274297a3b0SGarrett D'Amore 			 * documented as not being zero-padded.  However,
2284297a3b0SGarrett D'Amore 			 * there is no harm in allowing zero-padding.
2294297a3b0SGarrett D'Amore 			 *
2304297a3b0SGarrett D'Amore 			 * XXX The %l specifier may gobble one too many
2314297a3b0SGarrett D'Amore 			 * digits if used incorrectly.
2324297a3b0SGarrett D'Amore 			 */
2336eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
2346eaad1d3SGarrett D'Amore 				return (NULL);
2354297a3b0SGarrett D'Amore 
2364297a3b0SGarrett D'Amore 			len = 2;
2376eaad1d3SGarrett D'Amore 			for (i = 0; len && isdigit(*buf); buf++) {
2384297a3b0SGarrett D'Amore 				i *= 10;
2394297a3b0SGarrett D'Amore 				i += *buf - '0';
2404297a3b0SGarrett D'Amore 				len--;
2414297a3b0SGarrett D'Amore 			}
2424297a3b0SGarrett D'Amore 			if (c == 'H' || c == 'k') {
2434297a3b0SGarrett D'Amore 				if (i > 23)
2446eaad1d3SGarrett D'Amore 					return (NULL);
2454297a3b0SGarrett D'Amore 			} else if (i > 12)
2466eaad1d3SGarrett D'Amore 				return (NULL);
2474297a3b0SGarrett D'Amore 
2484297a3b0SGarrett D'Amore 			tm->tm_hour = i;
2494297a3b0SGarrett D'Amore 
2504297a3b0SGarrett D'Amore 			break;
2514297a3b0SGarrett D'Amore 
2524297a3b0SGarrett D'Amore 		case 'p':
2534297a3b0SGarrett D'Amore 			/*
2544297a3b0SGarrett D'Amore 			 * XXX This is bogus if parsed before hour-related
2554297a3b0SGarrett D'Amore 			 * specifiers.
2564297a3b0SGarrett D'Amore 			 */
2574297a3b0SGarrett D'Amore 			len = strlen(tptr->am);
2584297a3b0SGarrett D'Amore 			if (strncasecmp(buf, tptr->am, len) == 0) {
2594297a3b0SGarrett D'Amore 				if (tm->tm_hour > 12)
2606eaad1d3SGarrett D'Amore 					return (NULL);
2614297a3b0SGarrett D'Amore 				if (tm->tm_hour == 12)
2624297a3b0SGarrett D'Amore 					tm->tm_hour = 0;
2634297a3b0SGarrett D'Amore 				buf += len;
2644297a3b0SGarrett D'Amore 				break;
2654297a3b0SGarrett D'Amore 			}
2664297a3b0SGarrett D'Amore 
2674297a3b0SGarrett D'Amore 			len = strlen(tptr->pm);
2684297a3b0SGarrett D'Amore 			if (strncasecmp(buf, tptr->pm, len) == 0) {
2694297a3b0SGarrett D'Amore 				if (tm->tm_hour > 12)
2706eaad1d3SGarrett D'Amore 					return (NULL);
2714297a3b0SGarrett D'Amore 				if (tm->tm_hour != 12)
2724297a3b0SGarrett D'Amore 					tm->tm_hour += 12;
2734297a3b0SGarrett D'Amore 				buf += len;
2744297a3b0SGarrett D'Amore 				break;
2754297a3b0SGarrett D'Amore 			}
2764297a3b0SGarrett D'Amore 
2776eaad1d3SGarrett D'Amore 			return (NULL);
2784297a3b0SGarrett D'Amore 
2794297a3b0SGarrett D'Amore 		case 'A':
2804297a3b0SGarrett D'Amore 		case 'a':
2814297a3b0SGarrett D'Amore 			for (i = 0; i < asizeof(tptr->weekday); i++) {
2824297a3b0SGarrett D'Amore 				len = strlen(tptr->weekday[i]);
2834297a3b0SGarrett D'Amore 				if (strncasecmp(buf, tptr->weekday[i], len) ==
2844297a3b0SGarrett D'Amore 				    0)
2854297a3b0SGarrett D'Amore 					break;
2864297a3b0SGarrett D'Amore 				len = strlen(tptr->wday[i]);
2874297a3b0SGarrett D'Amore 				if (strncasecmp(buf, tptr->wday[i], len) == 0)
2884297a3b0SGarrett D'Amore 					break;
2894297a3b0SGarrett D'Amore 			}
2904297a3b0SGarrett D'Amore 			if (i == asizeof(tptr->weekday))
2916eaad1d3SGarrett D'Amore 				return (NULL);
2924297a3b0SGarrett D'Amore 
2934297a3b0SGarrett D'Amore 			tm->tm_wday = i;
2944297a3b0SGarrett D'Amore 			buf += len;
2954297a3b0SGarrett D'Amore 			break;
2964297a3b0SGarrett D'Amore 
2974297a3b0SGarrett D'Amore 		case 'U':
2984297a3b0SGarrett D'Amore 		case 'W':
2994297a3b0SGarrett D'Amore 			/*
3004297a3b0SGarrett D'Amore 			 * XXX This is bogus, as we can not assume any valid
3014297a3b0SGarrett D'Amore 			 * information present in the tm structure at this
3024297a3b0SGarrett D'Amore 			 * point to calculate a real value, so just check the
3034297a3b0SGarrett D'Amore 			 * range for now.
3044297a3b0SGarrett D'Amore 			 */
3056eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
3066eaad1d3SGarrett D'Amore 				return (NULL);
3074297a3b0SGarrett D'Amore 
3084297a3b0SGarrett D'Amore 			len = 2;
3096eaad1d3SGarrett D'Amore 			for (i = 0; len && isdigit(*buf); buf++) {
3104297a3b0SGarrett D'Amore 				i *= 10;
3114297a3b0SGarrett D'Amore 				i += *buf - '0';
3124297a3b0SGarrett D'Amore 				len--;
3134297a3b0SGarrett D'Amore 			}
3144297a3b0SGarrett D'Amore 			if (i > 53)
3156eaad1d3SGarrett D'Amore 				return (NULL);
3164297a3b0SGarrett D'Amore 
3174297a3b0SGarrett D'Amore 			break;
3184297a3b0SGarrett D'Amore 
3194297a3b0SGarrett D'Amore 		case 'w':
3206eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
3216eaad1d3SGarrett D'Amore 				return (NULL);
3224297a3b0SGarrett D'Amore 
3234297a3b0SGarrett D'Amore 			i = *buf - '0';
3244297a3b0SGarrett D'Amore 			if (i > 6)
3256eaad1d3SGarrett D'Amore 				return (NULL);
3264297a3b0SGarrett D'Amore 
3274297a3b0SGarrett D'Amore 			tm->tm_wday = i;
3284297a3b0SGarrett D'Amore 
3294297a3b0SGarrett D'Amore 			break;
3304297a3b0SGarrett D'Amore 
331a11c1571SGary Mills 		case 'd':
3324297a3b0SGarrett D'Amore 		case 'e':
33325f48f67SGordon Ross 			/*
33425f48f67SGordon Ross 			 * The %e format has a space before single digits
33525f48f67SGordon Ross 			 * which we need to skip.
33625f48f67SGordon Ross 			 */
33725f48f67SGordon Ross 			if (isspace(*buf))
33825f48f67SGordon Ross 				buf++;
3394297a3b0SGarrett D'Amore 			/*
3404297a3b0SGarrett D'Amore 			 * The %e specifier is explicitly documented as not
3414297a3b0SGarrett D'Amore 			 * being zero-padded but there is no harm in allowing
3424297a3b0SGarrett D'Amore 			 * such padding.
3434297a3b0SGarrett D'Amore 			 *
3444297a3b0SGarrett D'Amore 			 * XXX The %e specifier may gobble one too many
3454297a3b0SGarrett D'Amore 			 * digits if used incorrectly.
3464297a3b0SGarrett D'Amore 			 */
3476eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
3486eaad1d3SGarrett D'Amore 				return (NULL);
3494297a3b0SGarrett D'Amore 
3504297a3b0SGarrett D'Amore 			len = 2;
3516eaad1d3SGarrett D'Amore 			for (i = 0; len && isdigit(*buf); buf++) {
3524297a3b0SGarrett D'Amore 				i *= 10;
3534297a3b0SGarrett D'Amore 				i += *buf - '0';
3544297a3b0SGarrett D'Amore 				len--;
3554297a3b0SGarrett D'Amore 			}
3564297a3b0SGarrett D'Amore 			if (i > 31)
3576eaad1d3SGarrett D'Amore 				return (NULL);
3584297a3b0SGarrett D'Amore 
3594297a3b0SGarrett D'Amore 			tm->tm_mday = i;
3604297a3b0SGarrett D'Amore 
3614297a3b0SGarrett D'Amore 			break;
3624297a3b0SGarrett D'Amore 
3634297a3b0SGarrett D'Amore 		case 'B':
3644297a3b0SGarrett D'Amore 		case 'b':
3654297a3b0SGarrett D'Amore 		case 'h':
3664297a3b0SGarrett D'Amore 			for (i = 0; i < asizeof(tptr->month); i++) {
3674297a3b0SGarrett D'Amore 				len = strlen(tptr->month[i]);
3684297a3b0SGarrett D'Amore 				if (strncasecmp(buf, tptr->month[i], len) == 0)
3694297a3b0SGarrett D'Amore 					break;
3704297a3b0SGarrett D'Amore 			}
3714297a3b0SGarrett D'Amore 			/*
3724297a3b0SGarrett D'Amore 			 * Try the abbreviated month name if the full name
3734297a3b0SGarrett D'Amore 			 * wasn't found.
3744297a3b0SGarrett D'Amore 			 */
3754297a3b0SGarrett D'Amore 			if (i == asizeof(tptr->month)) {
3764297a3b0SGarrett D'Amore 				for (i = 0; i < asizeof(tptr->month); i++) {
3774297a3b0SGarrett D'Amore 					len = strlen(tptr->mon[i]);
3784297a3b0SGarrett D'Amore 					if (strncasecmp(buf, tptr->mon[i],
3794297a3b0SGarrett D'Amore 					    len) == 0)
3804297a3b0SGarrett D'Amore 						break;
3814297a3b0SGarrett D'Amore 				}
3824297a3b0SGarrett D'Amore 			}
3834297a3b0SGarrett D'Amore 			if (i == asizeof(tptr->month))
3846eaad1d3SGarrett D'Amore 				return (NULL);
3854297a3b0SGarrett D'Amore 
3864297a3b0SGarrett D'Amore 			tm->tm_mon = i;
3874297a3b0SGarrett D'Amore 			buf += len;
3884297a3b0SGarrett D'Amore 			break;
3894297a3b0SGarrett D'Amore 
3904297a3b0SGarrett D'Amore 		case 'm':
3916eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
3926eaad1d3SGarrett D'Amore 				return (NULL);
3934297a3b0SGarrett D'Amore 
3944297a3b0SGarrett D'Amore 			len = 2;
3956eaad1d3SGarrett D'Amore 			for (i = 0; len && isdigit(*buf); buf++) {
3964297a3b0SGarrett D'Amore 				i *= 10;
3974297a3b0SGarrett D'Amore 				i += *buf - '0';
3984297a3b0SGarrett D'Amore 				len--;
3994297a3b0SGarrett D'Amore 			}
4004297a3b0SGarrett D'Amore 			if (i < 1 || i > 12)
4016eaad1d3SGarrett D'Amore 				return (NULL);
4024297a3b0SGarrett D'Amore 
4034297a3b0SGarrett D'Amore 			tm->tm_mon = i - 1;
4044297a3b0SGarrett D'Amore 
4054297a3b0SGarrett D'Amore 			break;
4064297a3b0SGarrett D'Amore 
4076eaad1d3SGarrett D'Amore 		case 's':
4086eaad1d3SGarrett D'Amore 			{
4096eaad1d3SGarrett D'Amore 			char *cp;
4106eaad1d3SGarrett D'Amore 			int sverrno;
4116eaad1d3SGarrett D'Amore 			time_t t;
4126eaad1d3SGarrett D'Amore 
4136eaad1d3SGarrett D'Amore 			sverrno = errno;
4146eaad1d3SGarrett D'Amore 			errno = 0;
4156eaad1d3SGarrett D'Amore 			t = strtol(buf, &cp, 10);
4166eaad1d3SGarrett D'Amore 			if (errno == ERANGE) {
4176eaad1d3SGarrett D'Amore 				errno = sverrno;
4186eaad1d3SGarrett D'Amore 				return (NULL);
4196eaad1d3SGarrett D'Amore 			}
4206eaad1d3SGarrett D'Amore 			errno = sverrno;
4216eaad1d3SGarrett D'Amore 			buf = cp;
4226eaad1d3SGarrett D'Amore 			(void) gmtime_r(&t, tm);
4236eaad1d3SGarrett D'Amore 			*flagsp |= F_GMT;
4246eaad1d3SGarrett D'Amore 			}
4256eaad1d3SGarrett D'Amore 			break;
4266eaad1d3SGarrett D'Amore 
4274297a3b0SGarrett D'Amore 		case 'Y':
4284297a3b0SGarrett D'Amore 		case 'y':
429*e86c3f00SToomas Soome 			if (*buf == '\0' || isspace(*buf))
4304297a3b0SGarrett D'Amore 				break;
4314297a3b0SGarrett D'Amore 
4326eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
4336eaad1d3SGarrett D'Amore 				return (NULL);
4344297a3b0SGarrett D'Amore 
4354297a3b0SGarrett D'Amore 			len = (c == 'Y') ? 4 : 2;
4366eaad1d3SGarrett D'Amore 			for (i = 0; len && isdigit(*buf); buf++) {
4374297a3b0SGarrett D'Amore 				i *= 10;
4384297a3b0SGarrett D'Amore 				i += *buf - '0';
4394297a3b0SGarrett D'Amore 				len--;
4404297a3b0SGarrett D'Amore 			}
4414297a3b0SGarrett D'Amore 			if (c == 'Y')
4424297a3b0SGarrett D'Amore 				i -= 1900;
4434297a3b0SGarrett D'Amore 			if (c == 'y' && i < 69)
4444297a3b0SGarrett D'Amore 				i += 100;
4454297a3b0SGarrett D'Amore 			if (i < 0)
4466eaad1d3SGarrett D'Amore 				return (NULL);
4474297a3b0SGarrett D'Amore 
4484297a3b0SGarrett D'Amore 			tm->tm_year = i;
4494297a3b0SGarrett D'Amore 
4504297a3b0SGarrett D'Amore 			break;
4514297a3b0SGarrett D'Amore 
4524297a3b0SGarrett D'Amore 		case 'Z':
4534297a3b0SGarrett D'Amore 			{
4544297a3b0SGarrett D'Amore 			const char *cp = buf;
4554297a3b0SGarrett D'Amore 			char *zonestr;
4564297a3b0SGarrett D'Amore 
4576eaad1d3SGarrett D'Amore 			while (isupper(*cp))
4584297a3b0SGarrett D'Amore 				++cp;
4594297a3b0SGarrett D'Amore 			if (cp - buf) {
4604297a3b0SGarrett D'Amore 				zonestr = alloca(cp - buf + 1);
4614297a3b0SGarrett D'Amore 				(void) strncpy(zonestr, buf, cp - buf);
4624297a3b0SGarrett D'Amore 				zonestr[cp - buf] = '\0';
4634297a3b0SGarrett D'Amore 				tzset();
4646eaad1d3SGarrett D'Amore 				if (strcmp(zonestr, "GMT") == 0) {
4656eaad1d3SGarrett D'Amore 					*flagsp |= F_GMT;
4666eaad1d3SGarrett D'Amore 				} else if (0 == strcmp(zonestr, tzname[0])) {
4674297a3b0SGarrett D'Amore 					tm->tm_isdst = 0;
4684297a3b0SGarrett D'Amore 				} else if (0 == strcmp(zonestr, tzname[1])) {
4694297a3b0SGarrett D'Amore 					tm->tm_isdst = 1;
4704297a3b0SGarrett D'Amore 				} else {
4716eaad1d3SGarrett D'Amore 					return (NULL);
4724297a3b0SGarrett D'Amore 				}
4734297a3b0SGarrett D'Amore 				buf += cp - buf;
4744297a3b0SGarrett D'Amore 			}
4754297a3b0SGarrett D'Amore 			}
4764297a3b0SGarrett D'Amore 			break;
4774297a3b0SGarrett D'Amore 
4786eaad1d3SGarrett D'Amore 		case 'z':
4796eaad1d3SGarrett D'Amore 			{
4806eaad1d3SGarrett D'Amore 			int sign = 1;
4816eaad1d3SGarrett D'Amore 
4826eaad1d3SGarrett D'Amore 			if (*buf != '+') {
4836eaad1d3SGarrett D'Amore 				if (*buf == '-')
4846eaad1d3SGarrett D'Amore 					sign = -1;
4856eaad1d3SGarrett D'Amore 				else
4866eaad1d3SGarrett D'Amore 					return (NULL);
4876eaad1d3SGarrett D'Amore 			}
4886eaad1d3SGarrett D'Amore 			buf++;
4896eaad1d3SGarrett D'Amore 			i = 0;
4906eaad1d3SGarrett D'Amore 			for (len = 4; len > 0; len--) {
4916eaad1d3SGarrett D'Amore 				if (!isdigit(*buf))
4926eaad1d3SGarrett D'Amore 					return (NULL);
4936eaad1d3SGarrett D'Amore 				i *= 10;
4946eaad1d3SGarrett D'Amore 				i += *buf - '0';
4956eaad1d3SGarrett D'Amore 				buf++;
4966eaad1d3SGarrett D'Amore 			}
4976eaad1d3SGarrett D'Amore 
4986eaad1d3SGarrett D'Amore 			tm->tm_hour -= sign * (i / 100);
4996eaad1d3SGarrett D'Amore 			tm->tm_min -= sign * (i % 100);
5006eaad1d3SGarrett D'Amore 			*flagsp |= F_GMT;
5016eaad1d3SGarrett D'Amore 			}
5026eaad1d3SGarrett D'Amore 			break;
503a11c1571SGary Mills 		case 'n':
504a11c1571SGary Mills 		case 't':
505a11c1571SGary Mills 			while (isspace(*buf))
506a11c1571SGary Mills 				buf++;
507a11c1571SGary Mills 			break;
5086eaad1d3SGarrett D'Amore 		}
5096eaad1d3SGarrett D'Amore 	}
5106eaad1d3SGarrett D'Amore 
5116eaad1d3SGarrett D'Amore 	if (!recurse) {
5126eaad1d3SGarrett D'Amore 		if (buf && (*flagsp & F_GMT)) {
5136eaad1d3SGarrett D'Amore 			time_t t = timegm(tm);
5146eaad1d3SGarrett D'Amore 			(void) localtime_r(&t, tm);
5154297a3b0SGarrett D'Amore 		}
5164297a3b0SGarrett D'Amore 	}
5176eaad1d3SGarrett D'Amore 
5184297a3b0SGarrett D'Amore 	return ((char *)buf);
5194297a3b0SGarrett D'Amore }
5204297a3b0SGarrett D'Amore 
5214297a3b0SGarrett D'Amore char *
strptime(const char * _RESTRICT_KYWD buf,const char * _RESTRICT_KYWD fmt,struct tm * _RESTRICT_KYWD tm)5222d08521bSGarrett D'Amore strptime(const char *_RESTRICT_KYWD buf, const char *_RESTRICT_KYWD fmt,
5232d08521bSGarrett D'Amore     struct tm *_RESTRICT_KYWD tm)
5244297a3b0SGarrett D'Amore {
5252d08521bSGarrett D'Amore 	int	flags = 0;
5262d08521bSGarrett D'Amore 
5272d08521bSGarrett D'Amore 	(void) memset(tm, 0, sizeof (*tm));
5284297a3b0SGarrett D'Amore 
5292d08521bSGarrett D'Amore 	return (__strptime(buf, fmt, tm, &flags, uselocale(NULL)));
5304297a3b0SGarrett D'Amore }
5314297a3b0SGarrett D'Amore 
5324297a3b0SGarrett D'Amore /*
5334297a3b0SGarrett D'Amore  * This is used by Solaris, and is a variant that does not clear the
5344297a3b0SGarrett D'Amore  * incoming tm.  It is triggered by -D_STRPTIME_DONTZERO.
5354297a3b0SGarrett D'Amore  */
5364297a3b0SGarrett D'Amore char *
__strptime_dontzero(const char * _RESTRICT_KYWD buf,const char * _RESTRICT_KYWD fmt,struct tm * _RESTRICT_KYWD tm)5372d08521bSGarrett D'Amore __strptime_dontzero(const char *_RESTRICT_KYWD buf,
5382d08521bSGarrett D'Amore     const char *_RESTRICT_KYWD fmt, struct tm *_RESTRICT_KYWD tm)
5394297a3b0SGarrett D'Amore {
5406eaad1d3SGarrett D'Amore 	int	flags = 0;
5416eaad1d3SGarrett D'Amore 
5422d08521bSGarrett D'Amore 	return (__strptime(buf, fmt, tm, &flags, uselocale(NULL)));
5432d08521bSGarrett D'Amore }
5442d08521bSGarrett D'Amore 
5452d08521bSGarrett D'Amore /*
5462d08521bSGarrett D'Amore  * strptime_l is an extension that seems natural, and indeed, MacOS X
5472d08521bSGarrett D'Amore  * includes it within their <xlocale.h> and it is part of GNU libc as well.
5482d08521bSGarrett D'Amore  * For now we restrict it to the cases where strict namespaces are not
5492d08521bSGarrett D'Amore  * included.  We expect to see it in a future version of POSIX.  locale_t is
5502d08521bSGarrett D'Amore  * not a restrict, since the spec for it doesn't assume its a pointer.  We
5512d08521bSGarrett D'Amore  * therefore pass it analagously to the way strftime_l is specified.
5522d08521bSGarrett D'Amore  *
5532d08521bSGarrett D'Amore  * We are not providing a non-zeroing version at this time.
5542d08521bSGarrett D'Amore  */
5552d08521bSGarrett D'Amore char *
strptime_l(const char * _RESTRICT_KYWD buf,const char * _RESTRICT_KYWD fmt,struct tm * _RESTRICT_KYWD tm,locale_t loc)5562d08521bSGarrett D'Amore strptime_l(const char *_RESTRICT_KYWD buf, const char *_RESTRICT_KYWD fmt,
5572d08521bSGarrett D'Amore     struct tm *_RESTRICT_KYWD tm, locale_t loc)
5582d08521bSGarrett D'Amore {
5592d08521bSGarrett D'Amore 	int	flags =  0;
5602d08521bSGarrett D'Amore 
5612d08521bSGarrett D'Amore 	(void) memset(tm, 0, sizeof (*tm));
5622d08521bSGarrett D'Amore 
5632d08521bSGarrett D'Amore 	return (__strptime(buf, fmt, tm, &flags, loc));
5644297a3b0SGarrett D'Amore }
565