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