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