1 /* $OpenBSD: strptime.c,v 1.12 2008/06/26 05:42:05 ray 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/localedef.h> 33 #include <ctype.h> 34 #include <locale.h> 35 #include <string.h> 36 #include <time.h> 37 #include <tzfile.h> 38 39 #define _ctloc(x) (_CurrentTimeLocale->x) 40 41 /* 42 * We do not implement alternate representations. However, we always 43 * check whether a given modifier is allowed for a certain conversion. 44 */ 45 #define _ALT_E 0x01 46 #define _ALT_O 0x02 47 #define _LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); } 48 49 50 static int _conv_num(const unsigned char **, int *, int, int); 51 static char *_strptime(const char *, const char *, struct tm *, int); 52 53 54 char * 55 strptime(const char *buf, const char *fmt, struct tm *tm) 56 { 57 return(_strptime(buf, fmt, tm, 1)); 58 } 59 60 static char * 61 _strptime(const char *buf, const char *fmt, struct tm *tm, int initialize) 62 { 63 unsigned char c; 64 const unsigned char *bp; 65 size_t len; 66 int alt_format, i; 67 static int century, relyear; 68 69 if (initialize) { 70 century = TM_YEAR_BASE; 71 relyear = -1; 72 } 73 74 bp = (unsigned char *)buf; 75 while ((c = *fmt) != '\0') { 76 /* Clear `alternate' modifier prior to new conversion. */ 77 alt_format = 0; 78 79 /* Eat up white-space. */ 80 if (isspace(c)) { 81 while (isspace(*bp)) 82 bp++; 83 84 fmt++; 85 continue; 86 } 87 88 if ((c = *fmt++) != '%') 89 goto literal; 90 91 92 again: switch (c = *fmt++) { 93 case '%': /* "%%" is converted to "%". */ 94 literal: 95 if (c != *bp++) 96 return (NULL); 97 98 break; 99 100 /* 101 * "Alternative" modifiers. Just set the appropriate flag 102 * and start over again. 103 */ 104 case 'E': /* "%E?" alternative conversion modifier. */ 105 _LEGAL_ALT(0); 106 alt_format |= _ALT_E; 107 goto again; 108 109 case 'O': /* "%O?" alternative conversion modifier. */ 110 _LEGAL_ALT(0); 111 alt_format |= _ALT_O; 112 goto again; 113 114 /* 115 * "Complex" conversion rules, implemented through recursion. 116 */ 117 case 'c': /* Date and time, using the locale's format. */ 118 _LEGAL_ALT(_ALT_E); 119 if (!(bp = _strptime(bp, _ctloc(d_t_fmt), tm, 0))) 120 return (NULL); 121 break; 122 123 case 'D': /* The date as "%m/%d/%y". */ 124 _LEGAL_ALT(0); 125 if (!(bp = _strptime(bp, "%m/%d/%y", tm, 0))) 126 return (NULL); 127 break; 128 129 case 'R': /* The time as "%H:%M". */ 130 _LEGAL_ALT(0); 131 if (!(bp = _strptime(bp, "%H:%M", tm, 0))) 132 return (NULL); 133 break; 134 135 case 'r': /* The time as "%I:%M:%S %p". */ 136 _LEGAL_ALT(0); 137 if (!(bp = _strptime(bp, "%I:%M:%S %p", tm, 0))) 138 return (NULL); 139 break; 140 141 case 'T': /* The time as "%H:%M:%S". */ 142 _LEGAL_ALT(0); 143 if (!(bp = _strptime(bp, "%H:%M:%S", tm, 0))) 144 return (NULL); 145 break; 146 147 case 'X': /* The time, using the locale's format. */ 148 _LEGAL_ALT(_ALT_E); 149 if (!(bp = _strptime(bp, _ctloc(t_fmt), tm, 0))) 150 return (NULL); 151 break; 152 153 case 'x': /* The date, using the locale's format. */ 154 _LEGAL_ALT(_ALT_E); 155 if (!(bp = _strptime(bp, _ctloc(d_fmt), tm, 0))) 156 return (NULL); 157 break; 158 159 /* 160 * "Elementary" conversion rules. 161 */ 162 case 'A': /* The day of week, using the locale's form. */ 163 case 'a': 164 _LEGAL_ALT(0); 165 for (i = 0; i < 7; i++) { 166 /* Full name. */ 167 len = strlen(_ctloc(day[i])); 168 if (strncasecmp(_ctloc(day[i]), bp, len) == 0) 169 break; 170 171 /* Abbreviated name. */ 172 len = strlen(_ctloc(abday[i])); 173 if (strncasecmp(_ctloc(abday[i]), bp, len) == 0) 174 break; 175 } 176 177 /* Nothing matched. */ 178 if (i == 7) 179 return (NULL); 180 181 tm->tm_wday = i; 182 bp += len; 183 break; 184 185 case 'B': /* The month, using the locale's form. */ 186 case 'b': 187 case 'h': 188 _LEGAL_ALT(0); 189 for (i = 0; i < 12; i++) { 190 /* Full name. */ 191 len = strlen(_ctloc(mon[i])); 192 if (strncasecmp(_ctloc(mon[i]), bp, len) == 0) 193 break; 194 195 /* Abbreviated name. */ 196 len = strlen(_ctloc(abmon[i])); 197 if (strncasecmp(_ctloc(abmon[i]), bp, len) == 0) 198 break; 199 } 200 201 /* Nothing matched. */ 202 if (i == 12) 203 return (NULL); 204 205 tm->tm_mon = i; 206 bp += len; 207 break; 208 209 case 'C': /* The century number. */ 210 _LEGAL_ALT(_ALT_E); 211 if (!(_conv_num(&bp, &i, 0, 99))) 212 return (NULL); 213 214 century = i * 100; 215 break; 216 217 case 'd': /* The day of month. */ 218 case 'e': 219 _LEGAL_ALT(_ALT_O); 220 if (!(_conv_num(&bp, &tm->tm_mday, 1, 31))) 221 return (NULL); 222 break; 223 224 case 'k': /* The hour (24-hour clock representation). */ 225 _LEGAL_ALT(0); 226 /* FALLTHROUGH */ 227 case 'H': 228 _LEGAL_ALT(_ALT_O); 229 if (!(_conv_num(&bp, &tm->tm_hour, 0, 23))) 230 return (NULL); 231 break; 232 233 case 'l': /* The hour (12-hour clock representation). */ 234 _LEGAL_ALT(0); 235 /* FALLTHROUGH */ 236 case 'I': 237 _LEGAL_ALT(_ALT_O); 238 if (!(_conv_num(&bp, &tm->tm_hour, 1, 12))) 239 return (NULL); 240 break; 241 242 case 'j': /* The day of year. */ 243 _LEGAL_ALT(0); 244 if (!(_conv_num(&bp, &tm->tm_yday, 1, 366))) 245 return (NULL); 246 tm->tm_yday--; 247 break; 248 249 case 'M': /* The minute. */ 250 _LEGAL_ALT(_ALT_O); 251 if (!(_conv_num(&bp, &tm->tm_min, 0, 59))) 252 return (NULL); 253 break; 254 255 case 'm': /* The month. */ 256 _LEGAL_ALT(_ALT_O); 257 if (!(_conv_num(&bp, &tm->tm_mon, 1, 12))) 258 return (NULL); 259 tm->tm_mon--; 260 break; 261 262 case 'p': /* The locale's equivalent of AM/PM. */ 263 _LEGAL_ALT(0); 264 /* AM? */ 265 len = strlen(_ctloc(am_pm[0])); 266 if (strncasecmp(_ctloc(am_pm[0]), bp, len) == 0) { 267 if (tm->tm_hour > 12) /* i.e., 13:00 AM ?! */ 268 return (NULL); 269 else if (tm->tm_hour == 12) 270 tm->tm_hour = 0; 271 272 bp += len; 273 break; 274 } 275 /* PM? */ 276 len = strlen(_ctloc(am_pm[1])); 277 if (strncasecmp(_ctloc(am_pm[1]), bp, len) == 0) { 278 if (tm->tm_hour > 12) /* i.e., 13:00 PM ?! */ 279 return (NULL); 280 else if (tm->tm_hour < 12) 281 tm->tm_hour += 12; 282 283 bp += len; 284 break; 285 } 286 287 /* Nothing matched. */ 288 return (NULL); 289 290 case 'S': /* The seconds. */ 291 _LEGAL_ALT(_ALT_O); 292 if (!(_conv_num(&bp, &tm->tm_sec, 0, 61))) 293 return (NULL); 294 break; 295 296 case 'U': /* The week of year, beginning on sunday. */ 297 case 'W': /* The week of year, beginning on monday. */ 298 _LEGAL_ALT(_ALT_O); 299 /* 300 * XXX This is bogus, as we can not assume any valid 301 * information present in the tm structure at this 302 * point to calculate a real value, so just check the 303 * range for now. 304 */ 305 if (!(_conv_num(&bp, &i, 0, 53))) 306 return (NULL); 307 break; 308 309 case 'w': /* The day of week, beginning on sunday. */ 310 _LEGAL_ALT(_ALT_O); 311 if (!(_conv_num(&bp, &tm->tm_wday, 0, 6))) 312 return (NULL); 313 break; 314 315 case 'Y': /* The year. */ 316 _LEGAL_ALT(_ALT_E); 317 if (!(_conv_num(&bp, &i, 0, 9999))) 318 return (NULL); 319 320 relyear = -1; 321 tm->tm_year = i - TM_YEAR_BASE; 322 break; 323 324 case 'y': /* The year within the century (2 digits). */ 325 _LEGAL_ALT(_ALT_E | _ALT_O); 326 if (!(_conv_num(&bp, &relyear, 0, 99))) 327 return (NULL); 328 break; 329 330 /* 331 * Miscellaneous conversions. 332 */ 333 case 'n': /* Any kind of white-space. */ 334 case 't': 335 _LEGAL_ALT(0); 336 while (isspace(*bp)) 337 bp++; 338 break; 339 340 341 default: /* Unknown/unsupported conversion. */ 342 return (NULL); 343 } 344 345 346 } 347 348 /* 349 * We need to evaluate the two digit year spec (%y) 350 * last as we can get a century spec (%C) at any time. 351 */ 352 if (relyear != -1) { 353 if (century == TM_YEAR_BASE) { 354 if (relyear <= 68) 355 tm->tm_year = relyear + 2000 - TM_YEAR_BASE; 356 else 357 tm->tm_year = relyear + 1900 - TM_YEAR_BASE; 358 } else { 359 tm->tm_year = relyear + century - TM_YEAR_BASE; 360 } 361 } 362 363 return ((char *)bp); 364 } 365 366 367 static int 368 _conv_num(const unsigned char **buf, int *dest, int llim, int ulim) 369 { 370 int result = 0; 371 int rulim = ulim; 372 373 if (**buf < '0' || **buf > '9') 374 return (0); 375 376 /* we use rulim to break out of the loop when we run out of digits */ 377 do { 378 result *= 10; 379 result += *(*buf)++ - '0'; 380 rulim /= 10; 381 } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9'); 382 383 if (result < llim || result > ulim) 384 return (0); 385 386 *dest = result; 387 return (1); 388 } 389