1 /** $MirOS: src/lib/libc/time/strptime.c,v 1.6 2011/11/20 19:32:10 tg Exp $ */
2 /* $OpenBSD: strptime.c,v 1.12 2008/06/26 05:42:05 ray Exp $ */
3 /* $NetBSD: strptime.c,v 1.12 1998/01/20 21:39:40 mycroft Exp $ */
4
5 /*-
6 * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
7 * All rights reserved.
8 *
9 * This code was contributed to The NetBSD Foundation by Klaus Klein.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <sys/cdefs.h>
34 #include <sys/localedef.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <limits.h>
38 #include <locale.h>
39 #include <stdint.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <time.h>
43 #include <tzfile.h>
44
45 __RCSID("$MirOS: src/lib/libc/time/strptime.c,v 1.6 2011/11/20 19:32:10 tg Exp $");
46
47 /*
48 * Evil hack for const correctness due to API brokenness
49 */
50 union mksh_cchack {
51 char *rw;
52 const char *ro;
53 };
54
55 #define _ctloc(x) (_DefaultTimeLocale.x)
56
57 /*
58 * We do not implement alternate representations. However, we always
59 * check whether a given modifier is allowed for a certain conversion.
60 */
61 #define _ALT_E 0x01
62 #define _ALT_O 0x02
63 #define _LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); }
64
65
66 static int _conv_num(const unsigned char **, int *, int, int);
67 static char *_strptime(const char *, const char *, struct tm *, int);
68
69
70 char *
strptime(const char * buf,const char * fmt,struct tm * tm)71 strptime(const char *buf, const char *fmt, struct tm *tm)
72 {
73 return(_strptime(buf, fmt, tm, 1));
74 }
75
76 static char *
_strptime(const char * buf,const char * fmt,struct tm * tm,int initialize)77 _strptime(const char *buf, const char *fmt, struct tm *tm, int initialize)
78 {
79 unsigned char c;
80 const unsigned char *bp;
81 size_t len = 0;
82 int alt_format, i;
83 static int century, relyear;
84 union mksh_cchack rv;
85
86 if (initialize) {
87 century = TM_YEAR_BASE;
88 relyear = -1;
89 }
90
91 bp = (const unsigned char *)buf;
92 while ((c = *fmt) != '\0') {
93 /* Clear `alternate' modifier prior to new conversion. */
94 alt_format = 0;
95
96 /* Eat up white-space. */
97 if (isspace(c)) {
98 while (isspace(*bp))
99 bp++;
100
101 fmt++;
102 continue;
103 }
104
105 if ((c = *fmt++) != '%')
106 goto literal;
107
108
109 again: switch (c = *fmt++) {
110 case '%': /* "%%" is converted to "%". */
111 literal:
112 if (c != *bp++)
113 return (NULL);
114
115 break;
116
117 /*
118 * "Alternative" modifiers. Just set the appropriate flag
119 * and start over again.
120 */
121 case 'E': /* "%E?" alternative conversion modifier. */
122 _LEGAL_ALT(0);
123 alt_format |= _ALT_E;
124 goto again;
125
126 case 'O': /* "%O?" alternative conversion modifier. */
127 _LEGAL_ALT(0);
128 alt_format |= _ALT_O;
129 goto again;
130
131 /*
132 * "Complex" conversion rules, implemented through recursion.
133 */
134 case 'c': /* Date and time, using the locale's format. */
135 _LEGAL_ALT(_ALT_E);
136 if (!(bp = _strptime(bp, _ctloc(d_t_fmt), tm, 0)))
137 return (NULL);
138 break;
139
140 case 'D': /* The date as "%m/%d/%y". */
141 _LEGAL_ALT(0);
142 if (!(bp = _strptime(bp, "%m/%d/%y", tm, 0)))
143 return (NULL);
144 break;
145
146 case 'F': /* The date as "%Y-%m-%d". */
147 _LEGAL_ALT(0);
148 if (!(bp = _strptime(bp, "%Y-%m-%d", tm, 0)))
149 return (NULL);
150 break;
151
152 case 'R': /* The time as "%H:%M". */
153 _LEGAL_ALT(0);
154 if (!(bp = _strptime(bp, "%H:%M", tm, 0)))
155 return (NULL);
156 break;
157
158 case 'r': /* The time as "%I:%M:%S %p". */
159 _LEGAL_ALT(0);
160 if (!(bp = _strptime(bp, "%I:%M:%S %p", tm, 0)))
161 return (NULL);
162 break;
163
164 case 'T': /* The time as "%H:%M:%S". */
165 _LEGAL_ALT(0);
166 if (!(bp = _strptime(bp, "%H:%M:%S", tm, 0)))
167 return (NULL);
168 break;
169
170 case 'X': /* The time, using the locale's format. */
171 _LEGAL_ALT(_ALT_E);
172 if (!(bp = _strptime(bp, _ctloc(t_fmt), tm, 0)))
173 return (NULL);
174 break;
175
176 case 'x': /* The date, using the locale's format. */
177 _LEGAL_ALT(_ALT_E);
178 if (!(bp = _strptime(bp, _ctloc(d_fmt), tm, 0)))
179 return (NULL);
180 break;
181
182 /*
183 * "Elementary" conversion rules.
184 */
185 case 'A': /* The day of week, using the locale's form. */
186 case 'a':
187 _LEGAL_ALT(0);
188 for (i = 0; i < 7; i++) {
189 /* Full name. */
190 len = strlen(_ctloc(day[i]));
191 if (strncasecmp(_ctloc(day[i]), bp, len) == 0)
192 break;
193
194 /* Abbreviated name. */
195 len = strlen(_ctloc(abday[i]));
196 if (strncasecmp(_ctloc(abday[i]), bp, len) == 0)
197 break;
198 }
199
200 /* Nothing matched. */
201 if (i == 7)
202 return (NULL);
203
204 tm->tm_wday = i;
205 bp += len;
206 break;
207
208 case 'B': /* The month, using the locale's form. */
209 case 'b':
210 case 'h':
211 _LEGAL_ALT(0);
212 for (i = 0; i < 12; i++) {
213 /* Full name. */
214 len = strlen(_ctloc(mon[i]));
215 if (strncasecmp(_ctloc(mon[i]), bp, len) == 0)
216 break;
217
218 /* Abbreviated name. */
219 len = strlen(_ctloc(abmon[i]));
220 if (strncasecmp(_ctloc(abmon[i]), bp, len) == 0)
221 break;
222 }
223
224 /* Nothing matched. */
225 if (i == 12)
226 return (NULL);
227
228 tm->tm_mon = i;
229 bp += len;
230 break;
231
232 case 'C': /* The century number. */
233 _LEGAL_ALT(_ALT_E);
234 if (!(_conv_num(&bp, &i, 0, 99)))
235 return (NULL);
236
237 century = i * 100;
238 break;
239
240 case 'd': /* The day of month. */
241 case 'e':
242 _LEGAL_ALT(_ALT_O);
243 if (!(_conv_num(&bp, &tm->tm_mday, 1, 31)))
244 return (NULL);
245 break;
246
247 case 'k': /* The hour (24-hour clock representation). */
248 _LEGAL_ALT(0);
249 /* FALLTHROUGH */
250 case 'H':
251 _LEGAL_ALT(_ALT_O);
252 if (!(_conv_num(&bp, &tm->tm_hour, 0, 23)))
253 return (NULL);
254 break;
255
256 case 'l': /* The hour (12-hour clock representation). */
257 _LEGAL_ALT(0);
258 /* FALLTHROUGH */
259 case 'I':
260 _LEGAL_ALT(_ALT_O);
261 if (!(_conv_num(&bp, &tm->tm_hour, 1, 12)))
262 return (NULL);
263 break;
264
265 case 'J': { /* Julian Date */
266 /* Modified Julian Date */
267 mirtime_mjd t_mjd;
268 /* julian date, as per http://tycho.usno.navy.mil/mjd.html */
269 double t_jd;
270 char cvb[24], *cp;
271 size_t cvbp = 0;
272 int gotdot = 0, saved_errno;
273
274 if (*bp == '-')
275 cvb[cvbp++] = *bp++;
276
277 if (*bp != '.' && (*bp < '0' || *bp > '9'))
278 return (NULL);
279
280 while (cvbp < sizeof(cvb) - 1)
281 if ((!gotdot && *bp == '.') ||
282 (*bp >= '0' && *bp <= '9')) {
283 if (*bp == '.')
284 gotdot = 1;
285 cvb[cvbp++] = *bp++;
286 } else
287 break;
288 cvb[cvbp] = '\0';
289
290 saved_errno = errno;
291 errno = 0;
292 t_jd = strtod(cvb, &cp);
293 if (errno == ERANGE)
294 cp = NULL;
295 errno = saved_errno;
296 if (cp == NULL || *cp != '\0')
297 return (NULL);
298
299 t_jd -= 2400000.5; /* JD -> MJD */
300 t_mjd.mjd = (time_t)t_jd; /* day part */
301 if (t_mjd.mjd < 0)
302 --t_mjd.mjd; /* make seconds positive */
303 t_jd -= t_mjd.mjd; /* frac. seconds part */
304 /* how many seconds does this day have? */
305 t_mjd.sec = 86399;
306 t_jd *= mirtime_isleap(mjd2timet(&t_mjd) + 1) ?
307 86401 : 86400;
308 t_mjd.sec = t_jd; /* int. seconds part */
309 /* return the value */
310 mjd_explode(tm, &t_mjd);
311 break;
312 }
313
314 case 'j': /* The day of year. */
315 _LEGAL_ALT(0);
316 if (!(_conv_num(&bp, &tm->tm_yday, 1, 366)))
317 return (NULL);
318 tm->tm_yday--;
319 break;
320
321 case 'M': /* The minute. */
322 _LEGAL_ALT(_ALT_O);
323 if (!(_conv_num(&bp, &tm->tm_min, 0, 59)))
324 return (NULL);
325 break;
326
327 case 'm': /* The month. */
328 _LEGAL_ALT(_ALT_O);
329 if (!(_conv_num(&bp, &tm->tm_mon, 1, 12)))
330 return (NULL);
331 tm->tm_mon--;
332 break;
333
334 case 'p': /* The locale's equivalent of AM/PM. */
335 _LEGAL_ALT(0);
336 /* AM? */
337 len = strlen(_ctloc(am_pm[0]));
338 if (strncasecmp(_ctloc(am_pm[0]), bp, len) == 0) {
339 if (tm->tm_hour > 12) /* i.e., 13:00 AM ?! */
340 return (NULL);
341 else if (tm->tm_hour == 12)
342 tm->tm_hour = 0;
343
344 bp += len;
345 break;
346 }
347 /* PM? */
348 len = strlen(_ctloc(am_pm[1]));
349 if (strncasecmp(_ctloc(am_pm[1]), bp, len) == 0) {
350 if (tm->tm_hour > 12) /* i.e., 13:00 PM ?! */
351 return (NULL);
352 else if (tm->tm_hour < 12)
353 tm->tm_hour += 12;
354
355 bp += len;
356 break;
357 }
358
359 /* Nothing matched. */
360 return (NULL);
361
362 case 'S': /* The seconds. */
363 _LEGAL_ALT(_ALT_O);
364 if (!(_conv_num(&bp, &tm->tm_sec, 0, 61)))
365 return (NULL);
366 break;
367
368 #ifndef TIME_MAX
369 #ifdef _BSD_TIME_T_IS_64_BIT
370 #define TIME_MAX INT64_MAX
371 #else
372 #define TIME_MAX INT32_MAX
373 #endif
374 #endif
375 case 's': /* seconds since the epoch */
376 {
377 time_t sse = 0;
378 uint64_t rulim = TIME_MAX;
379
380 if (*bp < '0' || *bp > '9')
381 return (NULL);
382
383 do {
384 sse *= 10;
385 sse += *bp++ - '0';
386 rulim /= 10;
387 } while ((sse <= TIME_MAX / 10) &&
388 rulim && *bp >= '0' && *bp <= '9');
389
390 if (sse < 0 || (uint64_t)sse > TIME_MAX)
391 return (NULL);
392
393 if (localtime_r(&sse, tm) == NULL)
394 return (NULL);
395 }
396 break;
397
398 case 'U': /* The week of year, beginning on sunday. */
399 case 'W': /* The week of year, beginning on monday. */
400 _LEGAL_ALT(_ALT_O);
401 /*
402 * XXX This is bogus, as we can not assume any valid
403 * information present in the tm structure at this
404 * point to calculate a real value, so just check the
405 * range for now.
406 */
407 if (!(_conv_num(&bp, &i, 0, 53)))
408 return (NULL);
409 break;
410
411 case 'w': /* The day of week, beginning on sunday. */
412 _LEGAL_ALT(_ALT_O);
413 if (!(_conv_num(&bp, &tm->tm_wday, 0, 6)))
414 return (NULL);
415 break;
416
417 case 'Y': /* The year. */
418 _LEGAL_ALT(_ALT_E);
419 if (!(_conv_num(&bp, &i, 0, 9999)))
420 return (NULL);
421
422 relyear = -1;
423 tm->tm_year = i - TM_YEAR_BASE;
424 break;
425
426 case 'y': /* The year within the century (2 digits). */
427 _LEGAL_ALT(_ALT_E | _ALT_O);
428 if (!(_conv_num(&bp, &relyear, 0, 99)))
429 return (NULL);
430 break;
431
432 /*
433 * Miscellaneous conversions.
434 */
435 case 'n': /* Any kind of white-space. */
436 case 't':
437 _LEGAL_ALT(0);
438 while (isspace(*bp))
439 bp++;
440 break;
441
442
443 default: /* Unknown/unsupported conversion. */
444 return (NULL);
445 }
446
447
448 }
449
450 /*
451 * We need to evaluate the two digit year spec (%y)
452 * last as we can get a century spec (%C) at any time.
453 */
454 if (relyear != -1) {
455 if (century == TM_YEAR_BASE) {
456 if (relyear <= 68)
457 tm->tm_year = relyear + 2000 - TM_YEAR_BASE;
458 else
459 tm->tm_year = relyear + 1900 - TM_YEAR_BASE;
460 } else {
461 tm->tm_year = relyear + century - TM_YEAR_BASE;
462 }
463 }
464
465 rv.ro = bp;
466 return (rv.rw);
467 }
468
469
470 static int
_conv_num(const unsigned char ** buf,int * dest,int llim,int ulim)471 _conv_num(const unsigned char **buf, int *dest, int llim, int ulim)
472 {
473 int result = 0;
474 int rulim = ulim;
475
476 if (**buf < '0' || **buf > '9')
477 return (0);
478
479 /* we use rulim to break out of the loop when we run out of digits */
480 do {
481 result *= 10;
482 result += *(*buf)++ - '0';
483 rulim /= 10;
484 } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
485
486 if (result < llim || result > ulim)
487 return (0);
488
489 *dest = result;
490 return (1);
491 }
492