14297a3b0SGarrett D'Amore /*
2a11c1571SGary Mills * Copyright (c) 2014 Gary Mills
3*2d08521bSGarrett 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>
44*2d08521bSGarrett D'Amore #include <locale.h>
454297a3b0SGarrett D'Amore #include "timelocal.h"
46*2d08521bSGarrett 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)54*2d08521bSGarrett D'Amore __strptime(const char *_RESTRICT_KYWD buf, const char *_RESTRICT_KYWD fmt,
55*2d08521bSGarrett D'Amore struct tm *_RESTRICT_KYWD tm, int *_RESTRICT_KYWD flagsp,
56*2d08521bSGarrett 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;
62*2d08521bSGarrett 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 '+':
96*2d08521bSGarrett 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':
119*2d08521bSGarrett 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':
125*2d08521bSGarrett 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':
143*2d08521bSGarrett 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':
149*2d08521bSGarrett 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':
155*2d08521bSGarrett 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':
161*2d08521bSGarrett 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':
167*2d08521bSGarrett 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':
173*2d08521bSGarrett 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':
3334297a3b0SGarrett D'Amore /*
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++;
33925f48f67SGordon Ross /*
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':
4296eaad1d3SGarrett D'Amore if (*buf == NULL || 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;
5084297a3b0SGarrett D'Amore }
5094297a3b0SGarrett 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);
5156eaad1d3SGarrett D'Amore }
5166eaad1d3SGarrett 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)522*2d08521bSGarrett D'Amore strptime(const char *_RESTRICT_KYWD buf, const char *_RESTRICT_KYWD fmt,
523*2d08521bSGarrett D'Amore struct tm *_RESTRICT_KYWD tm)
5244297a3b0SGarrett D'Amore {
525*2d08521bSGarrett D'Amore int flags = 0;
5264297a3b0SGarrett D'Amore
527*2d08521bSGarrett D'Amore (void) memset(tm, 0, sizeof (*tm));
528*2d08521bSGarrett D'Amore
529*2d08521bSGarrett 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)537*2d08521bSGarrett D'Amore __strptime_dontzero(const char *_RESTRICT_KYWD buf,
538*2d08521bSGarrett D'Amore const char *_RESTRICT_KYWD fmt, struct tm *_RESTRICT_KYWD tm)
5394297a3b0SGarrett D'Amore {
5406eaad1d3SGarrett D'Amore int flags = 0;
5416eaad1d3SGarrett D'Amore
542*2d08521bSGarrett D'Amore return (__strptime(buf, fmt, tm, &flags, uselocale(NULL)));
543*2d08521bSGarrett D'Amore }
544*2d08521bSGarrett D'Amore
545*2d08521bSGarrett D'Amore /*
546*2d08521bSGarrett D'Amore * strptime_l is an extension that seems natural, and indeed, MacOS X
547*2d08521bSGarrett D'Amore * includes it within their <xlocale.h> and it is part of GNU libc as well.
548*2d08521bSGarrett D'Amore * For now we restrict it to the cases where strict namespaces are not
549*2d08521bSGarrett D'Amore * included. We expect to see it in a future version of POSIX. locale_t is
550*2d08521bSGarrett D'Amore * not a restrict, since the spec for it doesn't assume its a pointer. We
551*2d08521bSGarrett D'Amore * therefore pass it analagously to the way strftime_l is specified.
552*2d08521bSGarrett D'Amore *
553*2d08521bSGarrett D'Amore * We are not providing a non-zeroing version at this time.
554*2d08521bSGarrett D'Amore */
555*2d08521bSGarrett D'Amore char *
strptime_l(const char * _RESTRICT_KYWD buf,const char * _RESTRICT_KYWD fmt,struct tm * _RESTRICT_KYWD tm,locale_t loc)556*2d08521bSGarrett D'Amore strptime_l(const char *_RESTRICT_KYWD buf, const char *_RESTRICT_KYWD fmt,
557*2d08521bSGarrett D'Amore struct tm *_RESTRICT_KYWD tm, locale_t loc)
558*2d08521bSGarrett D'Amore {
559*2d08521bSGarrett D'Amore int flags = 0;
560*2d08521bSGarrett D'Amore
561*2d08521bSGarrett D'Amore (void) memset(tm, 0, sizeof (*tm));
562*2d08521bSGarrett D'Amore
563*2d08521bSGarrett D'Amore return (__strptime(buf, fmt, tm, &flags, loc));
5644297a3b0SGarrett D'Amore }
565