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