14297a3b0SGarrett D'Amore /*
2*25f48f67SGordon Ross  * Copyright 2011, Nexenta Systems, Inc.  All rights reserved.
34297a3b0SGarrett D'Amore  * Copyright (c) 1994 Powerdog Industries.  All rights reserved.
44297a3b0SGarrett D'Amore  *
54297a3b0SGarrett D'Amore  * Redistribution and use in source and binary forms, with or without
64297a3b0SGarrett D'Amore  * modification, are permitted provided that the following conditions
74297a3b0SGarrett D'Amore  * are met:
85aec55ebSGarrett D'Amore  *
94297a3b0SGarrett D'Amore  * 1. Redistributions of source code must retain the above copyright
104297a3b0SGarrett D'Amore  *    notice, this list of conditions and the following disclaimer.
115aec55ebSGarrett D'Amore  *
124297a3b0SGarrett D'Amore  * 2. Redistributions in binary form must reproduce the above copyright
134297a3b0SGarrett D'Amore  *    notice, this list of conditions and the following disclaimer
144297a3b0SGarrett D'Amore  *    in the documentation and/or other materials provided with the
154297a3b0SGarrett D'Amore  *    distribution.
164297a3b0SGarrett D'Amore  *
174297a3b0SGarrett D'Amore  * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
184297a3b0SGarrett D'Amore  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
194297a3b0SGarrett D'Amore  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
204297a3b0SGarrett D'Amore  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
214297a3b0SGarrett D'Amore  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
224297a3b0SGarrett D'Amore  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
234297a3b0SGarrett D'Amore  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
244297a3b0SGarrett D'Amore  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
254297a3b0SGarrett D'Amore  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
264297a3b0SGarrett D'Amore  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
274297a3b0SGarrett D'Amore  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
285aec55ebSGarrett D'Amore  *
295aec55ebSGarrett D'Amore  * The views and conclusions contained in the software and documentation
305aec55ebSGarrett D'Amore  * are those of the authors and should not be interpreted as representing
315aec55ebSGarrett D'Amore  * official policies, either expressed or implied, of Powerdog Industries.
324297a3b0SGarrett D'Amore  */
335aec55ebSGarrett D'Amore 
344297a3b0SGarrett D'Amore #include "lint.h"
354297a3b0SGarrett D'Amore #include <time.h>
364297a3b0SGarrett D'Amore #include <ctype.h>
374297a3b0SGarrett D'Amore #include <errno.h>
384297a3b0SGarrett D'Amore #include <stdlib.h>
394297a3b0SGarrett D'Amore #include <string.h>
404297a3b0SGarrett D'Amore #include <pthread.h>
414297a3b0SGarrett D'Amore #include <alloca.h>
424297a3b0SGarrett D'Amore #include "timelocal.h"
434297a3b0SGarrett D'Amore 
444297a3b0SGarrett D'Amore #define	asizeof(a)	(sizeof (a) / sizeof ((a)[0]))
454297a3b0SGarrett D'Amore 
466eaad1d3SGarrett D'Amore #define	F_GMT		(1 << 0)
476eaad1d3SGarrett D'Amore #define	F_ZERO		(1 << 1)
486eaad1d3SGarrett D'Amore #define	F_RECURSE	(1 << 2)
496eaad1d3SGarrett D'Amore 
504297a3b0SGarrett D'Amore static char *
516eaad1d3SGarrett D'Amore __strptime(const char *buf, const char *fmt, struct tm *tm, int *flagsp)
524297a3b0SGarrett D'Amore {
534297a3b0SGarrett D'Amore 	char	c;
544297a3b0SGarrett D'Amore 	const char *ptr;
556eaad1d3SGarrett D'Amore 	int	i, len, recurse = 0;
564297a3b0SGarrett D'Amore 	int Ealternative, Oalternative;
574297a3b0SGarrett D'Amore 	struct lc_time_T *tptr = __get_current_time_locale();
584297a3b0SGarrett D'Amore 
596eaad1d3SGarrett D'Amore 	if (*flagsp & F_RECURSE)
606eaad1d3SGarrett D'Amore 		recurse = 1;
616eaad1d3SGarrett D'Amore 	*flagsp |= F_RECURSE;
626eaad1d3SGarrett D'Amore 
636eaad1d3SGarrett D'Amore 	if (*flagsp & F_ZERO)
646eaad1d3SGarrett D'Amore 		(void) memset(tm, 0, sizeof (*tm));
656eaad1d3SGarrett D'Amore 	*flagsp &= ~F_ZERO;
666eaad1d3SGarrett D'Amore 
674297a3b0SGarrett D'Amore 	ptr = fmt;
684297a3b0SGarrett D'Amore 	while (*ptr != 0) {
694297a3b0SGarrett D'Amore 		if (*buf == 0)
704297a3b0SGarrett D'Amore 			break;
714297a3b0SGarrett D'Amore 
724297a3b0SGarrett D'Amore 		c = *ptr++;
734297a3b0SGarrett D'Amore 
744297a3b0SGarrett D'Amore 		if (c != '%') {
756eaad1d3SGarrett D'Amore 			if (isspace(c))
766eaad1d3SGarrett D'Amore 				while (isspace(*buf))
774297a3b0SGarrett D'Amore 					buf++;
784297a3b0SGarrett D'Amore 			else if (c != *buf++)
796eaad1d3SGarrett D'Amore 				return (NULL);
804297a3b0SGarrett D'Amore 			continue;
814297a3b0SGarrett D'Amore 		}
824297a3b0SGarrett D'Amore 
834297a3b0SGarrett D'Amore 		Ealternative = 0;
844297a3b0SGarrett D'Amore 		Oalternative = 0;
854297a3b0SGarrett D'Amore label:
864297a3b0SGarrett D'Amore 		c = *ptr++;
874297a3b0SGarrett D'Amore 		switch (c) {
884297a3b0SGarrett D'Amore 		case 0:
894297a3b0SGarrett D'Amore 		case '%':
904297a3b0SGarrett D'Amore 			if (*buf++ != '%')
916eaad1d3SGarrett D'Amore 				return (NULL);
924297a3b0SGarrett D'Amore 			break;
934297a3b0SGarrett D'Amore 
944297a3b0SGarrett D'Amore 		case '+':
956eaad1d3SGarrett D'Amore 			buf = __strptime(buf, tptr->date_fmt, tm, flagsp);
966eaad1d3SGarrett D'Amore 			if (buf == NULL)
976eaad1d3SGarrett D'Amore 				return (NULL);
984297a3b0SGarrett D'Amore 			break;
994297a3b0SGarrett D'Amore 
1004297a3b0SGarrett D'Amore 		case 'C':
1016eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
1026eaad1d3SGarrett D'Amore 				return (NULL);
1034297a3b0SGarrett D'Amore 
1044297a3b0SGarrett D'Amore 			/* XXX This will break for 3-digit centuries. */
1054297a3b0SGarrett D'Amore 			len = 2;
1066eaad1d3SGarrett D'Amore 			for (i = 0; len && isdigit(*buf); buf++) {
1074297a3b0SGarrett D'Amore 				i *= 10;
1084297a3b0SGarrett D'Amore 				i += *buf - '0';
1094297a3b0SGarrett D'Amore 				len--;
1104297a3b0SGarrett D'Amore 			}
1114297a3b0SGarrett D'Amore 			if (i < 19)
1126eaad1d3SGarrett D'Amore 				return (NULL);
1134297a3b0SGarrett D'Amore 
1144297a3b0SGarrett D'Amore 			tm->tm_year = i * 100 - 1900;
1154297a3b0SGarrett D'Amore 			break;
1164297a3b0SGarrett D'Amore 
1174297a3b0SGarrett D'Amore 		case 'c':
1186eaad1d3SGarrett D'Amore 			buf = __strptime(buf, tptr->c_fmt, tm, flagsp);
1196eaad1d3SGarrett D'Amore 			if (buf == NULL)
1206eaad1d3SGarrett D'Amore 				return (NULL);
1214297a3b0SGarrett D'Amore 			break;
1224297a3b0SGarrett D'Amore 
1234297a3b0SGarrett D'Amore 		case 'D':
1246eaad1d3SGarrett D'Amore 			buf = __strptime(buf, "%m/%d/%y", tm, flagsp);
1256eaad1d3SGarrett D'Amore 			if (buf == NULL)
1266eaad1d3SGarrett D'Amore 				return (NULL);
1274297a3b0SGarrett D'Amore 			break;
1284297a3b0SGarrett D'Amore 
1294297a3b0SGarrett D'Amore 		case 'E':
1304297a3b0SGarrett D'Amore 			if (Ealternative || Oalternative)
1314297a3b0SGarrett D'Amore 				break;
1324297a3b0SGarrett D'Amore 			Ealternative++;
1334297a3b0SGarrett D'Amore 			goto label;
1344297a3b0SGarrett D'Amore 
1354297a3b0SGarrett D'Amore 		case 'O':
1364297a3b0SGarrett D'Amore 			if (Ealternative || Oalternative)
1374297a3b0SGarrett D'Amore 				break;
1384297a3b0SGarrett D'Amore 			Oalternative++;
1394297a3b0SGarrett D'Amore 			goto label;
1404297a3b0SGarrett D'Amore 
1414297a3b0SGarrett D'Amore 		case 'F':
1426eaad1d3SGarrett D'Amore 			buf = __strptime(buf, "%Y-%m-%d", tm, flagsp);
1436eaad1d3SGarrett D'Amore 			if (buf == NULL)
1446eaad1d3SGarrett D'Amore 				return (NULL);
1454297a3b0SGarrett D'Amore 			break;
1464297a3b0SGarrett D'Amore 
1474297a3b0SGarrett D'Amore 		case 'R':
1486eaad1d3SGarrett D'Amore 			buf = __strptime(buf, "%H:%M", tm, flagsp);
1496eaad1d3SGarrett D'Amore 			if (buf == NULL)
1506eaad1d3SGarrett D'Amore 				return (NULL);
1514297a3b0SGarrett D'Amore 			break;
1524297a3b0SGarrett D'Amore 
1534297a3b0SGarrett D'Amore 		case 'r':
1546eaad1d3SGarrett D'Amore 			buf = __strptime(buf, tptr->ampm_fmt, tm, flagsp);
1556eaad1d3SGarrett D'Amore 			if (buf == NULL)
1566eaad1d3SGarrett D'Amore 				return (NULL);
1574297a3b0SGarrett D'Amore 			break;
1584297a3b0SGarrett D'Amore 
1594297a3b0SGarrett D'Amore 		case 'T':
1606eaad1d3SGarrett D'Amore 			buf = __strptime(buf, "%H:%M:%S", tm, flagsp);
1616eaad1d3SGarrett D'Amore 			if (buf == NULL)
1626eaad1d3SGarrett D'Amore 				return (NULL);
1634297a3b0SGarrett D'Amore 			break;
1644297a3b0SGarrett D'Amore 
1654297a3b0SGarrett D'Amore 		case 'X':
1666eaad1d3SGarrett D'Amore 			buf = __strptime(buf, tptr->X_fmt, tm, flagsp);
1676eaad1d3SGarrett D'Amore 			if (buf == NULL)
1686eaad1d3SGarrett D'Amore 				return (NULL);
1694297a3b0SGarrett D'Amore 			break;
1704297a3b0SGarrett D'Amore 
1714297a3b0SGarrett D'Amore 		case 'x':
1726eaad1d3SGarrett D'Amore 			buf = __strptime(buf, tptr->x_fmt, tm, flagsp);
1736eaad1d3SGarrett D'Amore 			if (buf == NULL)
1746eaad1d3SGarrett D'Amore 				return (NULL);
1754297a3b0SGarrett D'Amore 			break;
1764297a3b0SGarrett D'Amore 
1774297a3b0SGarrett D'Amore 		case 'j':
1786eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
1796eaad1d3SGarrett D'Amore 				return (NULL);
1804297a3b0SGarrett D'Amore 
1814297a3b0SGarrett D'Amore 			len = 3;
1826eaad1d3SGarrett D'Amore 			for (i = 0; len && isdigit(*buf); buf++) {
1834297a3b0SGarrett D'Amore 				i *= 10;
1844297a3b0SGarrett D'Amore 				i += *buf - '0';
1854297a3b0SGarrett D'Amore 				len--;
1864297a3b0SGarrett D'Amore 			}
1874297a3b0SGarrett D'Amore 			if (i < 1 || i > 366)
1886eaad1d3SGarrett D'Amore 				return (NULL);
1894297a3b0SGarrett D'Amore 
1904297a3b0SGarrett D'Amore 			tm->tm_yday = i - 1;
1914297a3b0SGarrett D'Amore 			break;
1924297a3b0SGarrett D'Amore 
1934297a3b0SGarrett D'Amore 		case 'M':
1944297a3b0SGarrett D'Amore 		case 'S':
1956eaad1d3SGarrett D'Amore 			if (*buf == 0 || isspace(*buf))
1964297a3b0SGarrett D'Amore 				break;
1974297a3b0SGarrett D'Amore 
1986eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
1996eaad1d3SGarrett D'Amore 				return (NULL);
2004297a3b0SGarrett D'Amore 
2014297a3b0SGarrett D'Amore 			len = 2;
2026eaad1d3SGarrett D'Amore 			for (i = 0; len && isdigit(*buf); buf++) {
2034297a3b0SGarrett D'Amore 				i *= 10;
2044297a3b0SGarrett D'Amore 				i += *buf - '0';
2054297a3b0SGarrett D'Amore 				len--;
2064297a3b0SGarrett D'Amore 			}
2074297a3b0SGarrett D'Amore 
2084297a3b0SGarrett D'Amore 			if (c == 'M') {
2094297a3b0SGarrett D'Amore 				if (i > 59)
2106eaad1d3SGarrett D'Amore 					return (NULL);
2114297a3b0SGarrett D'Amore 				tm->tm_min = i;
2124297a3b0SGarrett D'Amore 			} else {
2134297a3b0SGarrett D'Amore 				if (i > 60)
2146eaad1d3SGarrett D'Amore 					return (NULL);
2154297a3b0SGarrett D'Amore 				tm->tm_sec = i;
2164297a3b0SGarrett D'Amore 			}
2174297a3b0SGarrett D'Amore 
2186eaad1d3SGarrett D'Amore 			if (isspace(*buf))
2196eaad1d3SGarrett D'Amore 				while (*ptr != 0 && !isspace(*ptr))
2204297a3b0SGarrett D'Amore 					ptr++;
2214297a3b0SGarrett D'Amore 			break;
2224297a3b0SGarrett D'Amore 
2234297a3b0SGarrett D'Amore 		case 'H':
2244297a3b0SGarrett D'Amore 		case 'I':
2254297a3b0SGarrett D'Amore 		case 'k':
2264297a3b0SGarrett D'Amore 		case 'l':
2274297a3b0SGarrett D'Amore 			/*
2284297a3b0SGarrett D'Amore 			 * Of these, %l is the only specifier explicitly
2294297a3b0SGarrett D'Amore 			 * documented as not being zero-padded.  However,
2304297a3b0SGarrett D'Amore 			 * there is no harm in allowing zero-padding.
2314297a3b0SGarrett D'Amore 			 *
2324297a3b0SGarrett D'Amore 			 * XXX The %l specifier may gobble one too many
2334297a3b0SGarrett D'Amore 			 * digits if used incorrectly.
2344297a3b0SGarrett D'Amore 			 */
2356eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
2366eaad1d3SGarrett D'Amore 				return (NULL);
2374297a3b0SGarrett D'Amore 
2384297a3b0SGarrett D'Amore 			len = 2;
2396eaad1d3SGarrett D'Amore 			for (i = 0; len && isdigit(*buf); buf++) {
2404297a3b0SGarrett D'Amore 				i *= 10;
2414297a3b0SGarrett D'Amore 				i += *buf - '0';
2424297a3b0SGarrett D'Amore 				len--;
2434297a3b0SGarrett D'Amore 			}
2444297a3b0SGarrett D'Amore 			if (c == 'H' || c == 'k') {
2454297a3b0SGarrett D'Amore 				if (i > 23)
2466eaad1d3SGarrett D'Amore 					return (NULL);
2474297a3b0SGarrett D'Amore 			} else if (i > 12)
2486eaad1d3SGarrett D'Amore 				return (NULL);
2494297a3b0SGarrett D'Amore 
2504297a3b0SGarrett D'Amore 			tm->tm_hour = i;
2514297a3b0SGarrett D'Amore 
2526eaad1d3SGarrett D'Amore 			if (isspace(*buf))
2536eaad1d3SGarrett D'Amore 				while (*ptr != 0 && !isspace(*ptr))
2544297a3b0SGarrett D'Amore 					ptr++;
2554297a3b0SGarrett D'Amore 			break;
2564297a3b0SGarrett D'Amore 
2574297a3b0SGarrett D'Amore 		case 'p':
2584297a3b0SGarrett D'Amore 			/*
2594297a3b0SGarrett D'Amore 			 * XXX This is bogus if parsed before hour-related
2604297a3b0SGarrett D'Amore 			 * specifiers.
2614297a3b0SGarrett D'Amore 			 */
2624297a3b0SGarrett D'Amore 			len = strlen(tptr->am);
2634297a3b0SGarrett D'Amore 			if (strncasecmp(buf, tptr->am, len) == 0) {
2644297a3b0SGarrett D'Amore 				if (tm->tm_hour > 12)
2656eaad1d3SGarrett D'Amore 					return (NULL);
2664297a3b0SGarrett D'Amore 				if (tm->tm_hour == 12)
2674297a3b0SGarrett D'Amore 					tm->tm_hour = 0;
2684297a3b0SGarrett D'Amore 				buf += len;
2694297a3b0SGarrett D'Amore 				break;
2704297a3b0SGarrett D'Amore 			}
2714297a3b0SGarrett D'Amore 
2724297a3b0SGarrett D'Amore 			len = strlen(tptr->pm);
2734297a3b0SGarrett D'Amore 			if (strncasecmp(buf, tptr->pm, len) == 0) {
2744297a3b0SGarrett D'Amore 				if (tm->tm_hour > 12)
2756eaad1d3SGarrett D'Amore 					return (NULL);
2764297a3b0SGarrett D'Amore 				if (tm->tm_hour != 12)
2774297a3b0SGarrett D'Amore 					tm->tm_hour += 12;
2784297a3b0SGarrett D'Amore 				buf += len;
2794297a3b0SGarrett D'Amore 				break;
2804297a3b0SGarrett D'Amore 			}
2814297a3b0SGarrett D'Amore 
2826eaad1d3SGarrett D'Amore 			return (NULL);
2834297a3b0SGarrett D'Amore 
2844297a3b0SGarrett D'Amore 		case 'A':
2854297a3b0SGarrett D'Amore 		case 'a':
2864297a3b0SGarrett D'Amore 			for (i = 0; i < asizeof(tptr->weekday); i++) {
2874297a3b0SGarrett D'Amore 				len = strlen(tptr->weekday[i]);
2884297a3b0SGarrett D'Amore 				if (strncasecmp(buf, tptr->weekday[i], len) ==
2894297a3b0SGarrett D'Amore 				    0)
2904297a3b0SGarrett D'Amore 					break;
2914297a3b0SGarrett D'Amore 				len = strlen(tptr->wday[i]);
2924297a3b0SGarrett D'Amore 				if (strncasecmp(buf, tptr->wday[i], len) == 0)
2934297a3b0SGarrett D'Amore 					break;
2944297a3b0SGarrett D'Amore 			}
2954297a3b0SGarrett D'Amore 			if (i == asizeof(tptr->weekday))
2966eaad1d3SGarrett D'Amore 				return (NULL);
2974297a3b0SGarrett D'Amore 
2984297a3b0SGarrett D'Amore 			tm->tm_wday = i;
2994297a3b0SGarrett D'Amore 			buf += len;
3004297a3b0SGarrett D'Amore 			break;
3014297a3b0SGarrett D'Amore 
3024297a3b0SGarrett D'Amore 		case 'U':
3034297a3b0SGarrett D'Amore 		case 'W':
3044297a3b0SGarrett D'Amore 			/*
3054297a3b0SGarrett D'Amore 			 * XXX This is bogus, as we can not assume any valid
3064297a3b0SGarrett D'Amore 			 * information present in the tm structure at this
3074297a3b0SGarrett D'Amore 			 * point to calculate a real value, so just check the
3084297a3b0SGarrett D'Amore 			 * range for now.
3094297a3b0SGarrett D'Amore 			 */
3106eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
3116eaad1d3SGarrett D'Amore 				return (NULL);
3124297a3b0SGarrett D'Amore 
3134297a3b0SGarrett D'Amore 			len = 2;
3146eaad1d3SGarrett D'Amore 			for (i = 0; len && isdigit(*buf); buf++) {
3154297a3b0SGarrett D'Amore 				i *= 10;
3164297a3b0SGarrett D'Amore 				i += *buf - '0';
3174297a3b0SGarrett D'Amore 				len--;
3184297a3b0SGarrett D'Amore 			}
3194297a3b0SGarrett D'Amore 			if (i > 53)
3206eaad1d3SGarrett D'Amore 				return (NULL);
3214297a3b0SGarrett D'Amore 
3226eaad1d3SGarrett D'Amore 			if (isspace(*buf))
3236eaad1d3SGarrett D'Amore 				while (*ptr != 0 && !isspace(*ptr))
3244297a3b0SGarrett D'Amore 					ptr++;
3254297a3b0SGarrett D'Amore 			break;
3264297a3b0SGarrett D'Amore 
3274297a3b0SGarrett D'Amore 		case 'w':
3286eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
3296eaad1d3SGarrett D'Amore 				return (NULL);
3304297a3b0SGarrett D'Amore 
3314297a3b0SGarrett D'Amore 			i = *buf - '0';
3324297a3b0SGarrett D'Amore 			if (i > 6)
3336eaad1d3SGarrett D'Amore 				return (NULL);
3344297a3b0SGarrett D'Amore 
3354297a3b0SGarrett D'Amore 			tm->tm_wday = i;
3364297a3b0SGarrett D'Amore 
3376eaad1d3SGarrett D'Amore 			if (isspace(*buf))
3386eaad1d3SGarrett D'Amore 				while (*ptr != 0 && !isspace(*ptr))
3394297a3b0SGarrett D'Amore 					ptr++;
3404297a3b0SGarrett D'Amore 			break;
3414297a3b0SGarrett D'Amore 
3424297a3b0SGarrett D'Amore 		case 'e':
343*25f48f67SGordon Ross 			/*
344*25f48f67SGordon Ross 			 * The %e format has a space before single digits
345*25f48f67SGordon Ross 			 * which we need to skip.
346*25f48f67SGordon Ross 			 */
347*25f48f67SGordon Ross 			if (isspace(*buf))
348*25f48f67SGordon Ross 				buf++;
349*25f48f67SGordon Ross 			/* FALLTHROUGH */
350*25f48f67SGordon Ross 		case 'd':
3514297a3b0SGarrett D'Amore 			/*
3524297a3b0SGarrett D'Amore 			 * The %e specifier is explicitly documented as not
3534297a3b0SGarrett D'Amore 			 * being zero-padded but there is no harm in allowing
3544297a3b0SGarrett D'Amore 			 * such padding.
3554297a3b0SGarrett D'Amore 			 *
3564297a3b0SGarrett D'Amore 			 * XXX The %e specifier may gobble one too many
3574297a3b0SGarrett D'Amore 			 * digits if used incorrectly.
3584297a3b0SGarrett D'Amore 			 */
3596eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
3606eaad1d3SGarrett D'Amore 				return (NULL);
3614297a3b0SGarrett D'Amore 
3624297a3b0SGarrett D'Amore 			len = 2;
3636eaad1d3SGarrett D'Amore 			for (i = 0; len && isdigit(*buf); buf++) {
3644297a3b0SGarrett D'Amore 				i *= 10;
3654297a3b0SGarrett D'Amore 				i += *buf - '0';
3664297a3b0SGarrett D'Amore 				len--;
3674297a3b0SGarrett D'Amore 			}
3684297a3b0SGarrett D'Amore 			if (i > 31)
3696eaad1d3SGarrett D'Amore 				return (NULL);
3704297a3b0SGarrett D'Amore 
3714297a3b0SGarrett D'Amore 			tm->tm_mday = i;
3724297a3b0SGarrett D'Amore 
3736eaad1d3SGarrett D'Amore 			if (isspace(*buf))
3746eaad1d3SGarrett D'Amore 				while (*ptr != 0 && !isspace(*ptr))
3754297a3b0SGarrett D'Amore 					ptr++;
3764297a3b0SGarrett D'Amore 			break;
3774297a3b0SGarrett D'Amore 
3784297a3b0SGarrett D'Amore 		case 'B':
3794297a3b0SGarrett D'Amore 		case 'b':
3804297a3b0SGarrett D'Amore 		case 'h':
3814297a3b0SGarrett D'Amore 			for (i = 0; i < asizeof(tptr->month); i++) {
3824297a3b0SGarrett D'Amore 				len = strlen(tptr->month[i]);
3834297a3b0SGarrett D'Amore 				if (strncasecmp(buf, tptr->month[i], len) == 0)
3844297a3b0SGarrett D'Amore 					break;
3854297a3b0SGarrett D'Amore 			}
3864297a3b0SGarrett D'Amore 			/*
3874297a3b0SGarrett D'Amore 			 * Try the abbreviated month name if the full name
3884297a3b0SGarrett D'Amore 			 * wasn't found.
3894297a3b0SGarrett D'Amore 			 */
3904297a3b0SGarrett D'Amore 			if (i == asizeof(tptr->month)) {
3914297a3b0SGarrett D'Amore 				for (i = 0; i < asizeof(tptr->month); i++) {
3924297a3b0SGarrett D'Amore 					len = strlen(tptr->mon[i]);
3934297a3b0SGarrett D'Amore 					if (strncasecmp(buf, tptr->mon[i],
3944297a3b0SGarrett D'Amore 					    len) == 0)
3954297a3b0SGarrett D'Amore 						break;
3964297a3b0SGarrett D'Amore 				}
3974297a3b0SGarrett D'Amore 			}
3984297a3b0SGarrett D'Amore 			if (i == asizeof(tptr->month))
3996eaad1d3SGarrett D'Amore 				return (NULL);
4004297a3b0SGarrett D'Amore 
4014297a3b0SGarrett D'Amore 			tm->tm_mon = i;
4024297a3b0SGarrett D'Amore 			buf += len;
4034297a3b0SGarrett D'Amore 			break;
4044297a3b0SGarrett D'Amore 
4054297a3b0SGarrett D'Amore 		case 'm':
4066eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
4076eaad1d3SGarrett D'Amore 				return (NULL);
4084297a3b0SGarrett D'Amore 
4094297a3b0SGarrett D'Amore 			len = 2;
4106eaad1d3SGarrett D'Amore 			for (i = 0; len && isdigit(*buf); buf++) {
4114297a3b0SGarrett D'Amore 				i *= 10;
4124297a3b0SGarrett D'Amore 				i += *buf - '0';
4134297a3b0SGarrett D'Amore 				len--;
4144297a3b0SGarrett D'Amore 			}
4154297a3b0SGarrett D'Amore 			if (i < 1 || i > 12)
4166eaad1d3SGarrett D'Amore 				return (NULL);
4174297a3b0SGarrett D'Amore 
4184297a3b0SGarrett D'Amore 			tm->tm_mon = i - 1;
4194297a3b0SGarrett D'Amore 
4206eaad1d3SGarrett D'Amore 			if (isspace(*buf))
4216eaad1d3SGarrett D'Amore 				while (*ptr != NULL && !isspace(*ptr))
4224297a3b0SGarrett D'Amore 					ptr++;
4234297a3b0SGarrett D'Amore 			break;
4244297a3b0SGarrett D'Amore 
4256eaad1d3SGarrett D'Amore 		case 's':
4266eaad1d3SGarrett D'Amore 			{
4276eaad1d3SGarrett D'Amore 			char *cp;
4286eaad1d3SGarrett D'Amore 			int sverrno;
4296eaad1d3SGarrett D'Amore 			time_t t;
4306eaad1d3SGarrett D'Amore 
4316eaad1d3SGarrett D'Amore 			sverrno = errno;
4326eaad1d3SGarrett D'Amore 			errno = 0;
4336eaad1d3SGarrett D'Amore 			t = strtol(buf, &cp, 10);
4346eaad1d3SGarrett D'Amore 			if (errno == ERANGE) {
4356eaad1d3SGarrett D'Amore 				errno = sverrno;
4366eaad1d3SGarrett D'Amore 				return (NULL);
4376eaad1d3SGarrett D'Amore 			}
4386eaad1d3SGarrett D'Amore 			errno = sverrno;
4396eaad1d3SGarrett D'Amore 			buf = cp;
4406eaad1d3SGarrett D'Amore 			(void) gmtime_r(&t, tm);
4416eaad1d3SGarrett D'Amore 			*flagsp |= F_GMT;
4426eaad1d3SGarrett D'Amore 			}
4436eaad1d3SGarrett D'Amore 			break;
4446eaad1d3SGarrett D'Amore 
4454297a3b0SGarrett D'Amore 		case 'Y':
4464297a3b0SGarrett D'Amore 		case 'y':
4476eaad1d3SGarrett D'Amore 			if (*buf == NULL || isspace(*buf))
4484297a3b0SGarrett D'Amore 				break;
4494297a3b0SGarrett D'Amore 
4506eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
4516eaad1d3SGarrett D'Amore 				return (NULL);
4524297a3b0SGarrett D'Amore 
4534297a3b0SGarrett D'Amore 			len = (c == 'Y') ? 4 : 2;
4546eaad1d3SGarrett D'Amore 			for (i = 0; len && isdigit(*buf); buf++) {
4554297a3b0SGarrett D'Amore 				i *= 10;
4564297a3b0SGarrett D'Amore 				i += *buf - '0';
4574297a3b0SGarrett D'Amore 				len--;
4584297a3b0SGarrett D'Amore 			}
4594297a3b0SGarrett D'Amore 			if (c == 'Y')
4604297a3b0SGarrett D'Amore 				i -= 1900;
4614297a3b0SGarrett D'Amore 			if (c == 'y' && i < 69)
4624297a3b0SGarrett D'Amore 				i += 100;
4634297a3b0SGarrett D'Amore 			if (i < 0)
4646eaad1d3SGarrett D'Amore 				return (NULL);
4654297a3b0SGarrett D'Amore 
4664297a3b0SGarrett D'Amore 			tm->tm_year = i;
4674297a3b0SGarrett D'Amore 
4686eaad1d3SGarrett D'Amore 			if (isspace(*buf))
4696eaad1d3SGarrett D'Amore 				while (*ptr != 0 && !isspace(*ptr))
4704297a3b0SGarrett D'Amore 					ptr++;
4714297a3b0SGarrett D'Amore 			break;
4724297a3b0SGarrett D'Amore 
4734297a3b0SGarrett D'Amore 		case 'Z':
4744297a3b0SGarrett D'Amore 			{
4754297a3b0SGarrett D'Amore 			const char *cp = buf;
4764297a3b0SGarrett D'Amore 			char *zonestr;
4774297a3b0SGarrett D'Amore 
4786eaad1d3SGarrett D'Amore 			while (isupper(*cp))
4794297a3b0SGarrett D'Amore 				++cp;
4804297a3b0SGarrett D'Amore 			if (cp - buf) {
4814297a3b0SGarrett D'Amore 				zonestr = alloca(cp - buf + 1);
4824297a3b0SGarrett D'Amore 				(void) strncpy(zonestr, buf, cp - buf);
4834297a3b0SGarrett D'Amore 				zonestr[cp - buf] = '\0';
4844297a3b0SGarrett D'Amore 				tzset();
4856eaad1d3SGarrett D'Amore 				if (strcmp(zonestr, "GMT") == 0) {
4866eaad1d3SGarrett D'Amore 					*flagsp |= F_GMT;
4876eaad1d3SGarrett D'Amore 				} else if (0 == strcmp(zonestr, tzname[0])) {
4884297a3b0SGarrett D'Amore 					tm->tm_isdst = 0;
4894297a3b0SGarrett D'Amore 				} else if (0 == strcmp(zonestr, tzname[1])) {
4904297a3b0SGarrett D'Amore 					tm->tm_isdst = 1;
4914297a3b0SGarrett D'Amore 				} else {
4926eaad1d3SGarrett D'Amore 					return (NULL);
4934297a3b0SGarrett D'Amore 				}
4944297a3b0SGarrett D'Amore 				buf += cp - buf;
4954297a3b0SGarrett D'Amore 			}
4964297a3b0SGarrett D'Amore 			}
4974297a3b0SGarrett D'Amore 			break;
4984297a3b0SGarrett D'Amore 
4996eaad1d3SGarrett D'Amore 		case 'z':
5006eaad1d3SGarrett D'Amore 			{
5016eaad1d3SGarrett D'Amore 			int sign = 1;
5026eaad1d3SGarrett D'Amore 
5036eaad1d3SGarrett D'Amore 			if (*buf != '+') {
5046eaad1d3SGarrett D'Amore 				if (*buf == '-')
5056eaad1d3SGarrett D'Amore 					sign = -1;
5066eaad1d3SGarrett D'Amore 				else
5076eaad1d3SGarrett D'Amore 					return (NULL);
5086eaad1d3SGarrett D'Amore 			}
5096eaad1d3SGarrett D'Amore 			buf++;
5106eaad1d3SGarrett D'Amore 			i = 0;
5116eaad1d3SGarrett D'Amore 			for (len = 4; len > 0; len--) {
5126eaad1d3SGarrett D'Amore 				if (!isdigit(*buf))
5136eaad1d3SGarrett D'Amore 					return (NULL);
5146eaad1d3SGarrett D'Amore 				i *= 10;
5156eaad1d3SGarrett D'Amore 				i += *buf - '0';
5166eaad1d3SGarrett D'Amore 				buf++;
5176eaad1d3SGarrett D'Amore 			}
5186eaad1d3SGarrett D'Amore 
5196eaad1d3SGarrett D'Amore 			tm->tm_hour -= sign * (i / 100);
5206eaad1d3SGarrett D'Amore 			tm->tm_min -= sign * (i % 100);
5216eaad1d3SGarrett D'Amore 			*flagsp |= F_GMT;
5226eaad1d3SGarrett D'Amore 			}
5236eaad1d3SGarrett D'Amore 			break;
5246eaad1d3SGarrett D'Amore 		}
5256eaad1d3SGarrett D'Amore 	}
5266eaad1d3SGarrett D'Amore 
5276eaad1d3SGarrett D'Amore 	if (!recurse) {
5286eaad1d3SGarrett D'Amore 		if (buf && (*flagsp & F_GMT)) {
5296eaad1d3SGarrett D'Amore 			time_t t = timegm(tm);
5306eaad1d3SGarrett D'Amore 			(void) localtime_r(&t, tm);
5314297a3b0SGarrett D'Amore 		}
5324297a3b0SGarrett D'Amore 	}
5336eaad1d3SGarrett D'Amore 
5344297a3b0SGarrett D'Amore 	return ((char *)buf);
5354297a3b0SGarrett D'Amore }
5364297a3b0SGarrett D'Amore 
5374297a3b0SGarrett D'Amore char *
5384297a3b0SGarrett D'Amore strptime(const char *buf, const char *fmt, struct tm *tm)
5394297a3b0SGarrett D'Amore {
5406eaad1d3SGarrett D'Amore 	int	flags = F_ZERO;
5414297a3b0SGarrett D'Amore 
5426eaad1d3SGarrett D'Amore 	return (__strptime(buf, fmt, tm, &flags));
5434297a3b0SGarrett D'Amore }
5444297a3b0SGarrett D'Amore 
5454297a3b0SGarrett D'Amore /*
5464297a3b0SGarrett D'Amore  * This is used by Solaris, and is a variant that does not clear the
5474297a3b0SGarrett D'Amore  * incoming tm.  It is triggered by -D_STRPTIME_DONTZERO.
5484297a3b0SGarrett D'Amore  */
5494297a3b0SGarrett D'Amore char *
5504297a3b0SGarrett D'Amore __strptime_dontzero(const char *buf, const char *fmt, struct tm *tm)
5514297a3b0SGarrett D'Amore {
5526eaad1d3SGarrett D'Amore 	int	flags = 0;
5536eaad1d3SGarrett D'Amore 
5546eaad1d3SGarrett D'Amore 	return (__strptime(buf, fmt, tm, &flags));
5554297a3b0SGarrett D'Amore }
556