• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   strptime implementation
3 
4   Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>
5   This program and the accompanying materials are licensed and made available under
6   the terms and conditions of the BSD License that accompanies this distribution.
7   The full text of the license may be found at
8   http://opensource.org/licenses/bsd-license.php.
9 
10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13  * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.
14  * All rights reserved.
15  *
16  * This code was contributed to The NetBSD Foundation by Klaus Klein.
17  * Heavily optimised by David Laight
18  *
19  * Redistribution and use in source and binary forms, with or without
20  * modification, are permitted provided that the following conditions
21  * are met:
22  * 1. Redistributions of source code must retain the above copyright
23  *    notice, this list of conditions and the following disclaimer.
24  * 2. Redistributions in binary form must reproduce the above copyright
25  *    notice, this list of conditions and the following disclaimer in the
26  *    documentation and/or other materials provided with the distribution.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
29  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
32  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38  * POSSIBILITY OF SUCH DAMAGE.
39 
40   $NetBSD: strptime.c,v 1.28 2008/04/28 20:23:01 martin Exp $
41 
42 **/
43 
44 #if defined(_MSC_VER)           /* Handle Microsoft VC++ compiler specifics. */
45   #pragma warning ( disable : 4244 )
46   #pragma warning ( disable : 4018 )
47 #endif
48 
49 #include <LibConfig.h>
50 
51 #include <sys/cdefs.h>
52 
53 #include "namespace.h"
54 #include <time.h>
55 #include "tzfile.h"
56 #include <TimeVals.h>
57 #include <fcntl.h>
58 
59 #include <sys/localedef.h>
60 
61 #include <ctype.h>
62 #include <locale.h>
63 #include <string.h>
64 
65 #ifdef __weak_alias
66 __weak_alias(strptime,_strptime)
67 #endif
68 
69 #define	_ctloc(x)		(_CurrentTimeLocale->x)
70 
71 /*
72  * We do not implement alternate representations. However, we always
73  * check whether a given modifier is allowed for a certain conversion.
74  */
75 #define ALT_E			0x01
76 #define ALT_O			0x02
77 #define	LEGAL_ALT(x)		{ if (alt_format & ~(x)) return NULL; }
78 
79 static const unsigned char *conv_num(const unsigned char *, int *, unsigned int, unsigned int);
80 static const unsigned char *find_string(const unsigned char *, int *, const char * const *,
81 	const char * const *, int);
82 
83 
84 char *
strptime(const char * buf,const char * fmt,struct tm * tm)85 strptime(const char *buf, const char *fmt, struct tm *tm)
86 {
87 	unsigned char c;
88 	const unsigned char *bp;
89 	int alt_format, i, split_year = 0;
90 	const char *new_fmt;
91 
92 	bp = (const unsigned char *)buf;
93 
94 	while (bp != NULL && (c = *fmt++) != '\0') {
95 		/* Clear `alternate' modifier prior to new conversion. */
96 		alt_format = 0;
97 		i = 0;
98 
99 		/* Eat up white-space. */
100 		if (isspace(c)) {
101 			while (isspace(*bp))
102 				bp++;
103 			continue;
104 		}
105 
106 		if (c != '%')
107 			goto literal;
108 
109 
110 again:		switch (c = *fmt++) {
111 		case '%':	/* "%%" is converted to "%". */
112 literal:
113 			if (c != *bp++)
114 				return NULL;
115 			LEGAL_ALT(0);
116 			continue;
117 
118 		/*
119 		 * "Alternative" modifiers. Just set the appropriate flag
120 		 * and start over again.
121 		 */
122 		case 'E':	/* "%E?" alternative conversion modifier. */
123 			LEGAL_ALT(0);
124 			alt_format |= ALT_E;
125 			goto again;
126 
127 		case 'O':	/* "%O?" alternative conversion modifier. */
128 			LEGAL_ALT(0);
129 			alt_format |= ALT_O;
130 			goto again;
131 
132 		/*
133 		 * "Complex" conversion rules, implemented through recursion.
134 		 */
135 		case 'c':	/* Date and time, using the locale's format. */
136 			new_fmt = _ctloc(d_t_fmt);
137 			goto recurse;
138 
139 		case 'D':	/* The date as "%m/%d/%y". */
140 			new_fmt = "%m/%d/%y";
141 			LEGAL_ALT(0);
142 			goto recurse;
143 
144 		case 'F':	/* The date as "%Y-%m-%d". */
145 			new_fmt = "%Y-%m-%d";
146 			LEGAL_ALT(0);
147 			goto recurse;
148 
149 		case 'R':	/* The time as "%H:%M". */
150 			new_fmt = "%H:%M";
151 			LEGAL_ALT(0);
152 			goto recurse;
153 
154 		case 'r':	/* The time in 12-hour clock representation. */
155 			new_fmt =_ctloc(t_fmt_ampm);
156 			LEGAL_ALT(0);
157 			goto recurse;
158 
159 		case 'T':	/* The time as "%H:%M:%S". */
160 			new_fmt = "%H:%M:%S";
161 			LEGAL_ALT(0);
162 			goto recurse;
163 
164 		case 'X':	/* The time, using the locale's format. */
165 			new_fmt =_ctloc(t_fmt);
166 			goto recurse;
167 
168 		case 'x':	/* The date, using the locale's format. */
169 			new_fmt =_ctloc(d_fmt);
170 		    recurse:
171 			bp = (const unsigned char *)strptime((const char *)bp,
172 							    new_fmt, tm);
173 			LEGAL_ALT(ALT_E);
174 			continue;
175 
176 		/*
177 		 * "Elementary" conversion rules.
178 		 */
179 		case 'A':	/* The day of week, using the locale's form. */
180 		case 'a':
181 			bp = find_string(bp, &tm->tm_wday, _ctloc(day),
182 					_ctloc(abday), 7);
183 			LEGAL_ALT(0);
184 			continue;
185 
186 		case 'B':	/* The month, using the locale's form. */
187 		case 'b':
188 		case 'h':
189 			bp = find_string(bp, &tm->tm_mon, _ctloc(mon),
190 					_ctloc(abmon), 12);
191 			LEGAL_ALT(0);
192 			continue;
193 
194 		case 'C':	/* The century number. */
195 			i = 20;
196 			bp = conv_num(bp, &i, 0, 99);
197 
198 			i = i * 100 - TM_YEAR_BASE;
199 			if (split_year)
200 				i += tm->tm_year % 100;
201 			split_year = 1;
202 			tm->tm_year = i;
203 			LEGAL_ALT(ALT_E);
204 			continue;
205 
206 		case 'd':	/* The day of month. */
207 		case 'e':
208 			bp = conv_num(bp, &tm->tm_mday, 1, 31);
209 			LEGAL_ALT(ALT_O);
210 			continue;
211 
212 		case 'k':	/* The hour (24-hour clock representation). */
213 			LEGAL_ALT(0);
214 			/* FALLTHROUGH */
215 		case 'H':
216 			bp = conv_num(bp, &tm->tm_hour, 0, 23);
217 			LEGAL_ALT(ALT_O);
218 			continue;
219 
220 		case 'l':	/* The hour (12-hour clock representation). */
221 			LEGAL_ALT(0);
222 			/* FALLTHROUGH */
223 		case 'I':
224 			bp = conv_num(bp, &tm->tm_hour, 1, 12);
225 			if (tm->tm_hour == 12)
226 				tm->tm_hour = 0;
227 			LEGAL_ALT(ALT_O);
228 			continue;
229 
230 		case 'j':	/* The day of year. */
231 			i = 1;
232 			bp = conv_num(bp, &i, 1, 366);
233 			tm->tm_yday = i - 1;
234 			LEGAL_ALT(0);
235 			continue;
236 
237 		case 'M':	/* The minute. */
238 			bp = conv_num(bp, &tm->tm_min, 0, 59);
239 			LEGAL_ALT(ALT_O);
240 			continue;
241 
242 		case 'm':	/* The month. */
243 			i = 1;
244 			bp = conv_num(bp, &i, 1, 12);
245 			tm->tm_mon = i - 1;
246 			LEGAL_ALT(ALT_O);
247 			continue;
248 
249 		case 'p':	/* The locale's equivalent of AM/PM. */
250 			bp = find_string(bp, &i, _ctloc(am_pm), NULL, 2);
251 			if (tm->tm_hour > 11)
252 				return NULL;
253 			tm->tm_hour += i * 12;
254 			LEGAL_ALT(0);
255 			continue;
256 
257 		case 'S':	/* The seconds. */
258 			bp = conv_num(bp, &tm->tm_sec, 0, 61);
259 			LEGAL_ALT(ALT_O);
260 			continue;
261 
262 		case 'U':	/* The week of year, beginning on sunday. */
263 		case 'W':	/* The week of year, beginning on monday. */
264 			/*
265 			 * XXX This is bogus, as we can not assume any valid
266 			 * information present in the tm structure at this
267 			 * point to calculate a real value, so just check the
268 			 * range for now.
269 			 */
270 			 bp = conv_num(bp, &i, 0, 53);
271 			 LEGAL_ALT(ALT_O);
272 			 continue;
273 
274 		case 'w':	/* The day of week, beginning on sunday. */
275 			bp = conv_num(bp, &tm->tm_wday, 0, 6);
276 			LEGAL_ALT(ALT_O);
277 			continue;
278 
279 		case 'Y':	/* The year. */
280 			i = TM_YEAR_BASE;	/* just for data sanity... */
281 			bp = conv_num(bp, &i, 0, 9999);
282 			tm->tm_year = i - TM_YEAR_BASE;
283 			LEGAL_ALT(ALT_E);
284 			continue;
285 
286 		case 'y':	/* The year within 100 years of the epoch. */
287 			/* LEGAL_ALT(ALT_E | ALT_O); */
288 			bp = conv_num(bp, &i, 0, 99);
289 
290 			if (split_year)
291 				/* preserve century */
292 				i += (tm->tm_year / 100) * 100;
293 			else {
294 				split_year = 1;
295 				if (i <= 68)
296 					i = i + 2000 - TM_YEAR_BASE;
297 				else
298 					i = i + 1900 - TM_YEAR_BASE;
299 			}
300 			tm->tm_year = i;
301 			continue;
302 
303 		case 'Z':
304 			tzset();
305 			if (strncmp((const char *)bp, gmt, 3) == 0) {
306 				tm->tm_isdst = 0;
307 #ifdef TM_GMTOFF
308 				tm->TM_GMTOFF = 0;
309 #endif
310 #ifdef TM_ZONE
311 				tm->TM_ZONE = gmt;
312 #endif
313 				bp += 3;
314 			} else {
315 				const unsigned char *ep;
316 
317 				ep = find_string(bp, &i,
318 					       	 (const char * const *)tzname,
319 					       	  NULL, 2);
320 				if (ep != NULL) {
321 					tm->tm_isdst = i;
322 #ifdef TM_GMTOFF
323 					tm->TM_GMTOFF = -(timezone);
324 #endif
325 #ifdef TM_ZONE
326 					tm->TM_ZONE = tzname[i];
327 #endif
328 				}
329 				bp = ep;
330 			}
331 			continue;
332 
333 		/*
334 		 * Miscellaneous conversions.
335 		 */
336 		case 'n':	/* Any kind of white-space. */
337 		case 't':
338 			while (isspace(*bp))
339 				bp++;
340 			LEGAL_ALT(0);
341 			continue;
342 
343 
344 		default:	/* Unknown/unsupported conversion. */
345 			return NULL;
346 		}
347 	}
348 
349 	return __UNCONST(bp);
350 }
351 
352 
353 static const unsigned char *
conv_num(const unsigned char * buf,int * dest,unsigned int llim,unsigned int ulim)354 conv_num(const unsigned char *buf, int *dest, unsigned int llim, unsigned int ulim)
355 {
356 	unsigned int result = 0;
357 	unsigned char ch;
358 
359 	/* The limit also determines the number of valid digits. */
360 	unsigned int rulim = ulim;
361 
362 	ch = *buf;
363 	if (ch < '0' || ch > '9')
364 		return NULL;
365 
366 	do {
367 		result *= 10;
368 		result += ch - '0';
369 		rulim /= 10;
370 		ch = *++buf;
371 	} while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9');
372 
373 	if (result < llim || result > ulim)
374 		return NULL;
375 
376 	*dest = result;
377 	return buf;
378 }
379 
380 static const unsigned char *
find_string(const unsigned char * bp,int * tgt,const char * const * n1,const char * const * n2,int c)381 find_string(const unsigned char *bp, int *tgt, const char * const *n1,
382 		const char * const *n2, int c)
383 {
384 	int i;
385 	size_t len;
386 
387 	/* check full name - then abbreviated ones */
388 	for (; n1 != NULL; n1 = n2, n2 = NULL) {
389 		for (i = 0; i < c; i++, n1++) {
390 			len = strlen(*n1);
391 			if (strncasecmp(*n1, (const char *)bp, len) == 0) {
392 				*tgt = i;
393 				return bp + len;
394 			}
395 		}
396 	}
397 
398 	/* Nothing matched */
399 	return NULL;
400 }
401