14297a3b0SGarrett D'Amore /*
26b5e5868SGarrett D'Amore  * Copyright 2010, 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 
46*6eaad1d3SGarrett D'Amore #define	F_GMT		(1 << 0)
47*6eaad1d3SGarrett D'Amore #define	F_ZERO		(1 << 1)
48*6eaad1d3SGarrett D'Amore #define	F_RECURSE	(1 << 2)
49*6eaad1d3SGarrett D'Amore 
504297a3b0SGarrett D'Amore static char *
51*6eaad1d3SGarrett 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;
55*6eaad1d3SGarrett 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 
59*6eaad1d3SGarrett D'Amore 	if (*flagsp & F_RECURSE)
60*6eaad1d3SGarrett D'Amore 		recurse = 1;
61*6eaad1d3SGarrett D'Amore 	*flagsp |= F_RECURSE;
62*6eaad1d3SGarrett D'Amore 
63*6eaad1d3SGarrett D'Amore 	if (*flagsp & F_ZERO)
64*6eaad1d3SGarrett D'Amore 		(void) memset(tm, 0, sizeof (*tm));
65*6eaad1d3SGarrett D'Amore 	*flagsp &= ~F_ZERO;
66*6eaad1d3SGarrett 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 != '%') {
75*6eaad1d3SGarrett D'Amore 			if (isspace(c))
76*6eaad1d3SGarrett D'Amore 				while (isspace(*buf))
774297a3b0SGarrett D'Amore 					buf++;
784297a3b0SGarrett D'Amore 			else if (c != *buf++)
79*6eaad1d3SGarrett 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++ != '%')
91*6eaad1d3SGarrett D'Amore 				return (NULL);
924297a3b0SGarrett D'Amore 			break;
934297a3b0SGarrett D'Amore 
944297a3b0SGarrett D'Amore 		case '+':
95*6eaad1d3SGarrett D'Amore 			buf = __strptime(buf, tptr->date_fmt, tm, flagsp);
96*6eaad1d3SGarrett D'Amore 			if (buf == NULL)
97*6eaad1d3SGarrett D'Amore 				return (NULL);
984297a3b0SGarrett D'Amore 			break;
994297a3b0SGarrett D'Amore 
1004297a3b0SGarrett D'Amore 		case 'C':
101*6eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
102*6eaad1d3SGarrett D'Amore 				return (NULL);
1034297a3b0SGarrett D'Amore 
1044297a3b0SGarrett D'Amore 			/* XXX This will break for 3-digit centuries. */
1054297a3b0SGarrett D'Amore 			len = 2;
106*6eaad1d3SGarrett 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)
112*6eaad1d3SGarrett 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':
118*6eaad1d3SGarrett D'Amore 			buf = __strptime(buf, tptr->c_fmt, tm, flagsp);
119*6eaad1d3SGarrett D'Amore 			if (buf == NULL)
120*6eaad1d3SGarrett D'Amore 				return (NULL);
1214297a3b0SGarrett D'Amore 			break;
1224297a3b0SGarrett D'Amore 
1234297a3b0SGarrett D'Amore 		case 'D':
124*6eaad1d3SGarrett D'Amore 			buf = __strptime(buf, "%m/%d/%y", tm, flagsp);
125*6eaad1d3SGarrett D'Amore 			if (buf == NULL)
126*6eaad1d3SGarrett 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':
142*6eaad1d3SGarrett D'Amore 			buf = __strptime(buf, "%Y-%m-%d", tm, flagsp);
143*6eaad1d3SGarrett D'Amore 			if (buf == NULL)
144*6eaad1d3SGarrett D'Amore 				return (NULL);
1454297a3b0SGarrett D'Amore 			break;
1464297a3b0SGarrett D'Amore 
1474297a3b0SGarrett D'Amore 		case 'R':
148*6eaad1d3SGarrett D'Amore 			buf = __strptime(buf, "%H:%M", tm, flagsp);
149*6eaad1d3SGarrett D'Amore 			if (buf == NULL)
150*6eaad1d3SGarrett D'Amore 				return (NULL);
1514297a3b0SGarrett D'Amore 			break;
1524297a3b0SGarrett D'Amore 
1534297a3b0SGarrett D'Amore 		case 'r':
154*6eaad1d3SGarrett D'Amore 			buf = __strptime(buf, tptr->ampm_fmt, tm, flagsp);
155*6eaad1d3SGarrett D'Amore 			if (buf == NULL)
156*6eaad1d3SGarrett D'Amore 				return (NULL);
1574297a3b0SGarrett D'Amore 			break;
1584297a3b0SGarrett D'Amore 
1594297a3b0SGarrett D'Amore 		case 'T':
160*6eaad1d3SGarrett D'Amore 			buf = __strptime(buf, "%H:%M:%S", tm, flagsp);
161*6eaad1d3SGarrett D'Amore 			if (buf == NULL)
162*6eaad1d3SGarrett D'Amore 				return (NULL);
1634297a3b0SGarrett D'Amore 			break;
1644297a3b0SGarrett D'Amore 
1654297a3b0SGarrett D'Amore 		case 'X':
166*6eaad1d3SGarrett D'Amore 			buf = __strptime(buf, tptr->X_fmt, tm, flagsp);
167*6eaad1d3SGarrett D'Amore 			if (buf == NULL)
168*6eaad1d3SGarrett D'Amore 				return (NULL);
1694297a3b0SGarrett D'Amore 			break;
1704297a3b0SGarrett D'Amore 
1714297a3b0SGarrett D'Amore 		case 'x':
172*6eaad1d3SGarrett D'Amore 			buf = __strptime(buf, tptr->x_fmt, tm, flagsp);
173*6eaad1d3SGarrett D'Amore 			if (buf == NULL)
174*6eaad1d3SGarrett D'Amore 				return (NULL);
1754297a3b0SGarrett D'Amore 			break;
1764297a3b0SGarrett D'Amore 
1774297a3b0SGarrett D'Amore 		case 'j':
178*6eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
179*6eaad1d3SGarrett D'Amore 				return (NULL);
1804297a3b0SGarrett D'Amore 
1814297a3b0SGarrett D'Amore 			len = 3;
182*6eaad1d3SGarrett 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)
188*6eaad1d3SGarrett 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':
195*6eaad1d3SGarrett D'Amore 			if (*buf == 0 || isspace(*buf))
1964297a3b0SGarrett D'Amore 				break;
1974297a3b0SGarrett D'Amore 
198*6eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
199*6eaad1d3SGarrett D'Amore 				return (NULL);
2004297a3b0SGarrett D'Amore 
2014297a3b0SGarrett D'Amore 			len = 2;
202*6eaad1d3SGarrett 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)
210*6eaad1d3SGarrett D'Amore 					return (NULL);
2114297a3b0SGarrett D'Amore 				tm->tm_min = i;
2124297a3b0SGarrett D'Amore 			} else {
2134297a3b0SGarrett D'Amore 				if (i > 60)
214*6eaad1d3SGarrett D'Amore 					return (NULL);
2154297a3b0SGarrett D'Amore 				tm->tm_sec = i;
2164297a3b0SGarrett D'Amore 			}
2174297a3b0SGarrett D'Amore 
218*6eaad1d3SGarrett D'Amore 			if (isspace(*buf))
219*6eaad1d3SGarrett 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 			 */
235*6eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
236*6eaad1d3SGarrett D'Amore 				return (NULL);
2374297a3b0SGarrett D'Amore 
2384297a3b0SGarrett D'Amore 			len = 2;
239*6eaad1d3SGarrett 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)
246*6eaad1d3SGarrett D'Amore 					return (NULL);
2474297a3b0SGarrett D'Amore 			} else if (i > 12)
248*6eaad1d3SGarrett D'Amore 				return (NULL);
2494297a3b0SGarrett D'Amore 
2504297a3b0SGarrett D'Amore 			tm->tm_hour = i;
2514297a3b0SGarrett D'Amore 
252*6eaad1d3SGarrett D'Amore 			if (isspace(*buf))
253*6eaad1d3SGarrett 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)
265*6eaad1d3SGarrett 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)
275*6eaad1d3SGarrett 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 
282*6eaad1d3SGarrett 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))
296*6eaad1d3SGarrett 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 			 */
310*6eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
311*6eaad1d3SGarrett D'Amore 				return (NULL);
3124297a3b0SGarrett D'Amore 
3134297a3b0SGarrett D'Amore 			len = 2;
314*6eaad1d3SGarrett 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)
320*6eaad1d3SGarrett D'Amore 				return (NULL);
3214297a3b0SGarrett D'Amore 
322*6eaad1d3SGarrett D'Amore 			if (isspace(*buf))
323*6eaad1d3SGarrett D'Amore 				while (*ptr != 0 && !isspace(*ptr))
3244297a3b0SGarrett D'Amore 					ptr++;
3254297a3b0SGarrett D'Amore 			break;
3264297a3b0SGarrett D'Amore 
3274297a3b0SGarrett D'Amore 		case 'w':
328*6eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
329*6eaad1d3SGarrett D'Amore 				return (NULL);
3304297a3b0SGarrett D'Amore 
3314297a3b0SGarrett D'Amore 			i = *buf - '0';
3324297a3b0SGarrett D'Amore 			if (i > 6)
333*6eaad1d3SGarrett D'Amore 				return (NULL);
3344297a3b0SGarrett D'Amore 
3354297a3b0SGarrett D'Amore 			tm->tm_wday = i;
3364297a3b0SGarrett D'Amore 
337*6eaad1d3SGarrett D'Amore 			if (isspace(*buf))
338*6eaad1d3SGarrett D'Amore 				while (*ptr != 0 && !isspace(*ptr))
3394297a3b0SGarrett D'Amore 					ptr++;
3404297a3b0SGarrett D'Amore 			break;
3414297a3b0SGarrett D'Amore 
3424297a3b0SGarrett D'Amore 		case 'd':
3434297a3b0SGarrett D'Amore 		case 'e':
3444297a3b0SGarrett D'Amore 			/*
3454297a3b0SGarrett D'Amore 			 * The %e specifier is explicitly documented as not
3464297a3b0SGarrett D'Amore 			 * being zero-padded but there is no harm in allowing
3474297a3b0SGarrett D'Amore 			 * such padding.
3484297a3b0SGarrett D'Amore 			 *
3494297a3b0SGarrett D'Amore 			 * XXX The %e specifier may gobble one too many
3504297a3b0SGarrett D'Amore 			 * digits if used incorrectly.
3514297a3b0SGarrett D'Amore 			 */
352*6eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
353*6eaad1d3SGarrett D'Amore 				return (NULL);
3544297a3b0SGarrett D'Amore 
3554297a3b0SGarrett D'Amore 			len = 2;
356*6eaad1d3SGarrett D'Amore 			for (i = 0; len && isdigit(*buf); buf++) {
3574297a3b0SGarrett D'Amore 				i *= 10;
3584297a3b0SGarrett D'Amore 				i += *buf - '0';
3594297a3b0SGarrett D'Amore 				len--;
3604297a3b0SGarrett D'Amore 			}
3614297a3b0SGarrett D'Amore 			if (i > 31)
362*6eaad1d3SGarrett D'Amore 				return (NULL);
3634297a3b0SGarrett D'Amore 
3644297a3b0SGarrett D'Amore 			tm->tm_mday = i;
3654297a3b0SGarrett D'Amore 
366*6eaad1d3SGarrett D'Amore 			if (isspace(*buf))
367*6eaad1d3SGarrett D'Amore 				while (*ptr != 0 && !isspace(*ptr))
3684297a3b0SGarrett D'Amore 					ptr++;
3694297a3b0SGarrett D'Amore 			break;
3704297a3b0SGarrett D'Amore 
3714297a3b0SGarrett D'Amore 		case 'B':
3724297a3b0SGarrett D'Amore 		case 'b':
3734297a3b0SGarrett D'Amore 		case 'h':
3744297a3b0SGarrett D'Amore 			for (i = 0; i < asizeof(tptr->month); i++) {
3754297a3b0SGarrett D'Amore 				len = strlen(tptr->month[i]);
3764297a3b0SGarrett D'Amore 				if (strncasecmp(buf, tptr->month[i], len) == 0)
3774297a3b0SGarrett D'Amore 					break;
3784297a3b0SGarrett D'Amore 			}
3794297a3b0SGarrett D'Amore 			/*
3804297a3b0SGarrett D'Amore 			 * Try the abbreviated month name if the full name
3814297a3b0SGarrett D'Amore 			 * wasn't found.
3824297a3b0SGarrett D'Amore 			 */
3834297a3b0SGarrett D'Amore 			if (i == asizeof(tptr->month)) {
3844297a3b0SGarrett D'Amore 				for (i = 0; i < asizeof(tptr->month); i++) {
3854297a3b0SGarrett D'Amore 					len = strlen(tptr->mon[i]);
3864297a3b0SGarrett D'Amore 					if (strncasecmp(buf, tptr->mon[i],
3874297a3b0SGarrett D'Amore 					    len) == 0)
3884297a3b0SGarrett D'Amore 						break;
3894297a3b0SGarrett D'Amore 				}
3904297a3b0SGarrett D'Amore 			}
3914297a3b0SGarrett D'Amore 			if (i == asizeof(tptr->month))
392*6eaad1d3SGarrett D'Amore 				return (NULL);
3934297a3b0SGarrett D'Amore 
3944297a3b0SGarrett D'Amore 			tm->tm_mon = i;
3954297a3b0SGarrett D'Amore 			buf += len;
3964297a3b0SGarrett D'Amore 			break;
3974297a3b0SGarrett D'Amore 
3984297a3b0SGarrett D'Amore 		case 'm':
399*6eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
400*6eaad1d3SGarrett D'Amore 				return (NULL);
4014297a3b0SGarrett D'Amore 
4024297a3b0SGarrett D'Amore 			len = 2;
403*6eaad1d3SGarrett D'Amore 			for (i = 0; len && isdigit(*buf); buf++) {
4044297a3b0SGarrett D'Amore 				i *= 10;
4054297a3b0SGarrett D'Amore 				i += *buf - '0';
4064297a3b0SGarrett D'Amore 				len--;
4074297a3b0SGarrett D'Amore 			}
4084297a3b0SGarrett D'Amore 			if (i < 1 || i > 12)
409*6eaad1d3SGarrett D'Amore 				return (NULL);
4104297a3b0SGarrett D'Amore 
4114297a3b0SGarrett D'Amore 			tm->tm_mon = i - 1;
4124297a3b0SGarrett D'Amore 
413*6eaad1d3SGarrett D'Amore 			if (isspace(*buf))
414*6eaad1d3SGarrett D'Amore 				while (*ptr != NULL && !isspace(*ptr))
4154297a3b0SGarrett D'Amore 					ptr++;
4164297a3b0SGarrett D'Amore 			break;
4174297a3b0SGarrett D'Amore 
418*6eaad1d3SGarrett D'Amore 		case 's':
419*6eaad1d3SGarrett D'Amore 			{
420*6eaad1d3SGarrett D'Amore 			char *cp;
421*6eaad1d3SGarrett D'Amore 			int sverrno;
422*6eaad1d3SGarrett D'Amore 			time_t t;
423*6eaad1d3SGarrett D'Amore 
424*6eaad1d3SGarrett D'Amore 			sverrno = errno;
425*6eaad1d3SGarrett D'Amore 			errno = 0;
426*6eaad1d3SGarrett D'Amore 			t = strtol(buf, &cp, 10);
427*6eaad1d3SGarrett D'Amore 			if (errno == ERANGE) {
428*6eaad1d3SGarrett D'Amore 				errno = sverrno;
429*6eaad1d3SGarrett D'Amore 				return (NULL);
430*6eaad1d3SGarrett D'Amore 			}
431*6eaad1d3SGarrett D'Amore 			errno = sverrno;
432*6eaad1d3SGarrett D'Amore 			buf = cp;
433*6eaad1d3SGarrett D'Amore 			(void) gmtime_r(&t, tm);
434*6eaad1d3SGarrett D'Amore 			*flagsp |= F_GMT;
435*6eaad1d3SGarrett D'Amore 			}
436*6eaad1d3SGarrett D'Amore 			break;
437*6eaad1d3SGarrett D'Amore 
4384297a3b0SGarrett D'Amore 		case 'Y':
4394297a3b0SGarrett D'Amore 		case 'y':
440*6eaad1d3SGarrett D'Amore 			if (*buf == NULL || isspace(*buf))
4414297a3b0SGarrett D'Amore 				break;
4424297a3b0SGarrett D'Amore 
443*6eaad1d3SGarrett D'Amore 			if (!isdigit(*buf))
444*6eaad1d3SGarrett D'Amore 				return (NULL);
4454297a3b0SGarrett D'Amore 
4464297a3b0SGarrett D'Amore 			len = (c == 'Y') ? 4 : 2;
447*6eaad1d3SGarrett D'Amore 			for (i = 0; len && isdigit(*buf); buf++) {
4484297a3b0SGarrett D'Amore 				i *= 10;
4494297a3b0SGarrett D'Amore 				i += *buf - '0';
4504297a3b0SGarrett D'Amore 				len--;
4514297a3b0SGarrett D'Amore 			}
4524297a3b0SGarrett D'Amore 			if (c == 'Y')
4534297a3b0SGarrett D'Amore 				i -= 1900;
4544297a3b0SGarrett D'Amore 			if (c == 'y' && i < 69)
4554297a3b0SGarrett D'Amore 				i += 100;
4564297a3b0SGarrett D'Amore 			if (i < 0)
457*6eaad1d3SGarrett D'Amore 				return (NULL);
4584297a3b0SGarrett D'Amore 
4594297a3b0SGarrett D'Amore 			tm->tm_year = i;
4604297a3b0SGarrett D'Amore 
461*6eaad1d3SGarrett D'Amore 			if (isspace(*buf))
462*6eaad1d3SGarrett D'Amore 				while (*ptr != 0 && !isspace(*ptr))
4634297a3b0SGarrett D'Amore 					ptr++;
4644297a3b0SGarrett D'Amore 			break;
4654297a3b0SGarrett D'Amore 
4664297a3b0SGarrett D'Amore 		case 'Z':
4674297a3b0SGarrett D'Amore 			{
4684297a3b0SGarrett D'Amore 			const char *cp = buf;
4694297a3b0SGarrett D'Amore 			char *zonestr;
4704297a3b0SGarrett D'Amore 
471*6eaad1d3SGarrett D'Amore 			while (isupper(*cp))
4724297a3b0SGarrett D'Amore 				++cp;
4734297a3b0SGarrett D'Amore 			if (cp - buf) {
4744297a3b0SGarrett D'Amore 				zonestr = alloca(cp - buf + 1);
4754297a3b0SGarrett D'Amore 				(void) strncpy(zonestr, buf, cp - buf);
4764297a3b0SGarrett D'Amore 				zonestr[cp - buf] = '\0';
4774297a3b0SGarrett D'Amore 				tzset();
478*6eaad1d3SGarrett D'Amore 				if (strcmp(zonestr, "GMT") == 0) {
479*6eaad1d3SGarrett D'Amore 					*flagsp |= F_GMT;
480*6eaad1d3SGarrett D'Amore 				} else if (0 == strcmp(zonestr, tzname[0])) {
4814297a3b0SGarrett D'Amore 					tm->tm_isdst = 0;
4824297a3b0SGarrett D'Amore 				} else if (0 == strcmp(zonestr, tzname[1])) {
4834297a3b0SGarrett D'Amore 					tm->tm_isdst = 1;
4844297a3b0SGarrett D'Amore 				} else {
485*6eaad1d3SGarrett D'Amore 					return (NULL);
4864297a3b0SGarrett D'Amore 				}
4874297a3b0SGarrett D'Amore 				buf += cp - buf;
4884297a3b0SGarrett D'Amore 			}
4894297a3b0SGarrett D'Amore 			}
4904297a3b0SGarrett D'Amore 			break;
4914297a3b0SGarrett D'Amore 
492*6eaad1d3SGarrett D'Amore 		case 'z':
493*6eaad1d3SGarrett D'Amore 			{
494*6eaad1d3SGarrett D'Amore 			int sign = 1;
495*6eaad1d3SGarrett D'Amore 
496*6eaad1d3SGarrett D'Amore 			if (*buf != '+') {
497*6eaad1d3SGarrett D'Amore 				if (*buf == '-')
498*6eaad1d3SGarrett D'Amore 					sign = -1;
499*6eaad1d3SGarrett D'Amore 				else
500*6eaad1d3SGarrett D'Amore 					return (NULL);
501*6eaad1d3SGarrett D'Amore 			}
502*6eaad1d3SGarrett D'Amore 			buf++;
503*6eaad1d3SGarrett D'Amore 			i = 0;
504*6eaad1d3SGarrett D'Amore 			for (len = 4; len > 0; len--) {
505*6eaad1d3SGarrett D'Amore 				if (!isdigit(*buf))
506*6eaad1d3SGarrett D'Amore 					return (NULL);
507*6eaad1d3SGarrett D'Amore 				i *= 10;
508*6eaad1d3SGarrett D'Amore 				i += *buf - '0';
509*6eaad1d3SGarrett D'Amore 				buf++;
510*6eaad1d3SGarrett D'Amore 			}
511*6eaad1d3SGarrett D'Amore 
512*6eaad1d3SGarrett D'Amore 			tm->tm_hour -= sign * (i / 100);
513*6eaad1d3SGarrett D'Amore 			tm->tm_min -= sign * (i % 100);
514*6eaad1d3SGarrett D'Amore 			*flagsp |= F_GMT;
515*6eaad1d3SGarrett D'Amore 			}
516*6eaad1d3SGarrett D'Amore 			break;
517*6eaad1d3SGarrett D'Amore 		}
518*6eaad1d3SGarrett D'Amore 	}
519*6eaad1d3SGarrett D'Amore 
520*6eaad1d3SGarrett D'Amore 	if (!recurse) {
521*6eaad1d3SGarrett D'Amore 		if (buf && (*flagsp & F_GMT)) {
522*6eaad1d3SGarrett D'Amore 			time_t t = timegm(tm);
523*6eaad1d3SGarrett D'Amore 			(void) localtime_r(&t, tm);
5244297a3b0SGarrett D'Amore 		}
5254297a3b0SGarrett D'Amore 	}
526*6eaad1d3SGarrett D'Amore 
5274297a3b0SGarrett D'Amore 	return ((char *)buf);
5284297a3b0SGarrett D'Amore }
5294297a3b0SGarrett D'Amore 
5304297a3b0SGarrett D'Amore char *
5314297a3b0SGarrett D'Amore strptime(const char *buf, const char *fmt, struct tm *tm)
5324297a3b0SGarrett D'Amore {
533*6eaad1d3SGarrett D'Amore 	int	flags = F_ZERO;
5344297a3b0SGarrett D'Amore 
535*6eaad1d3SGarrett D'Amore 	return (__strptime(buf, fmt, tm, &flags));
5364297a3b0SGarrett D'Amore }
5374297a3b0SGarrett D'Amore 
5384297a3b0SGarrett D'Amore /*
5394297a3b0SGarrett D'Amore  * This is used by Solaris, and is a variant that does not clear the
5404297a3b0SGarrett D'Amore  * incoming tm.  It is triggered by -D_STRPTIME_DONTZERO.
5414297a3b0SGarrett D'Amore  */
5424297a3b0SGarrett D'Amore char *
5434297a3b0SGarrett D'Amore __strptime_dontzero(const char *buf, const char *fmt, struct tm *tm)
5444297a3b0SGarrett D'Amore {
545*6eaad1d3SGarrett D'Amore 	int	flags = 0;
546*6eaad1d3SGarrett D'Amore 
547*6eaad1d3SGarrett D'Amore 	return (__strptime(buf, fmt, tm, &flags));
5484297a3b0SGarrett D'Amore }
549