strptime.c revision 268043
1/*-
2 * Copyright (c) 2014 Gary Mills
3 * Copyright 2011, Nexenta Systems, Inc.  All rights reserved.
4 * Copyright (c) 1994 Powerdog Industries.  All rights reserved.
5 *
6 * Copyright (c) 2011 The FreeBSD Foundation
7 * All rights reserved.
8 * Portions of this software were developed by David Chisnall
9 * under sponsorship from the FreeBSD Foundation.
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
18 *    in the documentation and/or other materials provided with the
19 *    distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
30 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
31 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 * The views and conclusions contained in the software and documentation
34 * are those of the authors and should not be interpreted as representing
35 * official policies, either expressed or implied, of Powerdog Industries.
36 */
37
38#include <sys/cdefs.h>
39#ifndef lint
40#ifndef NOID
41static char copyright[] __unused =
42"@(#) Copyright (c) 1994 Powerdog Industries.  All rights reserved.";
43static char sccsid[] __unused = "@(#)strptime.c	0.1 (Powerdog) 94/03/27";
44#endif /* !defined NOID */
45#endif /* not lint */
46__FBSDID("$FreeBSD: stable/10/lib/libc/stdtime/strptime.c 268043 2014-06-30 14:52:40Z pfg $");
47
48#include "namespace.h"
49#include <time.h>
50#include <ctype.h>
51#include <errno.h>
52#include <stdlib.h>
53#include <string.h>
54#include <pthread.h>
55#include "un-namespace.h"
56#include "libc_private.h"
57#include "timelocal.h"
58
59static char * _strptime(const char *, const char *, struct tm *, int *, locale_t);
60
61#define	asizeof(a)	(sizeof (a) / sizeof ((a)[0]))
62
63static char *
64_strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp,
65		locale_t locale)
66{
67	char	c;
68	const char *ptr;
69	int	i, len;
70	int Ealternative, Oalternative;
71	struct lc_time_T *tptr = __get_current_time_locale(locale);
72
73	ptr = fmt;
74	while (*ptr != 0) {
75		if (*buf == 0)
76			break;
77
78		c = *ptr++;
79
80		if (c != '%') {
81			if (isspace_l((unsigned char)c, locale))
82				while (*buf != 0 &&
83				       isspace_l((unsigned char)*buf, locale))
84					buf++;
85			else if (c != *buf++)
86				return (NULL);
87			continue;
88		}
89
90		Ealternative = 0;
91		Oalternative = 0;
92label:
93		c = *ptr++;
94		switch (c) {
95		case 0:
96		case '%':
97			if (*buf++ != '%')
98				return (NULL);
99			break;
100
101		case '+':
102			buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale);
103			if (buf == NULL)
104				return (NULL);
105			break;
106
107		case 'C':
108			if (!isdigit_l((unsigned char)*buf, locale))
109				return (NULL);
110
111			/* XXX This will break for 3-digit centuries. */
112			len = 2;
113			for (i = 0; len && *buf != 0 &&
114			     isdigit_l((unsigned char)*buf, locale); buf++) {
115				i *= 10;
116				i += *buf - '0';
117				len--;
118			}
119			if (i < 19)
120				return (NULL);
121
122			tm->tm_year = i * 100 - 1900;
123			break;
124
125		case 'c':
126			buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale);
127			if (buf == NULL)
128				return (NULL);
129			break;
130
131		case 'D':
132			buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale);
133			if (buf == NULL)
134				return (NULL);
135			break;
136
137		case 'E':
138			if (Ealternative || Oalternative)
139				break;
140			Ealternative++;
141			goto label;
142
143		case 'O':
144			if (Ealternative || Oalternative)
145				break;
146			Oalternative++;
147			goto label;
148
149		case 'F':
150			buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale);
151			if (buf == NULL)
152				return (NULL);
153			break;
154
155		case 'R':
156			buf = _strptime(buf, "%H:%M", tm, GMTp, locale);
157			if (buf == NULL)
158				return (NULL);
159			break;
160
161		case 'r':
162			buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale);
163			if (buf == NULL)
164				return (NULL);
165			break;
166
167		case 'T':
168			buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale);
169			if (buf == NULL)
170				return (NULL);
171			break;
172
173		case 'X':
174			buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale);
175			if (buf == NULL)
176				return (NULL);
177			break;
178
179		case 'x':
180			buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale);
181			if (buf == NULL)
182				return (NULL);
183			break;
184
185		case 'j':
186			if (!isdigit_l((unsigned char)*buf, locale))
187				return (NULL);
188
189			len = 3;
190			for (i = 0; len && *buf != 0 &&
191			     isdigit_l((unsigned char)*buf, locale); buf++){
192				i *= 10;
193				i += *buf - '0';
194				len--;
195			}
196			if (i < 1 || i > 366)
197				return (NULL);
198
199			tm->tm_yday = i - 1;
200			break;
201
202		case 'M':
203		case 'S':
204			if (*buf == 0 ||
205				isspace_l((unsigned char)*buf, locale))
206				break;
207
208			if (!isdigit_l((unsigned char)*buf, locale))
209				return (NULL);
210
211			len = 2;
212			for (i = 0; len && *buf != 0 &&
213				isdigit_l((unsigned char)*buf, locale); buf++){
214				i *= 10;
215				i += *buf - '0';
216				len--;
217			}
218
219			if (c == 'M') {
220				if (i > 59)
221					return (NULL);
222				tm->tm_min = i;
223			} else {
224				if (i > 60)
225					return (NULL);
226				tm->tm_sec = i;
227			}
228
229			break;
230
231		case 'H':
232		case 'I':
233		case 'k':
234		case 'l':
235			/*
236			 * Of these, %l is the only specifier explicitly
237			 * documented as not being zero-padded.  However,
238			 * there is no harm in allowing zero-padding.
239			 *
240			 * XXX The %l specifier may gobble one too many
241			 * digits if used incorrectly.
242			 */
243			if (!isdigit_l((unsigned char)*buf, locale))
244				return (NULL);
245
246			len = 2;
247			for (i = 0; len && *buf != 0 &&
248			     isdigit_l((unsigned char)*buf, locale); buf++) {
249				i *= 10;
250				i += *buf - '0';
251				len--;
252			}
253			if (c == 'H' || c == 'k') {
254				if (i > 23)
255					return (NULL);
256			} else if (i > 12)
257				return (NULL);
258
259			tm->tm_hour = i;
260
261			break;
262
263		case 'p':
264			/*
265			 * XXX This is bogus if parsed before hour-related
266			 * specifiers.
267			 */
268			len = strlen(tptr->am);
269			if (strncasecmp_l(buf, tptr->am, len, locale) == 0) {
270				if (tm->tm_hour > 12)
271					return (NULL);
272				if (tm->tm_hour == 12)
273					tm->tm_hour = 0;
274				buf += len;
275				break;
276			}
277
278			len = strlen(tptr->pm);
279			if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) {
280				if (tm->tm_hour > 12)
281					return (NULL);
282				if (tm->tm_hour != 12)
283					tm->tm_hour += 12;
284				buf += len;
285				break;
286			}
287
288			return (NULL);
289
290		case 'A':
291		case 'a':
292			for (i = 0; i < asizeof(tptr->weekday); i++) {
293				len = strlen(tptr->weekday[i]);
294				if (strncasecmp_l(buf, tptr->weekday[i],
295						len, locale) == 0)
296					break;
297				len = strlen(tptr->wday[i]);
298				if (strncasecmp_l(buf, tptr->wday[i],
299						len, locale) == 0)
300					break;
301			}
302			if (i == asizeof(tptr->weekday))
303				return (NULL);
304
305			tm->tm_wday = i;
306			buf += len;
307			break;
308
309		case 'U':
310		case 'W':
311			/*
312			 * XXX This is bogus, as we can not assume any valid
313			 * information present in the tm structure at this
314			 * point to calculate a real value, so just check the
315			 * range for now.
316			 */
317			if (!isdigit_l((unsigned char)*buf, locale))
318				return (NULL);
319
320			len = 2;
321			for (i = 0; len && *buf != 0 &&
322			     isdigit_l((unsigned char)*buf, locale); buf++) {
323				i *= 10;
324				i += *buf - '0';
325				len--;
326			}
327			if (i > 53)
328				return (NULL);
329
330			break;
331
332		case 'w':
333			if (!isdigit_l((unsigned char)*buf, locale))
334				return (NULL);
335
336			i = *buf - '0';
337			if (i > 6)
338				return (NULL);
339
340			tm->tm_wday = i;
341
342			break;
343
344		case 'e':
345			/*
346			 * With %e format, our strftime(3) adds a blank space
347			 * before single digits.
348			 */
349			if (*buf != 0 &&
350			    isspace_l((unsigned char)*buf, locale))
351			       buf++;
352			/* FALLTHROUGH */
353		case 'd':
354			/*
355			 * The %e specifier was once explicitly documented as
356			 * not being zero-padded but was later changed to
357			 * equivalent to %d.  There is no harm in allowing
358			 * such padding.
359			 *
360			 * XXX The %e specifier may gobble one too many
361			 * digits if used incorrectly.
362			 */
363			if (!isdigit_l((unsigned char)*buf, locale))
364				return (NULL);
365
366			len = 2;
367			for (i = 0; len && *buf != 0 &&
368			     isdigit_l((unsigned char)*buf, locale); buf++) {
369				i *= 10;
370				i += *buf - '0';
371				len--;
372			}
373			if (i > 31)
374				return (NULL);
375
376			tm->tm_mday = i;
377
378			break;
379
380		case 'B':
381		case 'b':
382		case 'h':
383			for (i = 0; i < asizeof(tptr->month); i++) {
384				if (Oalternative) {
385					if (c == 'B') {
386						len = strlen(tptr->alt_month[i]);
387						if (strncasecmp_l(buf,
388								tptr->alt_month[i],
389								len, locale) == 0)
390							break;
391					}
392				} else {
393					len = strlen(tptr->month[i]);
394					if (strncasecmp_l(buf, tptr->month[i],
395							len, locale) == 0)
396						break;
397				}
398			}
399			/*
400			 * Try the abbreviated month name if the full name
401			 * wasn't found and Oalternative was not requested.
402			 */
403			if (i == asizeof(tptr->month) && !Oalternative) {
404				for (i = 0; i < asizeof(tptr->month); i++) {
405					len = strlen(tptr->mon[i]);
406					if (strncasecmp_l(buf, tptr->mon[i],
407							len, locale) == 0)
408						break;
409				}
410			}
411			if (i == asizeof(tptr->month))
412				return (NULL);
413
414			tm->tm_mon = i;
415			buf += len;
416			break;
417
418		case 'm':
419			if (!isdigit_l((unsigned char)*buf, locale))
420				return (NULL);
421
422			len = 2;
423			for (i = 0; len && *buf != 0 &&
424			     isdigit_l((unsigned char)*buf, locale); buf++) {
425				i *= 10;
426				i += *buf - '0';
427				len--;
428			}
429			if (i < 1 || i > 12)
430				return (NULL);
431
432			tm->tm_mon = i - 1;
433
434			break;
435
436		case 's':
437			{
438			char *cp;
439			int sverrno;
440			long n;
441			time_t t;
442
443			sverrno = errno;
444			errno = 0;
445			n = strtol_l(buf, &cp, 10, locale);
446			if (errno == ERANGE || (long)(t = n) != n) {
447				errno = sverrno;
448				return (NULL);
449			}
450			errno = sverrno;
451			buf = cp;
452			gmtime_r(&t, tm);
453			*GMTp = 1;
454			}
455			break;
456
457		case 'Y':
458		case 'y':
459			if (*buf == 0 ||
460			    isspace_l((unsigned char)*buf, locale))
461				break;
462
463			if (!isdigit_l((unsigned char)*buf, locale))
464				return (NULL);
465
466			len = (c == 'Y') ? 4 : 2;
467			for (i = 0; len && *buf != 0 &&
468			     isdigit_l((unsigned char)*buf, locale); buf++) {
469				i *= 10;
470				i += *buf - '0';
471				len--;
472			}
473			if (c == 'Y')
474				i -= 1900;
475			if (c == 'y' && i < 69)
476				i += 100;
477			if (i < 0)
478				return (NULL);
479
480			tm->tm_year = i;
481
482			break;
483
484		case 'Z':
485			{
486			const char *cp;
487			char *zonestr;
488
489			for (cp = buf; *cp &&
490			     isupper_l((unsigned char)*cp, locale); ++cp) {
491				/*empty*/}
492			if (cp - buf) {
493				zonestr = alloca(cp - buf + 1);
494				strncpy(zonestr, buf, cp - buf);
495				zonestr[cp - buf] = '\0';
496				tzset();
497				if (0 == strcmp(zonestr, "GMT")) {
498				    *GMTp = 1;
499				} else if (0 == strcmp(zonestr, tzname[0])) {
500				    tm->tm_isdst = 0;
501				} else if (0 == strcmp(zonestr, tzname[1])) {
502				    tm->tm_isdst = 1;
503				} else {
504				    return (NULL);
505				}
506				buf += cp - buf;
507			}
508			}
509			break;
510
511		case 'z':
512			{
513			int sign = 1;
514
515			if (*buf != '+') {
516				if (*buf == '-')
517					sign = -1;
518				else
519					return (NULL);
520			}
521
522			buf++;
523			i = 0;
524			for (len = 4; len > 0; len--) {
525				if (isdigit_l((unsigned char)*buf, locale)) {
526					i *= 10;
527					i += *buf - '0';
528					buf++;
529				} else
530					return (NULL);
531			}
532
533			tm->tm_hour -= sign * (i / 100);
534			tm->tm_min  -= sign * (i % 100);
535			*GMTp = 1;
536			}
537			break;
538
539		case 'n':
540		case 't':
541			while (isspace_l((unsigned char)*buf, locale))
542				buf++;
543			break;
544		}
545	}
546	return ((char *)buf);
547}
548
549
550char *
551strptime_l(const char * __restrict buf, const char * __restrict fmt,
552    struct tm * __restrict tm, locale_t loc)
553{
554	char *ret;
555	int gmt;
556	FIX_LOCALE(loc);
557
558	gmt = 0;
559	ret = _strptime(buf, fmt, tm, &gmt, loc);
560	if (ret && gmt) {
561		time_t t = timegm(tm);
562		localtime_r(&t, tm);
563	}
564
565	return (ret);
566}
567char *
568strptime(const char * __restrict buf, const char * __restrict fmt,
569    struct tm * __restrict tm)
570{
571	return strptime_l(buf, fmt, tm, __get_locale());
572}
573