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 * 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 * 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 * 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 * 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