10413bfcaSPedro F. Giffuni /*- 2cd9dcb03SPedro F. Giffuni * Copyright (c) 2014 Gary Mills 3cd9dcb03SPedro F. Giffuni * Copyright 2011, Nexenta Systems, Inc. All rights reserved. 4d7641983SJoerg Wunsch * Copyright (c) 1994 Powerdog Industries. All rights reserved. 5d7641983SJoerg Wunsch * 63c87aa1dSDavid Chisnall * Copyright (c) 2011 The FreeBSD Foundation 73c87aa1dSDavid Chisnall * All rights reserved. 83c87aa1dSDavid Chisnall * Portions of this software were developed by David Chisnall 93c87aa1dSDavid Chisnall * under sponsorship from the FreeBSD Foundation. 103c87aa1dSDavid Chisnall * 1137486f03SJoerg Wunsch * Redistribution and use in source and binary forms, with or without 12d7641983SJoerg Wunsch * modification, are permitted provided that the following conditions 13d7641983SJoerg Wunsch * are met: 14d7641983SJoerg Wunsch * 1. Redistributions of source code must retain the above copyright 15d7641983SJoerg Wunsch * notice, this list of conditions and the following disclaimer. 16d7641983SJoerg Wunsch * 2. Redistributions in binary form must reproduce the above copyright 17d7641983SJoerg Wunsch * notice, this list of conditions and the following disclaimer 18d7641983SJoerg Wunsch * in the documentation and/or other materials provided with the 19d7641983SJoerg Wunsch * distribution. 20d7641983SJoerg Wunsch * 21d7641983SJoerg Wunsch * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY 22d7641983SJoerg Wunsch * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23d7641983SJoerg Wunsch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24d7641983SJoerg Wunsch * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE 25d7641983SJoerg Wunsch * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26d7641983SJoerg Wunsch * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27d7641983SJoerg Wunsch * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 28d7641983SJoerg Wunsch * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29d7641983SJoerg Wunsch * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 30d7641983SJoerg Wunsch * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 31d7641983SJoerg Wunsch * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 320413bfcaSPedro F. Giffuni * 330413bfcaSPedro F. Giffuni * The views and conclusions contained in the software and documentation 340413bfcaSPedro F. Giffuni * are those of the authors and should not be interpreted as representing 350413bfcaSPedro F. Giffuni * official policies, either expressed or implied, of Powerdog Industries. 36d7641983SJoerg Wunsch */ 37d7641983SJoerg Wunsch 38e0554a53SJacques Vidrine #include <sys/cdefs.h> 39d7641983SJoerg Wunsch #ifndef lint 4037486f03SJoerg Wunsch #ifndef NOID 41e0554a53SJacques Vidrine static char copyright[] __unused = 42d7641983SJoerg Wunsch "@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved."; 43e0554a53SJacques Vidrine static char sccsid[] __unused = "@(#)strptime.c 0.1 (Powerdog) 94/03/27"; 4437486f03SJoerg Wunsch #endif /* !defined NOID */ 45d7641983SJoerg Wunsch #endif /* not lint */ 46333fc21eSDavid E. O'Brien __FBSDID("$FreeBSD$"); 47d7641983SJoerg Wunsch 48d201fe46SDaniel Eischen #include "namespace.h" 49d7641983SJoerg Wunsch #include <time.h> 50d7641983SJoerg Wunsch #include <ctype.h> 51c91e947dSJacques Vidrine #include <errno.h> 52427fc5edSDima Dorfman #include <stdlib.h> 53d7641983SJoerg Wunsch #include <string.h> 54cb7a4779SDavid E. O'Brien #include <pthread.h> 55d201fe46SDaniel Eischen #include "un-namespace.h" 56d201fe46SDaniel Eischen #include "libc_private.h" 5737486f03SJoerg Wunsch #include "timelocal.h" 582e465587SPedro F. Giffuni #include "tzfile.h" 591a6804c6SPedro F. Giffuni 603c87aa1dSDavid Chisnall static char * _strptime(const char *, const char *, struct tm *, int *, locale_t); 61cb7a4779SDavid E. O'Brien 62d7641983SJoerg Wunsch #define asizeof(a) (sizeof(a) / sizeof((a)[0])) 63d7641983SJoerg Wunsch 642e465587SPedro F. Giffuni #define FLAG_NONE (1 << 0) 652e465587SPedro F. Giffuni #define FLAG_YEAR (1 << 1) 662e465587SPedro F. Giffuni #define FLAG_MONTH (1 << 2) 672e465587SPedro F. Giffuni #define FLAG_YDAY (1 << 3) 682e465587SPedro F. Giffuni #define FLAG_MDAY (1 << 4) 692e465587SPedro F. Giffuni #define FLAG_WDAY (1 << 5) 702e465587SPedro F. Giffuni 712e465587SPedro F. Giffuni /* 722e465587SPedro F. Giffuni * Calculate the week day of the first day of a year. Valid for 732e465587SPedro F. Giffuni * the Gregorian calendar, which began Sept 14, 1752 in the UK 742e465587SPedro F. Giffuni * and its colonies. Ref: 752e465587SPedro F. Giffuni * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week 762e465587SPedro F. Giffuni */ 772e465587SPedro F. Giffuni 782e465587SPedro F. Giffuni static int 792e465587SPedro F. Giffuni first_wday_of(int year) 802e465587SPedro F. Giffuni { 812e465587SPedro F. Giffuni return (((2 * (3 - (year / 100) % 4)) + (year % 100) + 822e465587SPedro F. Giffuni ((year % 100) / 4) + (isleap(year) ? 6 : 0) + 1) % 7); 832e465587SPedro F. Giffuni } 842e465587SPedro F. Giffuni 85cb7a4779SDavid E. O'Brien static char * 863c87aa1dSDavid Chisnall _strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp, 873c87aa1dSDavid Chisnall locale_t locale) 88d7641983SJoerg Wunsch { 8937486f03SJoerg Wunsch char c; 9037486f03SJoerg Wunsch const char *ptr; 912e465587SPedro F. Giffuni int day_offset = -1, wday_offset; 922e465587SPedro F. Giffuni int week_offset; 933624c752SPedro F. Giffuni int i, len; 942e465587SPedro F. Giffuni int flags; 951d6c9941SAndrey A. Chernov int Ealternative, Oalternative; 962e465587SPedro F. Giffuni const struct lc_time_T *tptr = __get_current_time_locale(locale); 972e465587SPedro F. Giffuni static int start_of_month[2][13] = { 982e465587SPedro F. Giffuni {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, 992e465587SPedro F. Giffuni {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} 1002e465587SPedro F. Giffuni }; 1012e465587SPedro F. Giffuni 1022e465587SPedro F. Giffuni flags = FLAG_NONE; 103d7641983SJoerg Wunsch 104d7641983SJoerg Wunsch ptr = fmt; 105d7641983SJoerg Wunsch while (*ptr != 0) { 106d7641983SJoerg Wunsch if (*buf == 0) 107d7641983SJoerg Wunsch break; 108d7641983SJoerg Wunsch 109d7641983SJoerg Wunsch c = *ptr++; 110d7641983SJoerg Wunsch 111d7641983SJoerg Wunsch if (c != '%') { 1123c87aa1dSDavid Chisnall if (isspace_l((unsigned char)c, locale)) 1133c87aa1dSDavid Chisnall while (*buf != 0 && 1143c87aa1dSDavid Chisnall isspace_l((unsigned char)*buf, locale)) 115d7641983SJoerg Wunsch buf++; 116d7641983SJoerg Wunsch else if (c != *buf++) 1173624c752SPedro F. Giffuni return (NULL); 118d7641983SJoerg Wunsch continue; 119d7641983SJoerg Wunsch } 120d7641983SJoerg Wunsch 1211d6c9941SAndrey A. Chernov Ealternative = 0; 1221d6c9941SAndrey A. Chernov Oalternative = 0; 1231d6c9941SAndrey A. Chernov label: 124d7641983SJoerg Wunsch c = *ptr++; 125d7641983SJoerg Wunsch switch (c) { 126d7641983SJoerg Wunsch case 0: 127d7641983SJoerg Wunsch case '%': 128d7641983SJoerg Wunsch if (*buf++ != '%') 1293624c752SPedro F. Giffuni return (NULL); 130d7641983SJoerg Wunsch break; 131d7641983SJoerg Wunsch 1321d6c9941SAndrey A. Chernov case '+': 1333c87aa1dSDavid Chisnall buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale); 1343624c752SPedro F. Giffuni if (buf == NULL) 1353624c752SPedro F. Giffuni return (NULL); 1362e465587SPedro F. Giffuni flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; 137d7641983SJoerg Wunsch break; 138d7641983SJoerg Wunsch 1391d6c9941SAndrey A. Chernov case 'C': 1403c87aa1dSDavid Chisnall if (!isdigit_l((unsigned char)*buf, locale)) 1413624c752SPedro F. Giffuni return (NULL); 1421d6c9941SAndrey A. Chernov 143398592ffSSheldon Hearn /* XXX This will break for 3-digit centuries. */ 144398592ffSSheldon Hearn len = 2; 1453c87aa1dSDavid Chisnall for (i = 0; len && *buf != 0 && 1463c87aa1dSDavid Chisnall isdigit_l((unsigned char)*buf, locale); buf++) { 1471d6c9941SAndrey A. Chernov i *= 10; 1481d6c9941SAndrey A. Chernov i += *buf - '0'; 149398592ffSSheldon Hearn len--; 1501d6c9941SAndrey A. Chernov } 1511d6c9941SAndrey A. Chernov if (i < 19) 1523624c752SPedro F. Giffuni return (NULL); 1531d6c9941SAndrey A. Chernov 1542e465587SPedro F. Giffuni tm->tm_year = i * 100 - TM_YEAR_BASE; 1552e465587SPedro F. Giffuni flags |= FLAG_YEAR; 1562e465587SPedro F. Giffuni 1571d6c9941SAndrey A. Chernov break; 1581d6c9941SAndrey A. Chernov 159d7641983SJoerg Wunsch case 'c': 1603c87aa1dSDavid Chisnall buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale); 1613624c752SPedro F. Giffuni if (buf == NULL) 1623624c752SPedro F. Giffuni return (NULL); 1632e465587SPedro F. Giffuni flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; 164d7641983SJoerg Wunsch break; 165d7641983SJoerg Wunsch 166d7641983SJoerg Wunsch case 'D': 1673c87aa1dSDavid Chisnall buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale); 1683624c752SPedro F. Giffuni if (buf == NULL) 1693624c752SPedro F. Giffuni return (NULL); 1702e465587SPedro F. Giffuni flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; 171d7641983SJoerg Wunsch break; 172d7641983SJoerg Wunsch 1731d6c9941SAndrey A. Chernov case 'E': 174c63a4303SAndrey A. Chernov if (Ealternative || Oalternative) 175c63a4303SAndrey A. Chernov break; 1761d6c9941SAndrey A. Chernov Ealternative++; 1771d6c9941SAndrey A. Chernov goto label; 1781d6c9941SAndrey A. Chernov 1791d6c9941SAndrey A. Chernov case 'O': 180c63a4303SAndrey A. Chernov if (Ealternative || Oalternative) 181c63a4303SAndrey A. Chernov break; 1821d6c9941SAndrey A. Chernov Oalternative++; 1831d6c9941SAndrey A. Chernov goto label; 1841d6c9941SAndrey A. Chernov 185c63a4303SAndrey A. Chernov case 'F': 1863c87aa1dSDavid Chisnall buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale); 1873624c752SPedro F. Giffuni if (buf == NULL) 1883624c752SPedro F. Giffuni return (NULL); 1892e465587SPedro F. Giffuni flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; 190c63a4303SAndrey A. Chernov break; 191c63a4303SAndrey A. Chernov 192d7641983SJoerg Wunsch case 'R': 1933c87aa1dSDavid Chisnall buf = _strptime(buf, "%H:%M", tm, GMTp, locale); 1943624c752SPedro F. Giffuni if (buf == NULL) 1953624c752SPedro F. Giffuni return (NULL); 196d7641983SJoerg Wunsch break; 197d7641983SJoerg Wunsch 198d7641983SJoerg Wunsch case 'r': 1993c87aa1dSDavid Chisnall buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale); 2003624c752SPedro F. Giffuni if (buf == NULL) 2013624c752SPedro F. Giffuni return (NULL); 202d7641983SJoerg Wunsch break; 203d7641983SJoerg Wunsch 204d7641983SJoerg Wunsch case 'T': 2053c87aa1dSDavid Chisnall buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale); 2063624c752SPedro F. Giffuni if (buf == NULL) 2073624c752SPedro F. Giffuni return (NULL); 208d7641983SJoerg Wunsch break; 209d7641983SJoerg Wunsch 210d7641983SJoerg Wunsch case 'X': 2113c87aa1dSDavid Chisnall buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale); 2123624c752SPedro F. Giffuni if (buf == NULL) 2133624c752SPedro F. Giffuni return (NULL); 214d7641983SJoerg Wunsch break; 215d7641983SJoerg Wunsch 216d7641983SJoerg Wunsch case 'x': 2173c87aa1dSDavid Chisnall buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale); 2183624c752SPedro F. Giffuni if (buf == NULL) 2193624c752SPedro F. Giffuni return (NULL); 2202e465587SPedro F. Giffuni flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; 221d7641983SJoerg Wunsch break; 222d7641983SJoerg Wunsch 223d7641983SJoerg Wunsch case 'j': 2243c87aa1dSDavid Chisnall if (!isdigit_l((unsigned char)*buf, locale)) 2253624c752SPedro F. Giffuni return (NULL); 226d7641983SJoerg Wunsch 227398592ffSSheldon Hearn len = 3; 2283c87aa1dSDavid Chisnall for (i = 0; len && *buf != 0 && 2293c87aa1dSDavid Chisnall isdigit_l((unsigned char)*buf, locale); buf++){ 230d7641983SJoerg Wunsch i *= 10; 231d7641983SJoerg Wunsch i += *buf - '0'; 232398592ffSSheldon Hearn len--; 233d7641983SJoerg Wunsch } 23433dbb0a6SSheldon Hearn if (i < 1 || i > 366) 2353624c752SPedro F. Giffuni return (NULL); 236d7641983SJoerg Wunsch 23733dbb0a6SSheldon Hearn tm->tm_yday = i - 1; 2382e465587SPedro F. Giffuni flags |= FLAG_YDAY; 2392e465587SPedro F. Giffuni 240d7641983SJoerg Wunsch break; 241d7641983SJoerg Wunsch 242d7641983SJoerg Wunsch case 'M': 243d7641983SJoerg Wunsch case 'S': 2443c87aa1dSDavid Chisnall if (*buf == 0 || 2453c87aa1dSDavid Chisnall isspace_l((unsigned char)*buf, locale)) 246d7641983SJoerg Wunsch break; 247d7641983SJoerg Wunsch 2483c87aa1dSDavid Chisnall if (!isdigit_l((unsigned char)*buf, locale)) 2493624c752SPedro F. Giffuni return (NULL); 250d7641983SJoerg Wunsch 251398592ffSSheldon Hearn len = 2; 2523c87aa1dSDavid Chisnall for (i = 0; len && *buf != 0 && 2533c87aa1dSDavid Chisnall isdigit_l((unsigned char)*buf, locale); buf++){ 254d7641983SJoerg Wunsch i *= 10; 255d7641983SJoerg Wunsch i += *buf - '0'; 256398592ffSSheldon Hearn len--; 257d7641983SJoerg Wunsch } 25833dbb0a6SSheldon Hearn 25933dbb0a6SSheldon Hearn if (c == 'M') { 260d7641983SJoerg Wunsch if (i > 59) 2613624c752SPedro F. Giffuni return (NULL); 262d7641983SJoerg Wunsch tm->tm_min = i; 26333dbb0a6SSheldon Hearn } else { 26433dbb0a6SSheldon Hearn if (i > 60) 2653624c752SPedro F. Giffuni return (NULL); 266d7641983SJoerg Wunsch tm->tm_sec = i; 26733dbb0a6SSheldon Hearn } 268d7641983SJoerg Wunsch 269d7641983SJoerg Wunsch break; 270d7641983SJoerg Wunsch 271d7641983SJoerg Wunsch case 'H': 272d7641983SJoerg Wunsch case 'I': 273d7641983SJoerg Wunsch case 'k': 274d7641983SJoerg Wunsch case 'l': 275398592ffSSheldon Hearn /* 276398592ffSSheldon Hearn * Of these, %l is the only specifier explicitly 277398592ffSSheldon Hearn * documented as not being zero-padded. However, 278398592ffSSheldon Hearn * there is no harm in allowing zero-padding. 279398592ffSSheldon Hearn * 280398592ffSSheldon Hearn * XXX The %l specifier may gobble one too many 281398592ffSSheldon Hearn * digits if used incorrectly. 282398592ffSSheldon Hearn */ 2833c87aa1dSDavid Chisnall if (!isdigit_l((unsigned char)*buf, locale)) 2843624c752SPedro F. Giffuni return (NULL); 285d7641983SJoerg Wunsch 286398592ffSSheldon Hearn len = 2; 2873c87aa1dSDavid Chisnall for (i = 0; len && *buf != 0 && 2883c87aa1dSDavid Chisnall isdigit_l((unsigned char)*buf, locale); buf++) { 289d7641983SJoerg Wunsch i *= 10; 290d7641983SJoerg Wunsch i += *buf - '0'; 291398592ffSSheldon Hearn len--; 292d7641983SJoerg Wunsch } 293d7641983SJoerg Wunsch if (c == 'H' || c == 'k') { 294d7641983SJoerg Wunsch if (i > 23) 2953624c752SPedro F. Giffuni return (NULL); 296882f32c1SSheldon Hearn } else if (i > 12) 2973624c752SPedro F. Giffuni return (NULL); 298d7641983SJoerg Wunsch 299d7641983SJoerg Wunsch tm->tm_hour = i; 300d7641983SJoerg Wunsch 301d7641983SJoerg Wunsch break; 302d7641983SJoerg Wunsch 303d7641983SJoerg Wunsch case 'p': 304398592ffSSheldon Hearn /* 305398592ffSSheldon Hearn * XXX This is bogus if parsed before hour-related 306398592ffSSheldon Hearn * specifiers. 307398592ffSSheldon Hearn */ 308930cd711SAlexey Zelkin len = strlen(tptr->am); 3093c87aa1dSDavid Chisnall if (strncasecmp_l(buf, tptr->am, len, locale) == 0) { 310d7641983SJoerg Wunsch if (tm->tm_hour > 12) 3113624c752SPedro F. Giffuni return (NULL); 312d7641983SJoerg Wunsch if (tm->tm_hour == 12) 313d7641983SJoerg Wunsch tm->tm_hour = 0; 314d7641983SJoerg Wunsch buf += len; 315d7641983SJoerg Wunsch break; 316d7641983SJoerg Wunsch } 317d7641983SJoerg Wunsch 318930cd711SAlexey Zelkin len = strlen(tptr->pm); 3193c87aa1dSDavid Chisnall if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) { 320d7641983SJoerg Wunsch if (tm->tm_hour > 12) 3213624c752SPedro F. Giffuni return (NULL); 322d7641983SJoerg Wunsch if (tm->tm_hour != 12) 323d7641983SJoerg Wunsch tm->tm_hour += 12; 324d7641983SJoerg Wunsch buf += len; 325d7641983SJoerg Wunsch break; 326d7641983SJoerg Wunsch } 327d7641983SJoerg Wunsch 3283624c752SPedro F. Giffuni return (NULL); 329d7641983SJoerg Wunsch 330d7641983SJoerg Wunsch case 'A': 331d7641983SJoerg Wunsch case 'a': 332930cd711SAlexey Zelkin for (i = 0; i < asizeof(tptr->weekday); i++) { 333930cd711SAlexey Zelkin len = strlen(tptr->weekday[i]); 3343c87aa1dSDavid Chisnall if (strncasecmp_l(buf, tptr->weekday[i], 3353c87aa1dSDavid Chisnall len, locale) == 0) 336d7641983SJoerg Wunsch break; 337930cd711SAlexey Zelkin len = strlen(tptr->wday[i]); 3383c87aa1dSDavid Chisnall if (strncasecmp_l(buf, tptr->wday[i], 3393c87aa1dSDavid Chisnall len, locale) == 0) 340d7641983SJoerg Wunsch break; 341d7641983SJoerg Wunsch } 342930cd711SAlexey Zelkin if (i == asizeof(tptr->weekday)) 3433624c752SPedro F. Giffuni return (NULL); 344d7641983SJoerg Wunsch 3451a6804c6SPedro F. Giffuni buf += len; 346d7641983SJoerg Wunsch tm->tm_wday = i; 3472e465587SPedro F. Giffuni flags |= FLAG_WDAY; 348d7641983SJoerg Wunsch break; 349d7641983SJoerg Wunsch 35033dbb0a6SSheldon Hearn case 'U': 35133dbb0a6SSheldon Hearn case 'W': 35233dbb0a6SSheldon Hearn /* 35333dbb0a6SSheldon Hearn * XXX This is bogus, as we can not assume any valid 35433dbb0a6SSheldon Hearn * information present in the tm structure at this 35533dbb0a6SSheldon Hearn * point to calculate a real value, so just check the 35633dbb0a6SSheldon Hearn * range for now. 35733dbb0a6SSheldon Hearn */ 3583c87aa1dSDavid Chisnall if (!isdigit_l((unsigned char)*buf, locale)) 3593624c752SPedro F. Giffuni return (NULL); 36033dbb0a6SSheldon Hearn 361398592ffSSheldon Hearn len = 2; 3623c87aa1dSDavid Chisnall for (i = 0; len && *buf != 0 && 3633c87aa1dSDavid Chisnall isdigit_l((unsigned char)*buf, locale); buf++) { 36433dbb0a6SSheldon Hearn i *= 10; 36533dbb0a6SSheldon Hearn i += *buf - '0'; 366398592ffSSheldon Hearn len--; 36733dbb0a6SSheldon Hearn } 36833dbb0a6SSheldon Hearn if (i > 53) 3693624c752SPedro F. Giffuni return (NULL); 37033dbb0a6SSheldon Hearn 3712e465587SPedro F. Giffuni if (c == 'U') 3722e465587SPedro F. Giffuni day_offset = TM_SUNDAY; 3732e465587SPedro F. Giffuni else 3742e465587SPedro F. Giffuni day_offset = TM_MONDAY; 3752e465587SPedro F. Giffuni 3762e465587SPedro F. Giffuni 3772e465587SPedro F. Giffuni week_offset = i; 3782e465587SPedro F. Giffuni 37933dbb0a6SSheldon Hearn break; 38033dbb0a6SSheldon Hearn 38133dbb0a6SSheldon Hearn case 'w': 3823c87aa1dSDavid Chisnall if (!isdigit_l((unsigned char)*buf, locale)) 3833624c752SPedro F. Giffuni return (NULL); 38433dbb0a6SSheldon Hearn 385398592ffSSheldon Hearn i = *buf - '0'; 38633dbb0a6SSheldon Hearn if (i > 6) 3873624c752SPedro F. Giffuni return (NULL); 38833dbb0a6SSheldon Hearn 38933dbb0a6SSheldon Hearn tm->tm_wday = i; 3902e465587SPedro F. Giffuni flags |= FLAG_WDAY; 39133dbb0a6SSheldon Hearn 39233dbb0a6SSheldon Hearn break; 39333dbb0a6SSheldon Hearn 394d7641983SJoerg Wunsch case 'e': 395398592ffSSheldon Hearn /* 396cd9dcb03SPedro F. Giffuni * With %e format, our strftime(3) adds a blank space 397cd9dcb03SPedro F. Giffuni * before single digits. 398cd9dcb03SPedro F. Giffuni */ 399cd9dcb03SPedro F. Giffuni if (*buf != 0 && 400cd9dcb03SPedro F. Giffuni isspace_l((unsigned char)*buf, locale)) 401cd9dcb03SPedro F. Giffuni buf++; 402cd9dcb03SPedro F. Giffuni /* FALLTHROUGH */ 403cd9dcb03SPedro F. Giffuni case 'd': 404cd9dcb03SPedro F. Giffuni /* 405cd9dcb03SPedro F. Giffuni * The %e specifier was once explicitly documented as 406cd9dcb03SPedro F. Giffuni * not being zero-padded but was later changed to 407cd9dcb03SPedro F. Giffuni * equivalent to %d. There is no harm in allowing 408398592ffSSheldon Hearn * such padding. 409398592ffSSheldon Hearn * 410398592ffSSheldon Hearn * XXX The %e specifier may gobble one too many 411398592ffSSheldon Hearn * digits if used incorrectly. 412398592ffSSheldon Hearn */ 4133c87aa1dSDavid Chisnall if (!isdigit_l((unsigned char)*buf, locale)) 4143624c752SPedro F. Giffuni return (NULL); 415d7641983SJoerg Wunsch 416398592ffSSheldon Hearn len = 2; 4173c87aa1dSDavid Chisnall for (i = 0; len && *buf != 0 && 4183c87aa1dSDavid Chisnall isdigit_l((unsigned char)*buf, locale); buf++) { 419d7641983SJoerg Wunsch i *= 10; 420d7641983SJoerg Wunsch i += *buf - '0'; 421398592ffSSheldon Hearn len--; 422d7641983SJoerg Wunsch } 423d7641983SJoerg Wunsch if (i > 31) 4243624c752SPedro F. Giffuni return (NULL); 425d7641983SJoerg Wunsch 426d7641983SJoerg Wunsch tm->tm_mday = i; 4272e465587SPedro F. Giffuni flags |= FLAG_MDAY; 428d7641983SJoerg Wunsch 429d7641983SJoerg Wunsch break; 430d7641983SJoerg Wunsch 431d7641983SJoerg Wunsch case 'B': 432d7641983SJoerg Wunsch case 'b': 433d7641983SJoerg Wunsch case 'h': 434930cd711SAlexey Zelkin for (i = 0; i < asizeof(tptr->month); i++) { 4351d6c9941SAndrey A. Chernov if (Oalternative) { 4361d6c9941SAndrey A. Chernov if (c == 'B') { 437930cd711SAlexey Zelkin len = strlen(tptr->alt_month[i]); 4383c87aa1dSDavid Chisnall if (strncasecmp_l(buf, 439930cd711SAlexey Zelkin tptr->alt_month[i], 4403c87aa1dSDavid Chisnall len, locale) == 0) 4411d6c9941SAndrey A. Chernov break; 4421d6c9941SAndrey A. Chernov } 4431d6c9941SAndrey A. Chernov } else { 444930cd711SAlexey Zelkin len = strlen(tptr->month[i]); 4453c87aa1dSDavid Chisnall if (strncasecmp_l(buf, tptr->month[i], 4463c87aa1dSDavid Chisnall len, locale) == 0) 447d7641983SJoerg Wunsch break; 4483d74e220SEdwin Groothuis } 4493d74e220SEdwin Groothuis } 4503d74e220SEdwin Groothuis /* 4513d74e220SEdwin Groothuis * Try the abbreviated month name if the full name 4523d74e220SEdwin Groothuis * wasn't found and Oalternative was not requested. 4533d74e220SEdwin Groothuis */ 4543d74e220SEdwin Groothuis if (i == asizeof(tptr->month) && !Oalternative) { 4553d74e220SEdwin Groothuis for (i = 0; i < asizeof(tptr->month); i++) { 456930cd711SAlexey Zelkin len = strlen(tptr->mon[i]); 4573c87aa1dSDavid Chisnall if (strncasecmp_l(buf, tptr->mon[i], 4583c87aa1dSDavid Chisnall len, locale) == 0) 459d7641983SJoerg Wunsch break; 460d7641983SJoerg Wunsch } 4611d6c9941SAndrey A. Chernov } 462930cd711SAlexey Zelkin if (i == asizeof(tptr->month)) 4633624c752SPedro F. Giffuni return (NULL); 464d7641983SJoerg Wunsch 465d7641983SJoerg Wunsch tm->tm_mon = i; 466d7641983SJoerg Wunsch buf += len; 4672e465587SPedro F. Giffuni flags |= FLAG_MONTH; 4682e465587SPedro F. Giffuni 469d7641983SJoerg Wunsch break; 470d7641983SJoerg Wunsch 471d7641983SJoerg Wunsch case 'm': 4723c87aa1dSDavid Chisnall if (!isdigit_l((unsigned char)*buf, locale)) 4733624c752SPedro F. Giffuni return (NULL); 474d7641983SJoerg Wunsch 475398592ffSSheldon Hearn len = 2; 4763c87aa1dSDavid Chisnall for (i = 0; len && *buf != 0 && 4773c87aa1dSDavid Chisnall isdigit_l((unsigned char)*buf, locale); buf++) { 478d7641983SJoerg Wunsch i *= 10; 479d7641983SJoerg Wunsch i += *buf - '0'; 480398592ffSSheldon Hearn len--; 481d7641983SJoerg Wunsch } 482d7641983SJoerg Wunsch if (i < 1 || i > 12) 4833624c752SPedro F. Giffuni return (NULL); 484d7641983SJoerg Wunsch 485d7641983SJoerg Wunsch tm->tm_mon = i - 1; 4862e465587SPedro F. Giffuni flags |= FLAG_MONTH; 487d7641983SJoerg Wunsch 488d7641983SJoerg Wunsch break; 489d7641983SJoerg Wunsch 490427fc5edSDima Dorfman case 's': 491427fc5edSDima Dorfman { 492427fc5edSDima Dorfman char *cp; 493c91e947dSJacques Vidrine int sverrno; 494c91e947dSJacques Vidrine long n; 495427fc5edSDima Dorfman time_t t; 496427fc5edSDima Dorfman 497c91e947dSJacques Vidrine sverrno = errno; 498c91e947dSJacques Vidrine errno = 0; 4993c87aa1dSDavid Chisnall n = strtol_l(buf, &cp, 10, locale); 500c91e947dSJacques Vidrine if (errno == ERANGE || (long)(t = n) != n) { 501c91e947dSJacques Vidrine errno = sverrno; 5023624c752SPedro F. Giffuni return (NULL); 503c91e947dSJacques Vidrine } 504c91e947dSJacques Vidrine errno = sverrno; 505427fc5edSDima Dorfman buf = cp; 506eff6f444SPedro F. Giffuni if (gmtime_r(&t, tm) == NULL) 507eff6f444SPedro F. Giffuni return (NULL); 508fe71e0b8SMike Makonnen *GMTp = 1; 509eff6f444SPedro F. Giffuni flags |= FLAG_YDAY | FLAG_WDAY | FLAG_MONTH | 510eff6f444SPedro F. Giffuni FLAG_MDAY | FLAG_YEAR; 511427fc5edSDima Dorfman } 512427fc5edSDima Dorfman break; 513427fc5edSDima Dorfman 514d7641983SJoerg Wunsch case 'Y': 515d7641983SJoerg Wunsch case 'y': 5163c87aa1dSDavid Chisnall if (*buf == 0 || 5173c87aa1dSDavid Chisnall isspace_l((unsigned char)*buf, locale)) 518d7641983SJoerg Wunsch break; 519d7641983SJoerg Wunsch 5203c87aa1dSDavid Chisnall if (!isdigit_l((unsigned char)*buf, locale)) 5213624c752SPedro F. Giffuni return (NULL); 522d7641983SJoerg Wunsch 523398592ffSSheldon Hearn len = (c == 'Y') ? 4 : 2; 5243c87aa1dSDavid Chisnall for (i = 0; len && *buf != 0 && 5253c87aa1dSDavid Chisnall isdigit_l((unsigned char)*buf, locale); buf++) { 526d7641983SJoerg Wunsch i *= 10; 527d7641983SJoerg Wunsch i += *buf - '0'; 528398592ffSSheldon Hearn len--; 529d7641983SJoerg Wunsch } 530d7641983SJoerg Wunsch if (c == 'Y') 5312e465587SPedro F. Giffuni i -= TM_YEAR_BASE; 532aba0410bSWes Peters if (c == 'y' && i < 69) 533a00b1d8fSWes Peters i += 100; 534d7641983SJoerg Wunsch if (i < 0) 5353624c752SPedro F. Giffuni return (NULL); 536d7641983SJoerg Wunsch 537d7641983SJoerg Wunsch tm->tm_year = i; 5382e465587SPedro F. Giffuni flags |= FLAG_YEAR; 539d7641983SJoerg Wunsch 540d7641983SJoerg Wunsch break; 541b47f20dfSDavid E. O'Brien 542b47f20dfSDavid E. O'Brien case 'Z': 543b47f20dfSDavid E. O'Brien { 544b47f20dfSDavid E. O'Brien const char *cp; 545b47f20dfSDavid E. O'Brien char *zonestr; 546b47f20dfSDavid E. O'Brien 5473c87aa1dSDavid Chisnall for (cp = buf; *cp && 5483c87aa1dSDavid Chisnall isupper_l((unsigned char)*cp, locale); ++cp) { 5493c87aa1dSDavid Chisnall /*empty*/} 550b47f20dfSDavid E. O'Brien if (cp - buf) { 551b47f20dfSDavid E. O'Brien zonestr = alloca(cp - buf + 1); 552b47f20dfSDavid E. O'Brien strncpy(zonestr, buf, cp - buf); 553b47f20dfSDavid E. O'Brien zonestr[cp - buf] = '\0'; 554b47f20dfSDavid E. O'Brien tzset(); 555420bc6ceSAndrey A. Chernov if (0 == strcmp(zonestr, "GMT") || 556420bc6ceSAndrey A. Chernov 0 == strcmp(zonestr, "UTC")) { 557fe71e0b8SMike Makonnen *GMTp = 1; 558b47f20dfSDavid E. O'Brien } else if (0 == strcmp(zonestr, tzname[0])) { 559b47f20dfSDavid E. O'Brien tm->tm_isdst = 0; 560b47f20dfSDavid E. O'Brien } else if (0 == strcmp(zonestr, tzname[1])) { 561b47f20dfSDavid E. O'Brien tm->tm_isdst = 1; 562b47f20dfSDavid E. O'Brien } else { 5633624c752SPedro F. Giffuni return (NULL); 564b47f20dfSDavid E. O'Brien } 565b47f20dfSDavid E. O'Brien buf += cp - buf; 566b47f20dfSDavid E. O'Brien } 567b47f20dfSDavid E. O'Brien } 568b47f20dfSDavid E. O'Brien break; 56940523da7SXin LI 57040523da7SXin LI case 'z': 57140523da7SXin LI { 57240523da7SXin LI int sign = 1; 57340523da7SXin LI 57440523da7SXin LI if (*buf != '+') { 57540523da7SXin LI if (*buf == '-') 57640523da7SXin LI sign = -1; 57740523da7SXin LI else 5783624c752SPedro F. Giffuni return (NULL); 57940523da7SXin LI } 58040523da7SXin LI 58140523da7SXin LI buf++; 58240523da7SXin LI i = 0; 58340523da7SXin LI for (len = 4; len > 0; len--) { 5843c87aa1dSDavid Chisnall if (isdigit_l((unsigned char)*buf, locale)) { 58540523da7SXin LI i *= 10; 58640523da7SXin LI i += *buf - '0'; 58740523da7SXin LI buf++; 58840523da7SXin LI } else 5893624c752SPedro F. Giffuni return (NULL); 59040523da7SXin LI } 59140523da7SXin LI 59240523da7SXin LI tm->tm_hour -= sign * (i / 100); 59340523da7SXin LI tm->tm_min -= sign * (i % 100); 59440523da7SXin LI *GMTp = 1; 59540523da7SXin LI } 59640523da7SXin LI break; 597cd9dcb03SPedro F. Giffuni 598cd9dcb03SPedro F. Giffuni case 'n': 599cd9dcb03SPedro F. Giffuni case 't': 600cd9dcb03SPedro F. Giffuni while (isspace_l((unsigned char)*buf, locale)) 601cd9dcb03SPedro F. Giffuni buf++; 602cd9dcb03SPedro F. Giffuni break; 603d7641983SJoerg Wunsch } 604d7641983SJoerg Wunsch } 6052e465587SPedro F. Giffuni 6062e465587SPedro F. Giffuni if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) { 6072e465587SPedro F. Giffuni if ((flags & (FLAG_MONTH | FLAG_MDAY)) == 6082e465587SPedro F. Giffuni (FLAG_MONTH | FLAG_MDAY)) { 6092e465587SPedro F. Giffuni tm->tm_yday = start_of_month[isleap(tm->tm_year + 6102e465587SPedro F. Giffuni TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1); 6112e465587SPedro F. Giffuni flags |= FLAG_YDAY; 6122e465587SPedro F. Giffuni } else if (day_offset != -1) { 6132e465587SPedro F. Giffuni /* Set the date to the first Sunday (or Monday) 6142e465587SPedro F. Giffuni * of the specified week of the year. 6152e465587SPedro F. Giffuni */ 6162e465587SPedro F. Giffuni if (!(flags & FLAG_WDAY)) { 6172e465587SPedro F. Giffuni tm->tm_wday = day_offset; 6182e465587SPedro F. Giffuni flags |= FLAG_WDAY; 6192e465587SPedro F. Giffuni } 6202e465587SPedro F. Giffuni tm->tm_yday = (7 - 6212e465587SPedro F. Giffuni first_wday_of(tm->tm_year + TM_YEAR_BASE) + 6222e465587SPedro F. Giffuni day_offset) % 7 + (week_offset - 1) * 7 + 6232e465587SPedro F. Giffuni tm->tm_wday - day_offset; 6242e465587SPedro F. Giffuni flags |= FLAG_YDAY; 6252e465587SPedro F. Giffuni } 626ce1d331eSPedro F. Giffuni } 627cb7a4779SDavid E. O'Brien 6282e465587SPedro F. Giffuni if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) { 6292e465587SPedro F. Giffuni if (!(flags & FLAG_MONTH)) { 6302e465587SPedro F. Giffuni i = 0; 6312e465587SPedro F. Giffuni while (tm->tm_yday >= 6322e465587SPedro F. Giffuni start_of_month[isleap(tm->tm_year + 6332e465587SPedro F. Giffuni TM_YEAR_BASE)][i]) 6342e465587SPedro F. Giffuni i++; 6352e465587SPedro F. Giffuni if (i > 12) { 6362e465587SPedro F. Giffuni i = 1; 6372e465587SPedro F. Giffuni tm->tm_yday -= 6382e465587SPedro F. Giffuni start_of_month[isleap(tm->tm_year + 6392e465587SPedro F. Giffuni TM_YEAR_BASE)][12]; 6402e465587SPedro F. Giffuni tm->tm_year++; 6412e465587SPedro F. Giffuni } 6422e465587SPedro F. Giffuni tm->tm_mon = i - 1; 6432e465587SPedro F. Giffuni flags |= FLAG_MONTH; 6442e465587SPedro F. Giffuni } 6452e465587SPedro F. Giffuni if (!(flags & FLAG_MDAY)) { 6462e465587SPedro F. Giffuni tm->tm_mday = tm->tm_yday - 6472e465587SPedro F. Giffuni start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)] 6482e465587SPedro F. Giffuni [tm->tm_mon] + 1; 6492e465587SPedro F. Giffuni flags |= FLAG_MDAY; 6502e465587SPedro F. Giffuni } 6512e465587SPedro F. Giffuni if (!(flags & FLAG_WDAY)) { 6522e465587SPedro F. Giffuni i = 0; 6532e465587SPedro F. Giffuni wday_offset = first_wday_of(tm->tm_year); 6542e465587SPedro F. Giffuni while (i++ <= tm->tm_yday) { 6552e465587SPedro F. Giffuni if (wday_offset++ >= 6) 6562e465587SPedro F. Giffuni wday_offset = 0; 6572e465587SPedro F. Giffuni } 6582e465587SPedro F. Giffuni tm->tm_wday = wday_offset; 6592e465587SPedro F. Giffuni flags |= FLAG_WDAY; 6602e465587SPedro F. Giffuni } 6612e465587SPedro F. Giffuni } 6622e465587SPedro F. Giffuni 6632e465587SPedro F. Giffuni return ((char *)buf); 6642e465587SPedro F. Giffuni } 665c1cd488cSPedro F. Giffuni 666cb7a4779SDavid E. O'Brien char * 6673c87aa1dSDavid Chisnall strptime_l(const char * __restrict buf, const char * __restrict fmt, 6683c87aa1dSDavid Chisnall struct tm * __restrict tm, locale_t loc) 669cb7a4779SDavid E. O'Brien { 670cb7a4779SDavid E. O'Brien char *ret; 671fe71e0b8SMike Makonnen int gmt; 6723c87aa1dSDavid Chisnall FIX_LOCALE(loc); 673cb7a4779SDavid E. O'Brien 674fe71e0b8SMike Makonnen gmt = 0; 6753c87aa1dSDavid Chisnall ret = _strptime(buf, fmt, tm, &gmt, loc); 6766c688436SMike Makonnen if (ret && gmt) { 6776c688436SMike Makonnen time_t t = timegm(tm); 678420bc6ceSAndrey A. Chernov 679420bc6ceSAndrey A. Chernov if (t == -1) 680420bc6ceSAndrey A. Chernov return (NULL); 681b47f20dfSDavid E. O'Brien localtime_r(&t, tm); 682b47f20dfSDavid E. O'Brien } 683cb7a4779SDavid E. O'Brien 684fe71e0b8SMike Makonnen return (ret); 685d7641983SJoerg Wunsch } 6862e465587SPedro F. Giffuni 6873c87aa1dSDavid Chisnall char * 6883c87aa1dSDavid Chisnall strptime(const char * __restrict buf, const char * __restrict fmt, 6893c87aa1dSDavid Chisnall struct tm * __restrict tm) 6903c87aa1dSDavid Chisnall { 6913c87aa1dSDavid Chisnall return strptime_l(buf, fmt, tm, __get_locale()); 6923c87aa1dSDavid Chisnall } 693