Home | History | Annotate | Download | only in Time
      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 *
     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 *
    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 *
    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