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