1 /* $NetBSD: strptime.c,v 1.7 1997/07/21 14:09:22 jtc Exp $ */ 2 3 /*- 4 * Copyright (c) 1997 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 #include <sys/cdefs.h> 39 #if defined(LIBC_SCCS) && !defined(lint) 40 __RCSID("$NetBSD: strptime.c,v 1.7 1997/07/21 14:09:22 jtc Exp $"); 41 #endif 42 43 #include "namespace.h" 44 #include <sys/localedef.h> 45 #include <ctype.h> 46 #include <locale.h> 47 #include <string.h> 48 #include <time.h> 49 50 #ifdef __weak_alias 51 __weak_alias(strptime,_strptime); 52 #endif 53 54 #define _ctloc(x) __CONCAT(_CurrentTimeLocale->,x) 55 56 /* 57 * We do not implement alternate representations. However, we always 58 * check whether a given modifier is allowed for a certain conversion. 59 */ 60 #define _ALT_E 0x01 61 #define _ALT_O 0x02 62 #define _LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); } 63 64 65 static int _conv_num __P((const char **, int *, int, int)); 66 67 68 char * 69 strptime(buf, fmt, tm) 70 const char *buf, *fmt; 71 struct tm *tm; 72 { 73 char c; 74 const char *bp; 75 int alt_format, i, len; 76 77 bp = buf; 78 79 while ((c = *fmt) != '\0') { 80 /* Clear `alternate' modifier prior to new conversion. */ 81 alt_format = 0; 82 83 /* Eat up white-space. */ 84 if (isspace(c)) { 85 while (isspace(*bp)) 86 bp++; 87 88 fmt++; 89 continue; 90 } 91 92 if ((c = *fmt++) != '%') 93 goto literal; 94 95 96 again: switch (c = *fmt++) { 97 case '%': /* "%%" is converted to "%". */ 98 literal: 99 if (c != *bp++) 100 return (0); 101 102 break; 103 104 /* 105 * "Alternative" modifiers. Just set the appropriate flag 106 * and start over again. 107 */ 108 case 'E': /* "%E?" alternative conversion modifier. */ 109 _LEGAL_ALT(0); 110 alt_format |= _ALT_E; 111 goto again; 112 113 case 'O': /* "%O?" alternative conversion modifier. */ 114 _LEGAL_ALT(0); 115 alt_format |= _ALT_O; 116 goto again; 117 118 /* 119 * "Complex" conversion rules, implemented through recursion. 120 */ 121 case 'c': /* Date and time, using the locale's format. */ 122 _LEGAL_ALT(_ALT_E); 123 if (!(bp = strptime(bp, _ctloc(d_t_fmt), tm))) 124 return (0); 125 break; 126 127 case 'D': /* The date as "%m/%d/%y". */ 128 _LEGAL_ALT(0); 129 if (!(bp = strptime(bp, "%m/%d/%y", tm))) 130 return (0); 131 break; 132 133 case 'R': /* The time as "%H:%M". */ 134 _LEGAL_ALT(0); 135 if (!(bp = strptime(bp, "%H:%M", tm))) 136 return (0); 137 break; 138 139 case 'r': /* The time in 12-hour clock representation. */ 140 _LEGAL_ALT(0); 141 if (!(bp = strptime(bp, _ctloc(t_fmt_ampm), tm))) 142 return (0); 143 break; 144 145 case 'T': /* The time as "%H:%M:%S". */ 146 _LEGAL_ALT(0); 147 if (!(bp = strptime(bp, "%H:%M:%S", tm))) 148 return (0); 149 break; 150 151 case 'X': /* The time, using the locale's format. */ 152 _LEGAL_ALT(_ALT_E); 153 if (!(bp = strptime(bp, _ctloc(t_fmt), tm))) 154 return (0); 155 break; 156 157 case 'x': /* The date, using the locale's format. */ 158 _LEGAL_ALT(_ALT_E); 159 if (!(bp = strptime(bp, _ctloc(d_fmt), tm))) 160 return (0); 161 break; 162 163 /* 164 * "Elementary" conversion rules. 165 */ 166 case 'A': /* The day of week, using the locale's form. */ 167 case 'a': 168 _LEGAL_ALT(0); 169 for (i = 0; i < 7; i++) { 170 /* Full name. */ 171 len = strlen(_ctloc(day[i])); 172 if (strncmp(_ctloc(day[i]), bp, len) == 0) 173 break; 174 175 /* Abbreviated name. */ 176 len = strlen(_ctloc(abday[i])); 177 if (strncmp(_ctloc(abday[i]), bp, len) == 0) 178 break; 179 } 180 181 /* Nothing matched. */ 182 if (i == 7) 183 return (0); 184 185 tm->tm_wday = i; 186 bp += len; 187 break; 188 189 case 'B': /* The month, using the locale's form. */ 190 case 'b': 191 case 'h': 192 _LEGAL_ALT(0); 193 for (i = 0; i < 12; i++) { 194 /* Full name. */ 195 len = strlen(_ctloc(mon[i])); 196 if (strncmp(_ctloc(mon[i]), bp, len) == 0) 197 break; 198 199 /* Abbreviated name. */ 200 len = strlen(_ctloc(abmon[i])); 201 if (strncmp(_ctloc(abmon[i]), bp, len) == 0) 202 break; 203 } 204 205 /* Nothing matched. */ 206 if (i == 12) 207 return (0); 208 209 tm->tm_mon = i; 210 bp += len; 211 break; 212 213 case 'C': /* The century number. */ 214 _LEGAL_ALT(_ALT_E); 215 if (!(_conv_num(&bp, &i, 0, 99))) 216 return (0); 217 218 tm->tm_year = i * 100; 219 break; 220 221 case 'd': /* The day of month. */ 222 case 'e': 223 _LEGAL_ALT(_ALT_O); 224 if (!(_conv_num(&bp, &tm->tm_mday, 1, 31))) 225 return (0); 226 break; 227 228 case 'k': /* The hour (24-hour clock representation). */ 229 _LEGAL_ALT(0); 230 /* FALLTHROUGH */ 231 case 'H': 232 _LEGAL_ALT(_ALT_O); 233 if (!(_conv_num(&bp, &tm->tm_hour, 0, 23))) 234 return (0); 235 break; 236 237 case 'l': /* The hour (12-hour clock representation). */ 238 _LEGAL_ALT(0); 239 /* FALLTHROUGH */ 240 case 'I': 241 _LEGAL_ALT(_ALT_O); 242 if (!(_conv_num(&bp, &tm->tm_hour, 0, 11))) 243 return (0); 244 break; 245 246 case 'j': /* The day of year. */ 247 _LEGAL_ALT(0); 248 if (!(_conv_num(&bp, &tm->tm_yday, 1, 366))) 249 return (0); 250 break; 251 252 case 'M': /* The minute. */ 253 _LEGAL_ALT(_ALT_O); 254 if (!(_conv_num(&bp, &tm->tm_min, 0, 59))) 255 return (0); 256 break; 257 258 case 'm': /* The month. */ 259 _LEGAL_ALT(_ALT_O); 260 if (!(_conv_num(&bp, &tm->tm_mon, 1, 12))) 261 return (0); 262 break; 263 264 case 'p': /* The locale's equivalent of AM/PM. */ 265 _LEGAL_ALT(0); 266 /* AM? */ 267 if (strcmp(_ctloc(am_pm[0]), bp) == 0) { 268 if (tm->tm_hour > 12) /* i.e., 13:00 AM ?! */ 269 return (0); 270 else if (tm->tm_hour == 12) 271 tm->tm_hour = 0; 272 273 bp += strlen(_ctloc(am_pm[0])); 274 break; 275 } 276 /* PM? */ 277 else if (strcmp(_ctloc(am_pm[1]), bp) == 0) { 278 if (tm->tm_hour > 12) /* i.e., 13:00 PM ?! */ 279 return (0); 280 else if (tm->tm_hour < 12) 281 tm->tm_hour += 12; 282 283 bp += strlen(_ctloc(am_pm[1])); 284 break; 285 } 286 287 /* Nothing matched. */ 288 return (0); 289 290 case 'S': /* The seconds. */ 291 _LEGAL_ALT(_ALT_O); 292 if (!(_conv_num(&bp, &tm->tm_sec, 1, 61))) 293 return (0); 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 (0); 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 (0); 313 break; 314 315 case 'Y': /* The year. */ 316 _LEGAL_ALT(_ALT_E); 317 if (!(_conv_num(&bp, &i, 0, INT_MAX))) 318 return (0); 319 320 tm->tm_year = i - 1900; 321 break; 322 323 case 'y': /* The year within the 20th century. */ 324 _LEGAL_ALT(_ALT_E | _ALT_O); 325 if (!(_conv_num(&bp, &tm->tm_year, 0, 99))) 326 return (0); 327 break; 328 329 /* 330 * Miscellaneous conversions. 331 */ 332 case 'n': /* Any kind of white-space. */ 333 case 't': 334 _LEGAL_ALT(0); 335 while (isspace(*bp)) 336 bp++; 337 break; 338 339 340 default: /* Unknown/unsupported conversion. */ 341 return (0); 342 } 343 344 345 } 346 347 return ((char *)bp); 348 } 349 350 351 static int 352 _conv_num(buf, dest, llim, ulim) 353 const char **buf; 354 int *dest; 355 int llim, ulim; 356 { 357 *dest = 0; 358 359 if (**buf < '0' || **buf > '9') 360 return (0); 361 362 do { 363 *dest *= 10; 364 *dest += *(*buf)++ - '0'; 365 } while ((*dest * 10 <= ulim) && **buf >= '0' && **buf <= '9'); 366 367 if (*dest < llim || *dest > ulim) 368 return (0); 369 370 return (1); 371 } 372