1 /* $NetBSD: strptime.c,v 1.25 2005/11/29 03:12:00 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1997, 1998, 2005 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code was contributed to The NetBSD Foundation by Klaus Klein. 8 * Heavily optimised by David Laight 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/cdefs.h> 40 #if defined(LIBC_SCCS) && !defined(lint) 41 __RCSID("$NetBSD: strptime.c,v 1.25 2005/11/29 03:12:00 christos Exp $"); 42 #endif 43 44 #include "namespace.h" 45 #include <sys/localedef.h> 46 #include <ctype.h> 47 #include <locale.h> 48 #include <string.h> 49 #include <time.h> 50 #include <tzfile.h> 51 52 #ifdef __weak_alias 53 __weak_alias(strptime,_strptime) 54 #endif 55 56 #define _ctloc(x) (_CurrentTimeLocale->x) 57 58 /* 59 * We do not implement alternate representations. However, we always 60 * check whether a given modifier is allowed for a certain conversion. 61 */ 62 #define ALT_E 0x01 63 #define ALT_O 0x02 64 #define LEGAL_ALT(x) { if (alt_format & ~(x)) return NULL; } 65 66 67 static const u_char *conv_num(const unsigned char *, int *, uint, uint); 68 static const u_char *find_string(const u_char *, int *, const char * const *, 69 const char * const *, int); 70 71 72 char * 73 strptime(const char *buf, const char *fmt, struct tm *tm) 74 { 75 unsigned char c; 76 const unsigned char *bp; 77 int alt_format, i, split_year = 0; 78 const char *new_fmt; 79 80 bp = (const u_char *)buf; 81 82 while (bp != NULL && (c = *fmt++) != '\0') { 83 /* Clear `alternate' modifier prior to new conversion. */ 84 alt_format = 0; 85 i = 0; 86 87 /* Eat up white-space. */ 88 if (isspace(c)) { 89 while (isspace(*bp)) 90 bp++; 91 continue; 92 } 93 94 if (c != '%') 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 LEGAL_ALT(0); 104 continue; 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 case 'c': /* Date and time, using the locale's format. */ 124 new_fmt = _ctloc(d_t_fmt); 125 goto recurse; 126 127 case 'D': /* The date as "%m/%d/%y". */ 128 new_fmt = "%m/%d/%y"; 129 LEGAL_ALT(0); 130 goto recurse; 131 132 case 'R': /* The time as "%H:%M". */ 133 new_fmt = "%H:%M"; 134 LEGAL_ALT(0); 135 goto recurse; 136 137 case 'r': /* The time in 12-hour clock representation. */ 138 new_fmt =_ctloc(t_fmt_ampm); 139 LEGAL_ALT(0); 140 goto recurse; 141 142 case 'T': /* The time as "%H:%M:%S". */ 143 new_fmt = "%H:%M:%S"; 144 LEGAL_ALT(0); 145 goto recurse; 146 147 case 'X': /* The time, using the locale's format. */ 148 new_fmt =_ctloc(t_fmt); 149 goto recurse; 150 151 case 'x': /* The date, using the locale's format. */ 152 new_fmt =_ctloc(d_fmt); 153 recurse: 154 bp = (const u_char *)strptime((const char *)bp, 155 new_fmt, tm); 156 LEGAL_ALT(ALT_E); 157 continue; 158 159 /* 160 * "Elementary" conversion rules. 161 */ 162 case 'A': /* The day of week, using the locale's form. */ 163 case 'a': 164 bp = find_string(bp, &tm->tm_wday, _ctloc(day), 165 _ctloc(abday), 7); 166 LEGAL_ALT(0); 167 continue; 168 169 case 'B': /* The month, using the locale's form. */ 170 case 'b': 171 case 'h': 172 bp = find_string(bp, &tm->tm_mon, _ctloc(mon), 173 _ctloc(abmon), 12); 174 LEGAL_ALT(0); 175 continue; 176 177 case 'C': /* The century number. */ 178 i = 20; 179 bp = conv_num(bp, &i, 0, 99); 180 181 i = i * 100 - TM_YEAR_BASE; 182 if (split_year) 183 i += tm->tm_year % 100; 184 split_year = 1; 185 tm->tm_year = i; 186 LEGAL_ALT(ALT_E); 187 continue; 188 189 case 'd': /* The day of month. */ 190 case 'e': 191 bp = conv_num(bp, &tm->tm_mday, 1, 31); 192 LEGAL_ALT(ALT_O); 193 continue; 194 195 case 'k': /* The hour (24-hour clock representation). */ 196 LEGAL_ALT(0); 197 /* FALLTHROUGH */ 198 case 'H': 199 bp = conv_num(bp, &tm->tm_hour, 0, 23); 200 LEGAL_ALT(ALT_O); 201 continue; 202 203 case 'l': /* The hour (12-hour clock representation). */ 204 LEGAL_ALT(0); 205 /* FALLTHROUGH */ 206 case 'I': 207 bp = conv_num(bp, &tm->tm_hour, 1, 12); 208 if (tm->tm_hour == 12) 209 tm->tm_hour = 0; 210 LEGAL_ALT(ALT_O); 211 continue; 212 213 case 'j': /* The day of year. */ 214 i = 1; 215 bp = conv_num(bp, &i, 1, 366); 216 tm->tm_yday = i - 1; 217 LEGAL_ALT(0); 218 continue; 219 220 case 'M': /* The minute. */ 221 bp = conv_num(bp, &tm->tm_min, 0, 59); 222 LEGAL_ALT(ALT_O); 223 continue; 224 225 case 'm': /* The month. */ 226 i = 1; 227 bp = conv_num(bp, &i, 1, 12); 228 tm->tm_mon = i - 1; 229 LEGAL_ALT(ALT_O); 230 continue; 231 232 case 'p': /* The locale's equivalent of AM/PM. */ 233 bp = find_string(bp, &i, _ctloc(am_pm), NULL, 2); 234 if (tm->tm_hour > 11) 235 return NULL; 236 tm->tm_hour += i * 12; 237 LEGAL_ALT(0); 238 continue; 239 240 case 'S': /* The seconds. */ 241 bp = conv_num(bp, &tm->tm_sec, 0, 61); 242 LEGAL_ALT(ALT_O); 243 continue; 244 245 case 'U': /* The week of year, beginning on sunday. */ 246 case 'W': /* The week of year, beginning on monday. */ 247 /* 248 * XXX This is bogus, as we can not assume any valid 249 * information present in the tm structure at this 250 * point to calculate a real value, so just check the 251 * range for now. 252 */ 253 bp = conv_num(bp, &i, 0, 53); 254 LEGAL_ALT(ALT_O); 255 continue; 256 257 case 'w': /* The day of week, beginning on sunday. */ 258 bp = conv_num(bp, &tm->tm_wday, 0, 6); 259 LEGAL_ALT(ALT_O); 260 continue; 261 262 case 'Y': /* The year. */ 263 i = TM_YEAR_BASE; /* just for data sanity... */ 264 bp = conv_num(bp, &i, 0, 9999); 265 tm->tm_year = i - TM_YEAR_BASE; 266 LEGAL_ALT(ALT_E); 267 continue; 268 269 case 'y': /* The year within 100 years of the epoch. */ 270 /* LEGAL_ALT(ALT_E | ALT_O); */ 271 bp = conv_num(bp, &i, 0, 99); 272 273 if (split_year) 274 /* preserve century */ 275 i += (tm->tm_year / 100) * 100; 276 else { 277 split_year = 1; 278 if (i <= 68) 279 i = i + 2000 - TM_YEAR_BASE; 280 else 281 i = i + 1900 - TM_YEAR_BASE; 282 } 283 tm->tm_year = i; 284 continue; 285 286 /* 287 * Miscellaneous conversions. 288 */ 289 case 'n': /* Any kind of white-space. */ 290 case 't': 291 while (isspace(*bp)) 292 bp++; 293 LEGAL_ALT(0); 294 continue; 295 296 297 default: /* Unknown/unsupported conversion. */ 298 return NULL; 299 } 300 } 301 302 return __UNCONST(bp); 303 } 304 305 306 static const u_char * 307 conv_num(const unsigned char *buf, int *dest, uint llim, uint ulim) 308 { 309 uint result = 0; 310 unsigned char ch; 311 312 /* The limit also determines the number of valid digits. */ 313 uint rulim = ulim; 314 315 ch = *buf; 316 if (ch < '0' || ch > '9') 317 return NULL; 318 319 do { 320 result *= 10; 321 result += ch - '0'; 322 rulim /= 10; 323 ch = *++buf; 324 } while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9'); 325 326 if (result < llim || result > ulim) 327 return NULL; 328 329 *dest = result; 330 return buf; 331 } 332 333 static const u_char * 334 find_string(const u_char *bp, int *tgt, const char * const *n1, 335 const char * const *n2, int c) 336 { 337 int i; 338 unsigned int len; 339 340 /* check full name - then abbreviated ones */ 341 for (; n1 != NULL; n1 = n2, n2 = NULL) { 342 for (i = 0; i < c; i++, n1++) { 343 len = strlen(*n1); 344 if (strncasecmp(*n1, (const char *)bp, len) == 0) { 345 *tgt = i; 346 return bp + len; 347 } 348 } 349 } 350 351 /* Nothing matched */ 352 return NULL; 353 } 354