1 /* $OpenBSD: strptime.c,v 1.11 2005/08/08 08:05:38 espie Exp $ */ 2 /* $NetBSD: strptime.c,v 1.12 1998/01/20 21:39:40 mycroft Exp $ */ 3 4 /*- 5 * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code was contributed to The NetBSD Foundation by Klaus Klein. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 //#include <sys/localedef.h> 40 #include <ctype.h> 41 #include <errno.h> 42 #include <locale.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <time.h> 46 #include "tzfile.h" 47 48 static const struct { 49 const char *abday[7]; 50 const char *day[7]; 51 const char *abmon[12]; 52 const char *mon[12]; 53 const char *am_pm[2]; 54 const char *d_t_fmt; 55 const char *d_fmt; 56 const char *t_fmt; 57 const char *t_fmt_ampm; 58 } _DefaultTimeLocale = { 59 { 60 "Sun","Mon","Tue","Wed","Thu","Fri","Sat", 61 }, 62 { 63 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", 64 "Friday", "Saturday" 65 }, 66 { 67 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 68 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 69 }, 70 { 71 "January", "February", "March", "April", "May", "June", "July", 72 "August", "September", "October", "November", "December" 73 }, 74 { 75 "AM", "PM" 76 }, 77 "%a %b %d %H:%M:%S %Y", 78 "%m/%d/%y", 79 "%H:%M:%S", 80 "%I:%M:%S %p" 81 }; 82 83 #define _ctloc(x) (_DefaultTimeLocale.x) 84 85 /* 86 * We do not implement alternate representations. However, we always 87 * check whether a given modifier is allowed for a certain conversion. 88 */ 89 #define _ALT_E 0x01 90 #define _ALT_O 0x02 91 #define _LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); } 92 93 94 struct century_relyear { 95 int century; 96 int relyear; 97 }; 98 static int _conv_num(const unsigned char **, int *, int, int); 99 static unsigned char *_strptime(const unsigned char *, const char *, struct tm *, 100 struct century_relyear *); 101 102 103 char * 104 strptime(const char *buf, const char *fmt, struct tm *tm) 105 { 106 struct century_relyear cr; 107 cr.century = TM_YEAR_BASE; 108 cr.relyear = -1; 109 return (char*)(_strptime((const unsigned char*)buf, fmt, tm, &cr)); 110 } 111 112 static unsigned char * 113 _strptime(const unsigned char *buf, const char *fmt, struct tm *tm, struct century_relyear *cr) 114 { 115 unsigned char c; 116 const unsigned char *bp; 117 size_t len = 0; 118 int alt_format, i; 119 120 bp = (unsigned char *)buf; 121 while ((c = *fmt) != '\0') { 122 /* Clear `alternate' modifier prior to new conversion. */ 123 alt_format = 0; 124 125 /* Eat up white-space. */ 126 if (isspace(c)) { 127 while (isspace(*bp)) 128 bp++; 129 130 fmt++; 131 continue; 132 } 133 134 if ((c = *fmt++) != '%') 135 goto literal; 136 137 138 again: switch (c = *fmt++) { 139 case '%': /* "%%" is converted to "%". */ 140 literal: 141 if (c != *bp++) 142 return (NULL); 143 144 break; 145 146 /* 147 * "Alternative" modifiers. Just set the appropriate flag 148 * and start over again. 149 */ 150 case 'E': /* "%E?" alternative conversion modifier. */ 151 _LEGAL_ALT(0); 152 alt_format |= _ALT_E; 153 goto again; 154 155 case 'O': /* "%O?" alternative conversion modifier. */ 156 _LEGAL_ALT(0); 157 alt_format |= _ALT_O; 158 goto again; 159 160 /* 161 * "Complex" conversion rules, implemented through recursion. 162 */ 163 case 'c': /* Date and time, using the locale's format. */ 164 _LEGAL_ALT(_ALT_E); 165 if (!(bp = _strptime(bp, _ctloc(d_t_fmt), tm, cr))) 166 return (NULL); 167 break; 168 169 case 'D': /* The date as "%m/%d/%y". */ 170 _LEGAL_ALT(0); 171 if (!(bp = _strptime(bp, "%m/%d/%y", tm, cr))) 172 return (NULL); 173 break; 174 175 case 'F': /* The date as "%Y-%m-%d". */ 176 _LEGAL_ALT(0); 177 if (!(bp = _strptime(bp, "%Y-%m-%d", tm, cr))) 178 return (NULL); 179 continue; 180 181 case 'R': /* The time as "%H:%M". */ 182 _LEGAL_ALT(0); 183 if (!(bp = _strptime(bp, "%H:%M", tm, cr))) 184 return (NULL); 185 break; 186 187 case 'r': /* The time as "%I:%M:%S %p". */ 188 _LEGAL_ALT(0); 189 if (!(bp = _strptime(bp, "%I:%M:%S %p", tm, cr))) 190 return (NULL); 191 break; 192 193 case 'T': /* The time as "%H:%M:%S". */ 194 _LEGAL_ALT(0); 195 if (!(bp = _strptime(bp, "%H:%M:%S", tm, cr))) 196 return (NULL); 197 break; 198 199 case 'v': /* The date as "%e-%b-%Y". */ 200 _LEGAL_ALT(0); 201 if (!(bp = _strptime(bp, "%e-%b-%Y", tm, cr))) 202 return (NULL); 203 break; 204 205 case 'X': /* The time, using the locale's format. */ 206 _LEGAL_ALT(_ALT_E); 207 if (!(bp = _strptime(bp, _ctloc(t_fmt), tm, cr))) 208 return (NULL); 209 break; 210 211 case 'x': /* The date, using the locale's format. */ 212 _LEGAL_ALT(_ALT_E); 213 if (!(bp = _strptime(bp, _ctloc(d_fmt), tm, cr))) 214 return (NULL); 215 break; 216 217 /* 218 * "Elementary" conversion rules. 219 */ 220 case 'A': /* The day of week, using the locale's form. */ 221 case 'a': 222 _LEGAL_ALT(0); 223 for (i = 0; i < 7; i++) { 224 /* Full name. */ 225 len = strlen(_ctloc(day[i])); 226 if (strncasecmp(_ctloc(day[i]), (const char*)bp, len) == 0) 227 break; 228 229 /* Abbreviated name. */ 230 len = strlen(_ctloc(abday[i])); 231 if (strncasecmp(_ctloc(abday[i]), (const char*)bp, len) == 0) 232 break; 233 } 234 235 /* Nothing matched. */ 236 if (i == 7) 237 return (NULL); 238 239 tm->tm_wday = i; 240 bp += len; 241 break; 242 243 case 'B': /* The month, using the locale's form. */ 244 case 'b': 245 case 'h': 246 _LEGAL_ALT(0); 247 for (i = 0; i < 12; i++) { 248 /* Full name. */ 249 len = strlen(_ctloc(mon[i])); 250 if (strncasecmp(_ctloc(mon[i]), (const char*)bp, len) == 0) 251 break; 252 253 /* Abbreviated name. */ 254 len = strlen(_ctloc(abmon[i])); 255 if (strncasecmp(_ctloc(abmon[i]), (const char*)bp, len) == 0) 256 break; 257 } 258 259 /* Nothing matched. */ 260 if (i == 12) 261 return (NULL); 262 263 tm->tm_mon = i; 264 bp += len; 265 break; 266 267 case 'C': /* The century number. */ 268 _LEGAL_ALT(_ALT_E); 269 if (!(_conv_num(&bp, &i, 0, 99))) 270 return (NULL); 271 272 cr->century = i * 100; 273 break; 274 275 case 'd': /* The day of month. */ 276 case 'e': 277 _LEGAL_ALT(_ALT_O); 278 if (!(_conv_num(&bp, &tm->tm_mday, 1, 31))) 279 return (NULL); 280 break; 281 282 case 'k': /* The hour (24-hour clock representation). */ 283 _LEGAL_ALT(0); 284 /* FALLTHROUGH */ 285 case 'H': 286 _LEGAL_ALT(_ALT_O); 287 if (!(_conv_num(&bp, &tm->tm_hour, 0, 23))) 288 return (NULL); 289 break; 290 291 case 'l': /* The hour (12-hour clock representation). */ 292 _LEGAL_ALT(0); 293 /* FALLTHROUGH */ 294 case 'I': 295 _LEGAL_ALT(_ALT_O); 296 if (!(_conv_num(&bp, &tm->tm_hour, 1, 12))) 297 return (NULL); 298 break; 299 300 case 'j': /* The day of year. */ 301 _LEGAL_ALT(0); 302 if (!(_conv_num(&bp, &tm->tm_yday, 1, 366))) 303 return (NULL); 304 tm->tm_yday--; 305 break; 306 307 case 'M': /* The minute. */ 308 _LEGAL_ALT(_ALT_O); 309 if (!(_conv_num(&bp, &tm->tm_min, 0, 59))) 310 return (NULL); 311 break; 312 313 case 'm': /* The month. */ 314 _LEGAL_ALT(_ALT_O); 315 if (!(_conv_num(&bp, &tm->tm_mon, 1, 12))) 316 return (NULL); 317 tm->tm_mon--; 318 break; 319 320 case 'P': 321 case 'p': /* The locale's equivalent of AM/PM. */ 322 _LEGAL_ALT(0); 323 /* AM? */ 324 len = strlen(_ctloc(am_pm[0])); 325 if (strncasecmp(_ctloc(am_pm[0]), (const char*)bp, len) == 0) { 326 if (tm->tm_hour > 12) /* i.e., 13:00 AM ?! */ 327 return (NULL); 328 else if (tm->tm_hour == 12) 329 tm->tm_hour = 0; 330 331 bp += len; 332 break; 333 } 334 /* PM? */ 335 len = strlen(_ctloc(am_pm[1])); 336 if (strncasecmp(_ctloc(am_pm[1]), (const char*)bp, len) == 0) { 337 if (tm->tm_hour > 12) /* i.e., 13:00 PM ?! */ 338 return (NULL); 339 else if (tm->tm_hour < 12) 340 tm->tm_hour += 12; 341 342 bp += len; 343 break; 344 } 345 346 /* Nothing matched. */ 347 return (NULL); 348 349 case 'S': /* The seconds. */ 350 _LEGAL_ALT(_ALT_O); 351 if (!(_conv_num(&bp, &tm->tm_sec, 0, 61))) 352 return (NULL); 353 break; 354 355 case 's': 356 { 357 // Android addition, based on FreeBSD's implementation. 358 int saved_errno = errno; 359 errno = 0; 360 const unsigned char* old_bp = bp; 361 long n = strtol((const char*) bp, (char**) &bp, 10); 362 time_t t = n; 363 if (bp == old_bp || errno == ERANGE || ((long) t) != n) { 364 errno = saved_errno; 365 return NULL; 366 } 367 errno = saved_errno; 368 369 if (localtime_r(&t, tm) == NULL) return NULL; 370 } 371 break; 372 373 374 case 'U': /* The week of year, beginning on sunday. */ 375 case 'W': /* The week of year, beginning on monday. */ 376 _LEGAL_ALT(_ALT_O); 377 /* 378 * XXX This is bogus, as we can not assume any valid 379 * information present in the tm structure at this 380 * point to calculate a real value, so just check the 381 * range for now. 382 */ 383 if (!(_conv_num(&bp, &i, 0, 53))) 384 return (NULL); 385 break; 386 387 case 'w': /* The day of week, beginning on sunday. */ 388 _LEGAL_ALT(_ALT_O); 389 if (!(_conv_num(&bp, &tm->tm_wday, 0, 6))) 390 return (NULL); 391 break; 392 393 case 'u': /* The day of week, monday = 1. */ 394 _LEGAL_ALT(_ALT_O); 395 if (!(_conv_num(&bp, &i, 1, 7))) 396 return (NULL); 397 tm->tm_wday = i % 7; 398 continue; 399 400 case 'g': /* The year corresponding to the ISO week 401 * number but without the century. 402 */ 403 if (!(_conv_num(&bp, &i, 0, 99))) 404 return (NULL); 405 continue; 406 407 case 'G': /* The year corresponding to the ISO week 408 * number with century. 409 */ 410 do 411 bp++; 412 while (isdigit(*bp)); 413 continue; 414 415 case 'V': /* The ISO 8601:1988 week number as decimal */ 416 if (!(_conv_num(&bp, &i, 0, 53))) 417 return (NULL); 418 continue; 419 420 case 'Y': /* The year. */ 421 _LEGAL_ALT(_ALT_E); 422 if (!(_conv_num(&bp, &i, 0, 9999))) 423 return (NULL); 424 425 cr->relyear = -1; 426 tm->tm_year = i - TM_YEAR_BASE; 427 break; 428 429 case 'y': /* The year within the century (2 digits). */ 430 _LEGAL_ALT(_ALT_E | _ALT_O); 431 if (!(_conv_num(&bp, &cr->relyear, 0, 99))) 432 return (NULL); 433 break; 434 435 /* 436 * Miscellaneous conversions. 437 */ 438 case 'n': /* Any kind of white-space. */ 439 case 't': 440 _LEGAL_ALT(0); 441 while (isspace(*bp)) 442 bp++; 443 break; 444 445 446 default: /* Unknown/unsupported conversion. */ 447 return (NULL); 448 } 449 450 451 } 452 453 /* 454 * We need to evaluate the two digit year spec (%y) 455 * last as we can get a century spec (%C) at any time. 456 */ 457 if (cr->relyear != -1) { 458 if (cr->century == TM_YEAR_BASE) { 459 if (cr->relyear <= 68) 460 tm->tm_year = cr->relyear + 2000 - TM_YEAR_BASE; 461 else 462 tm->tm_year = cr->relyear + 1900 - TM_YEAR_BASE; 463 } else { 464 tm->tm_year = cr->relyear + cr->century - TM_YEAR_BASE; 465 } 466 } 467 468 return (unsigned char*)bp; 469 } 470 471 472 static int 473 _conv_num(const unsigned char **buf, int *dest, int llim, int ulim) 474 { 475 int result = 0; 476 int rulim = ulim; 477 478 if (**buf < '0' || **buf > '9') 479 return (0); 480 481 /* we use rulim to break out of the loop when we run out of digits */ 482 do { 483 result *= 10; 484 result += *(*buf)++ - '0'; 485 rulim /= 10; 486 } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9'); 487 488 if (result < llim || result > ulim) 489 return (0); 490 491 *dest = result; 492 return (1); 493 } 494 495 char* strptime_l(const char* buf, const char* fmt, struct tm* tm, locale_t l) { 496 return strptime(buf, fmt, tm); 497 } 498