strptime.c revision 227753
13125Sdg/*
23125Sdg * Powerdog Industries kindly requests feedback from anyone modifying
33125Sdg * this function:
43125Sdg *
53125Sdg * Date: Thu, 05 Jun 1997 23:17:17 -0400
63125Sdg * From: Kevin Ruddy <kevin.ruddy@powerdog.com>
73125Sdg * To: James FitzGibbon <james@nexis.net>
83125Sdg * Subject: Re: Use of your strptime(3) code (fwd)
93125Sdg *
103125Sdg * The reason for the "no mod" clause was so that modifications would
113125Sdg * come back and we could integrate them and reissue so that a wider
123125Sdg * audience could use it (thereby spreading the wealth).  This has
133125Sdg * made it possible to get strptime to work on many operating systems.
143125Sdg * I'm not sure why that's "plain unacceptable" to the FreeBSD team.
153125Sdg *
163125Sdg * Anyway, you can change it to "with or without modification" as
173125Sdg * you see fit.  Enjoy.
183125Sdg *
193125Sdg * Kevin Ruddy
203125Sdg * Powerdog Industries, Inc.
213125Sdg */
223125Sdg/*
233125Sdg * Copyright (c) 1994 Powerdog Industries.  All rights reserved.
243125Sdg *
253125Sdg * Copyright (c) 2011 The FreeBSD Foundation
263125Sdg * All rights reserved.
273125Sdg * Portions of this software were developed by David Chisnall
283125Sdg * under sponsorship from the FreeBSD Foundation.
293125Sdg *
3050479Speter * Redistribution and use in source and binary forms, with or without
313125Sdg * modification, are permitted provided that the following conditions
323125Sdg * are met:
333125Sdg * 1. Redistributions of source code must retain the above copyright
343125Sdg *    notice, this list of conditions and the following disclaimer.
353125Sdg * 2. Redistributions in binary form must reproduce the above copyright
363125Sdg *    notice, this list of conditions and the following disclaimer
373125Sdg *    in the documentation and/or other materials provided with the
383125Sdg *    distribution.
39169857Sdds * 3. All advertising materials mentioning features or use of this
40169857Sdds *    software must display the following acknowledgement:
413125Sdg *      This product includes software developed by Powerdog Industries.
423125Sdg * 4. The name of Powerdog Industries may not be used to endorse or
43169857Sdds *    promote products derived from this software without specific prior
443125Sdg *    written permission.
45169857Sdds *
46169857Sdds * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
47169857Sdds * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48169857Sdds * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
49169857Sdds * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
503125Sdg * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
513125Sdg * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
523125Sdg * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
533125Sdg * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
543125Sdg * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
55169857Sdds * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
563125Sdg * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
57169857Sdds */
58169857Sdds
59169857Sdds#include <sys/cdefs.h>
60169857Sdds#ifndef lint
613125Sdg#ifndef NOID
623125Sdgstatic char copyright[] __unused =
633125Sdg"@(#) Copyright (c) 1994 Powerdog Industries.  All rights reserved.";
643125Sdgstatic char sccsid[] __unused = "@(#)strptime.c	0.1 (Powerdog) 94/03/27";
6599829Salfred#endif /* !defined NOID */
663125Sdg#endif /* not lint */
67169857Sdds__FBSDID("$FreeBSD: head/lib/libc/stdtime/strptime.c 227753 2011-11-20 14:45:42Z theraven $");
68169857Sdds
69169857Sdds#include "namespace.h"
70169857Sdds#include <time.h>
71169857Sdds#include <ctype.h>
72169857Sdds#include <errno.h>
73169857Sdds#include <stdlib.h>
743125Sdg#include <string.h>
7599829Salfred#include <pthread.h>
7699829Salfred#include "un-namespace.h"
7799829Salfred#include "libc_private.h"
7899829Salfred#include "timelocal.h"
7999829Salfred
803125Sdgstatic char * _strptime(const char *, const char *, struct tm *, int *, locale_t);
81169857Sdds
82169857Sdds#define asizeof(a)	(sizeof (a) / sizeof ((a)[0]))
83169857Sdds
843125Sdgstatic char *
8599829Salfred_strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp,
8699829Salfred		locale_t locale)
8799829Salfred{
8899829Salfred	char	c;
8999829Salfred	const char *ptr;
903125Sdg	int	i,
913125Sdg		len;
923125Sdg	int Ealternative, Oalternative;
933125Sdg	struct lc_time_T *tptr = __get_current_time_locale(locale);
943125Sdg
9599829Salfred	ptr = fmt;
963125Sdg	while (*ptr != 0) {
97169670Sdds		if (*buf == 0)
983125Sdg			break;
993125Sdg
1003125Sdg		c = *ptr++;
1013125Sdg
1023125Sdg		if (c != '%') {
1033125Sdg			if (isspace_l((unsigned char)c, locale))
1043125Sdg				while (*buf != 0 &&
1053125Sdg				       isspace_l((unsigned char)*buf, locale))
1063125Sdg					buf++;
1073125Sdg			else if (c != *buf++)
1083125Sdg				return 0;
1093125Sdg			continue;
1103125Sdg		}
111
112		Ealternative = 0;
113		Oalternative = 0;
114label:
115		c = *ptr++;
116		switch (c) {
117		case 0:
118		case '%':
119			if (*buf++ != '%')
120				return 0;
121			break;
122
123		case '+':
124			buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale);
125			if (buf == 0)
126				return 0;
127			break;
128
129		case 'C':
130			if (!isdigit_l((unsigned char)*buf, locale))
131				return 0;
132
133			/* XXX This will break for 3-digit centuries. */
134			len = 2;
135			for (i = 0; len && *buf != 0 &&
136			     isdigit_l((unsigned char)*buf, locale); buf++) {
137				i *= 10;
138				i += *buf - '0';
139				len--;
140			}
141			if (i < 19)
142				return 0;
143
144			tm->tm_year = i * 100 - 1900;
145			break;
146
147		case 'c':
148			buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale);
149			if (buf == 0)
150				return 0;
151			break;
152
153		case 'D':
154			buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale);
155			if (buf == 0)
156				return 0;
157			break;
158
159		case 'E':
160			if (Ealternative || Oalternative)
161				break;
162			Ealternative++;
163			goto label;
164
165		case 'O':
166			if (Ealternative || Oalternative)
167				break;
168			Oalternative++;
169			goto label;
170
171		case 'F':
172			buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale);
173			if (buf == 0)
174				return 0;
175			break;
176
177		case 'R':
178			buf = _strptime(buf, "%H:%M", tm, GMTp, locale);
179			if (buf == 0)
180				return 0;
181			break;
182
183		case 'r':
184			buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale);
185			if (buf == 0)
186				return 0;
187			break;
188
189		case 'T':
190			buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale);
191			if (buf == 0)
192				return 0;
193			break;
194
195		case 'X':
196			buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale);
197			if (buf == 0)
198				return 0;
199			break;
200
201		case 'x':
202			buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale);
203			if (buf == 0)
204				return 0;
205			break;
206
207		case 'j':
208			if (!isdigit_l((unsigned char)*buf, locale))
209				return 0;
210
211			len = 3;
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			if (i < 1 || i > 366)
219				return 0;
220
221			tm->tm_yday = i - 1;
222			break;
223
224		case 'M':
225		case 'S':
226			if (*buf == 0 ||
227				isspace_l((unsigned char)*buf, locale))
228				break;
229
230			if (!isdigit_l((unsigned char)*buf, locale))
231				return 0;
232
233			len = 2;
234			for (i = 0; len && *buf != 0 &&
235				isdigit_l((unsigned char)*buf, locale); buf++){
236				i *= 10;
237				i += *buf - '0';
238				len--;
239			}
240
241			if (c == 'M') {
242				if (i > 59)
243					return 0;
244				tm->tm_min = i;
245			} else {
246				if (i > 60)
247					return 0;
248				tm->tm_sec = i;
249			}
250
251			if (*buf != 0 &&
252				isspace_l((unsigned char)*buf, locale))
253				while (*ptr != 0 &&
254				       !isspace_l((unsigned char)*ptr, locale))
255					ptr++;
256			break;
257
258		case 'H':
259		case 'I':
260		case 'k':
261		case 'l':
262			/*
263			 * Of these, %l is the only specifier explicitly
264			 * documented as not being zero-padded.  However,
265			 * there is no harm in allowing zero-padding.
266			 *
267			 * XXX The %l specifier may gobble one too many
268			 * digits if used incorrectly.
269			 */
270			if (!isdigit_l((unsigned char)*buf, locale))
271				return 0;
272
273			len = 2;
274			for (i = 0; len && *buf != 0 &&
275			     isdigit_l((unsigned char)*buf, locale); buf++) {
276				i *= 10;
277				i += *buf - '0';
278				len--;
279			}
280			if (c == 'H' || c == 'k') {
281				if (i > 23)
282					return 0;
283			} else if (i > 12)
284				return 0;
285
286			tm->tm_hour = i;
287
288			if (*buf != 0 &&
289			    isspace_l((unsigned char)*buf, locale))
290				while (*ptr != 0 &&
291				       !isspace_l((unsigned char)*ptr, locale))
292					ptr++;
293			break;
294
295		case 'p':
296			/*
297			 * XXX This is bogus if parsed before hour-related
298			 * specifiers.
299			 */
300			len = strlen(tptr->am);
301			if (strncasecmp_l(buf, tptr->am, len, locale) == 0) {
302				if (tm->tm_hour > 12)
303					return 0;
304				if (tm->tm_hour == 12)
305					tm->tm_hour = 0;
306				buf += len;
307				break;
308			}
309
310			len = strlen(tptr->pm);
311			if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) {
312				if (tm->tm_hour > 12)
313					return 0;
314				if (tm->tm_hour != 12)
315					tm->tm_hour += 12;
316				buf += len;
317				break;
318			}
319
320			return 0;
321
322		case 'A':
323		case 'a':
324			for (i = 0; i < asizeof(tptr->weekday); i++) {
325				len = strlen(tptr->weekday[i]);
326				if (strncasecmp_l(buf, tptr->weekday[i],
327						len, locale) == 0)
328					break;
329				len = strlen(tptr->wday[i]);
330				if (strncasecmp_l(buf, tptr->wday[i],
331						len, locale) == 0)
332					break;
333			}
334			if (i == asizeof(tptr->weekday))
335				return 0;
336
337			tm->tm_wday = i;
338			buf += len;
339			break;
340
341		case 'U':
342		case 'W':
343			/*
344			 * XXX This is bogus, as we can not assume any valid
345			 * information present in the tm structure at this
346			 * point to calculate a real value, so just check the
347			 * range for now.
348			 */
349			if (!isdigit_l((unsigned char)*buf, locale))
350				return 0;
351
352			len = 2;
353			for (i = 0; len && *buf != 0 &&
354			     isdigit_l((unsigned char)*buf, locale); buf++) {
355				i *= 10;
356				i += *buf - '0';
357				len--;
358			}
359			if (i > 53)
360				return 0;
361
362			if (*buf != 0 &&
363			    isspace_l((unsigned char)*buf, locale))
364				while (*ptr != 0 &&
365				       !isspace_l((unsigned char)*ptr, locale))
366					ptr++;
367			break;
368
369		case 'w':
370			if (!isdigit_l((unsigned char)*buf, locale))
371				return 0;
372
373			i = *buf - '0';
374			if (i > 6)
375				return 0;
376
377			tm->tm_wday = i;
378
379			if (*buf != 0 &&
380			    isspace_l((unsigned char)*buf, locale))
381				while (*ptr != 0 &&
382				       !isspace_l((unsigned char)*ptr, locale))
383					ptr++;
384			break;
385
386		case 'd':
387		case 'e':
388			/*
389			 * The %e specifier is explicitly documented as not
390			 * being zero-padded but there is no harm in allowing
391			 * such padding.
392			 *
393			 * XXX The %e specifier may gobble one too many
394			 * digits if used incorrectly.
395			 */
396			if (!isdigit_l((unsigned char)*buf, locale))
397				return 0;
398
399			len = 2;
400			for (i = 0; len && *buf != 0 &&
401			     isdigit_l((unsigned char)*buf, locale); buf++) {
402				i *= 10;
403				i += *buf - '0';
404				len--;
405			}
406			if (i > 31)
407				return 0;
408
409			tm->tm_mday = i;
410
411			if (*buf != 0 &&
412			    isspace_l((unsigned char)*buf, locale))
413				while (*ptr != 0 &&
414				       !isspace_l((unsigned char)*ptr, locale))
415					ptr++;
416			break;
417
418		case 'B':
419		case 'b':
420		case 'h':
421			for (i = 0; i < asizeof(tptr->month); i++) {
422				if (Oalternative) {
423					if (c == 'B') {
424						len = strlen(tptr->alt_month[i]);
425						if (strncasecmp_l(buf,
426								tptr->alt_month[i],
427								len, locale) == 0)
428							break;
429					}
430				} else {
431					len = strlen(tptr->month[i]);
432					if (strncasecmp_l(buf, tptr->month[i],
433							len, locale) == 0)
434						break;
435				}
436			}
437			/*
438			 * Try the abbreviated month name if the full name
439			 * wasn't found and Oalternative was not requested.
440			 */
441			if (i == asizeof(tptr->month) && !Oalternative) {
442				for (i = 0; i < asizeof(tptr->month); i++) {
443					len = strlen(tptr->mon[i]);
444					if (strncasecmp_l(buf, tptr->mon[i],
445							len, locale) == 0)
446						break;
447				}
448			}
449			if (i == asizeof(tptr->month))
450				return 0;
451
452			tm->tm_mon = i;
453			buf += len;
454			break;
455
456		case 'm':
457			if (!isdigit_l((unsigned char)*buf, locale))
458				return 0;
459
460			len = 2;
461			for (i = 0; len && *buf != 0 &&
462			     isdigit_l((unsigned char)*buf, locale); buf++) {
463				i *= 10;
464				i += *buf - '0';
465				len--;
466			}
467			if (i < 1 || i > 12)
468				return 0;
469
470			tm->tm_mon = i - 1;
471
472			if (*buf != 0 &&
473			    isspace_l((unsigned char)*buf, locale))
474				while (*ptr != 0 &&
475				       !isspace_l((unsigned char)*ptr, locale))
476					ptr++;
477			break;
478
479		case 's':
480			{
481			char *cp;
482			int sverrno;
483			long n;
484			time_t t;
485
486			sverrno = errno;
487			errno = 0;
488			n = strtol_l(buf, &cp, 10, locale);
489			if (errno == ERANGE || (long)(t = n) != n) {
490				errno = sverrno;
491				return 0;
492			}
493			errno = sverrno;
494			buf = cp;
495			gmtime_r(&t, tm);
496			*GMTp = 1;
497			}
498			break;
499
500		case 'Y':
501		case 'y':
502			if (*buf == 0 ||
503			    isspace_l((unsigned char)*buf, locale))
504				break;
505
506			if (!isdigit_l((unsigned char)*buf, locale))
507				return 0;
508
509			len = (c == 'Y') ? 4 : 2;
510			for (i = 0; len && *buf != 0 &&
511			     isdigit_l((unsigned char)*buf, locale); buf++) {
512				i *= 10;
513				i += *buf - '0';
514				len--;
515			}
516			if (c == 'Y')
517				i -= 1900;
518			if (c == 'y' && i < 69)
519				i += 100;
520			if (i < 0)
521				return 0;
522
523			tm->tm_year = i;
524
525			if (*buf != 0 &&
526			    isspace_l((unsigned char)*buf, locale))
527				while (*ptr != 0 &&
528				       !isspace_l((unsigned char)*ptr, locale))
529					ptr++;
530			break;
531
532		case 'Z':
533			{
534			const char *cp;
535			char *zonestr;
536
537			for (cp = buf; *cp &&
538			     isupper_l((unsigned char)*cp, locale); ++cp) {
539				/*empty*/}
540			if (cp - buf) {
541				zonestr = alloca(cp - buf + 1);
542				strncpy(zonestr, buf, cp - buf);
543				zonestr[cp - buf] = '\0';
544				tzset();
545				if (0 == strcmp(zonestr, "GMT")) {
546				    *GMTp = 1;
547				} else if (0 == strcmp(zonestr, tzname[0])) {
548				    tm->tm_isdst = 0;
549				} else if (0 == strcmp(zonestr, tzname[1])) {
550				    tm->tm_isdst = 1;
551				} else {
552				    return 0;
553				}
554				buf += cp - buf;
555			}
556			}
557			break;
558
559		case 'z':
560			{
561			int sign = 1;
562
563			if (*buf != '+') {
564				if (*buf == '-')
565					sign = -1;
566				else
567					return 0;
568			}
569
570			buf++;
571			i = 0;
572			for (len = 4; len > 0; len--) {
573				if (isdigit_l((unsigned char)*buf, locale)) {
574					i *= 10;
575					i += *buf - '0';
576					buf++;
577				} else
578					return 0;
579			}
580
581			tm->tm_hour -= sign * (i / 100);
582			tm->tm_min  -= sign * (i % 100);
583			*GMTp = 1;
584			}
585			break;
586		}
587	}
588	return (char *)buf;
589}
590
591
592char *
593strptime_l(const char * __restrict buf, const char * __restrict fmt,
594    struct tm * __restrict tm, locale_t loc)
595{
596	char *ret;
597	int gmt;
598	FIX_LOCALE(loc);
599
600	gmt = 0;
601	ret = _strptime(buf, fmt, tm, &gmt, loc);
602	if (ret && gmt) {
603		time_t t = timegm(tm);
604		localtime_r(&t, tm);
605	}
606
607	return (ret);
608}
609char *
610strptime(const char * __restrict buf, const char * __restrict fmt,
611    struct tm * __restrict tm)
612{
613	return strptime_l(buf, fmt, tm, __get_locale());
614}
615