1 /* Convert a string representation of time to a time value.
2 Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
5 
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
10 
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 Library General Public License for more details.
15 
16 You should have received a copy of the GNU Library General Public
17 License along with the GNU C Library; see the file COPYING.LIB.  If not,
18 write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301 USA.  */
20 
21 /* XXX This version of the implementation is not really complete.
22 Some of the fields cannot add information alone.  But if seeing
23 some of them in the same format (such as year, week and weekday)
24 this is enough information for determining the date.  */
25 
26 #ifdef HAVE_CONFIG_H
27 # include <config.h>
28 #endif
29 
30 #include <ctype.h>
31 //#include <langinfo.h>
32 #include <limits.h>
33 #include <string.h>
34 #include <time.h>
35 
36 // ABI EXPORT
37 #include "ut_export.h"
38 
UT_strncasecmp(const char * s1,const char * s2,size_t n)39 static int UT_strncasecmp (const char *s1, const char *s2, size_t n)
40 {
41 	int ret;
42 
43 	if (n == 0)
44 		return 0;
45 
46 	while ((ret = ((unsigned char) tolower (*s1) - (unsigned char) tolower (*s2))) == 0 && *s1++)
47 	{
48 		if (--n == 0)
49 			return 0;
50 		++s2;
51 	}
52 	return ret;
53 }
54 
55 #ifdef _LIBC
56 # include "../locale/localeinfo.h"
57 #endif
58 
59 
60 #ifndef __P
61 # if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
62 #  define __P(args) args
63 # else
64 #  define __P(args) ()
65 # endif  /* GCC.  */
66 #endif  /* Not __P.  */
67 
68 
69 #if ! HAVE_LOCALTIME_R && ! defined localtime_r
70 # ifdef _LIBC
71 #  define localtime_r __localtime_r
72 # else
73 /* Approximate localtime_r as best we can in its absence.  */
74 #  define localtime_r my_localtime_r
75 static struct tm *localtime_r (const time_t *, struct tm *);
localtime_r(const time_t * t,struct tm * tp)76 static struct tm * localtime_r (const time_t *t, struct tm *tp)
77 {
78 	struct tm *l = localtime (t);
79 	if (! l)
80 		return 0;
81 	*tp = *l;
82 	return tp;
83 }
84 # endif /* ! _LIBC */
85 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
86 
87 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
88 #if defined __GNUC__ && __GNUC__ >= 2
89 # define match_string(cs1, s2) \
90 	({ size_t len = strlen (cs1);						      \
91 	int result = UT_strncasecmp ((cs1), (s2), len) == 0;			      \
92 	if (result) (s2) += len;						      \
93 	result; })
94 #else
95 /* Oh come on.  Get a reasonable compiler.  */
96 # define match_string(cs1, s2) \
97 	(UT_strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
98 #endif
99 /* We intentionally do not use isdigit() for testing because this will
100 lead to problems with the wide character version.  */
101 #define get_number(from, to, n) \
102 	do {									      \
103 	int __n = n;							      \
104 	val = 0;								      \
105 	while (*rp == ' ')							      \
106 	++rp;								      \
107 	if (*rp < '0' || *rp > '9')						      \
108 	return NULL;							      \
109 	do {								      \
110 	val *= 10;							      \
111 	val += *rp++ - '0';						      \
112 	} while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9');	      \
113 	if (val < from || val > to)						      \
114 	return NULL;							      \
115 	} while (0)
116 #ifdef _NL_CURRENT
117 # define get_alt_number(from, to, n) \
118 	({									      \
119 	__label__ do_normal;						      \
120 	if (*decided != raw)						      \
121 {									      \
122 	const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS);		      \
123 	int __n = n;							      \
124 	int any = 0;							      \
125 	while (*rp == ' ')						      \
126 	++rp;								      \
127 	val = 0;							      \
128 	do {								      \
129 	val *= 10;							      \
130 	while (*alts != '\0')						      \
131 {								      \
132 	size_t len = strlen (alts);				      \
133 	if (UT_strncasecmp (alts, rp, len) == 0)			      \
134 	break;							      \
135 	alts += len + 1;						      \
136 	++val;							      \
137 }								      \
138 	if (*alts == '\0')						      \
139 {								      \
140 	if (*decided == not && ! any)				      \
141 	goto do_normal;						      \
142 	/* If we haven't read anything it's an error.  */		      \
143 	if (! any)						      \
144 	return NULL;						      \
145 	/* Correct the premature multiplication.  */		      \
146 	val /= 10;						      \
147 	break;							      \
148 }								      \
149 	  else								      \
150 	  *decided = loc;						      \
151 	} while (--__n > 0 && val * 10 <= to);				      \
152 	if (val < from || val > to)					      \
153 	return NULL;							      \
154 }									      \
155 	else								      \
156 {									      \
157 do_normal:							      \
158 	get_number (from, to, n);					      \
159 }									      \
160 	0;									      \
161 })
162 #else
163 # define get_alt_number(from, to, n) \
164 	/* We don't have the alternate representation.  */			      \
165 	get_number(from, to, n)
166 #endif
167 #define recursive(new_fmt) \
168 	(*(new_fmt) != '\0'							      \
169 	&& (rp = UT_strptime_internal (rp, (new_fmt), tm, decided)) != NULL)
170 
171 
172 #ifdef _LIBC
173 /* This is defined in locale/C-time.c in the GNU libc.  */
174 extern const struct locale_data _nl_C_LC_TIME;
175 extern const unsigned short int __mon_yday[2][13];
176 
177 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
178 # define ab_weekday_name \
179 	(&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
180 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
181 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
182 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
183 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
184 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
185 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
186 # define HERE_T_FMT_AMPM \
187 	(_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
188 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
189 
190 # define UT_strncasecmp(s1, s2, n) __UT_strncasecmp (s1, s2, n)
191 #else
192 static char const weekday_name[][10] =
193 {
194 	"Sunday", "Monday", "Tuesday", "Wednesday",
195 	"Thursday", "Friday", "Saturday"
196 };
197 static char const ab_weekday_name[][4] =
198 {
199 	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
200 };
201 static char const month_name[][10] =
202 {
203 	"January", "February", "March", "April", "May", "June",
204 	"July", "August", "September", "October", "November", "December"
205 };
206 static char const ab_month_name[][4] =
207 {
208 	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
209 	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
210 };
211 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
212 # define HERE_D_FMT "%m/%d/%y"
213 # define HERE_AM_STR "AM"
214 # define HERE_PM_STR "PM"
215 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
216 # define HERE_T_FMT "%H:%M:%S"
217 
218 const unsigned short int __mon_yday[13][13] =
219 {
220 	/* Normal years.  */
221 	{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
222 	/* Leap years.  */
223 	{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
224 };
225 #endif
226 
227 /* Status of lookup: do we use the locale data or the raw data?  */
228 enum locale_status { not, loc, raw };
229 
230 
231 #ifndef __isleap
232 /* Nonzero if YEAR is a leap year (every 4 years,
233 except every 100th isn't, and every 400th is).  */
234 # define __isleap(year)	\
235 	((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
236 #endif
237 
238 /* Compute the day of the week.  */
239 static void
day_of_the_week(struct tm * tm)240 day_of_the_week (struct tm *tm)
241 {
242 	/* We know that January 1st 1970 was a Thursday (= 4).  Compute the
243 	the difference between this data in the one on TM and so determine
244 	the weekday.  */
245 	int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
246 	int wday = (-473
247 		+ (365 * (tm->tm_year - 70))
248 		+ (corr_year / 4)
249 		- ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
250 		+ (((corr_year / 4) / 25) / 4)
251 		+ __mon_yday[0][tm->tm_mon]
252 	+ tm->tm_mday - 1);
253 	tm->tm_wday = wday % 7;
254 }
255 
256 /* Compute the day of the year.  */
257 static void
day_of_the_year(struct tm * tm)258 day_of_the_year (struct tm *tm)
259 {
260 	tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
261 	+ (tm->tm_mday - 1));
262 }
263 
264 static char *
265 #ifdef _LIBC
266 internal_function
267 #endif
268 UT_strptime_internal (const char *buf, const char *format, struct tm *tm, enum locale_status *decided);
269 
270 static char *
271 #ifdef _LIBC
272 internal_function
273 #endif
UT_strptime_internal(const char * buf,const char * format,struct tm * tm,enum locale_status * decided)274 UT_strptime_internal (const char *buf, const char *format, struct tm *tm, enum locale_status *decided)
275 {
276 	const char *rp;
277 	const char *fmt;
278 	int cnt;
279 	size_t val;
280 	int have_I, is_pm;
281 	int century, want_century;
282 	int have_wday, want_xday;
283 	int have_yday;
284 	int have_mon, have_mday;
285 
286 	rp = buf;
287 	fmt = format;
288 	have_I = is_pm = 0;
289 	century = -1;
290 	want_century = 0;
291 	have_wday = want_xday = have_yday = have_mon = have_mday = 0;
292 
293 	while (*fmt != '\0')
294 	{
295 		/* A white space in the format string matches 0 more or white
296 		space in the input string.  */
297 		if (isspace (*fmt))
298 		{
299 			while (isspace (*rp))
300 				++rp;
301 			++fmt;
302 			continue;
303 		}
304 
305 		/* Any character but `%' must be matched by the same character
306 		in the iput string.  */
307 		if (*fmt != '%')
308 		{
309 			match_char (*fmt++, *rp++);
310 			continue;
311 		}
312 
313 		++fmt;
314 #ifndef _NL_CURRENT
315 		/* We need this for handling the `E' modifier.  */
316 start_over:
317 #endif
318 		switch (*fmt++)
319 		{
320 		case '%':
321 			/* Match the `%' character itself.  */
322 			match_char ('%', *rp++);
323 			break;
324 		case 'a':
325 		case 'A':
326 			/* Match day of week.  */
327 			for (cnt = 0; cnt < 7; ++cnt)
328 			{
329 #ifdef _NL_CURRENT
330 				if (*decided !=raw)
331 				{
332 					if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
333 					{
334 						if (*decided == not
335 							&& strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
336 							weekday_name[cnt]))
337 							*decided = loc;
338 						break;
339 					}
340 					if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
341 					{
342 						if (*decided == not
343 							&& strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
344 							ab_weekday_name[cnt]))
345 							*decided = loc;
346 						break;
347 					}
348 				}
349 #endif
350 				if (*decided != loc
351 					&& (match_string (weekday_name[cnt], rp)
352 					|| match_string (ab_weekday_name[cnt], rp)))
353 				{
354 					*decided = raw;
355 					break;
356 				}
357 			}
358 			if (cnt == 7)
359 				/* Does not match a weekday name.  */
360 				return NULL;
361 			tm->tm_wday = cnt;
362 			have_wday = 1;
363 			break;
364 		case 'b':
365 		case 'B':
366 		case 'h':
367 			/* Match month name.  */
368 			for (cnt = 0; cnt < 12; ++cnt)
369 			{
370 #ifdef _NL_CURRENT
371 				if (*decided !=raw)
372 				{
373 					if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
374 					{
375 						if (*decided == not
376 							&& strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
377 							month_name[cnt]))
378 							*decided = loc;
379 						break;
380 					}
381 					if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
382 					{
383 						if (*decided == not
384 							&& strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
385 							ab_month_name[cnt]))
386 							*decided = loc;
387 						break;
388 					}
389 				}
390 #endif
391 				if (match_string (month_name[cnt], rp)
392 					|| match_string (ab_month_name[cnt], rp))
393 				{
394 					*decided = raw;
395 					break;
396 				}
397 			}
398 			if (cnt == 12)
399 				/* Does not match a month name.  */
400 				return NULL;
401 			tm->tm_mon = cnt;
402 			want_xday = 1;
403 			break;
404 		case 'c':
405 			/* Match locale's date and time format.  */
406 #ifdef _NL_CURRENT
407 			if (*decided != raw)
408 			{
409 				if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
410 				{
411 					if (*decided == loc)
412 						return NULL;
413 				}
414 				else
415 				{
416 					if (*decided == not &&
417 						strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
418 						*decided = loc;
419 					want_xday = 1;
420 					break;
421 				}
422 				*decided = raw;
423 			}
424 #endif
425 			if (!recursive (HERE_D_T_FMT))
426 				return NULL;
427 			want_xday = 1;
428 			break;
429 		case 'C':
430 			/* Match century number.  */
431 			get_number (0, 99, 2);
432 			century = val;
433 			want_xday = 1;
434 			break;
435 		case 'd':
436 		case 'e':
437 			/* Match day of month.  */
438 			get_number (1, 31, 2);
439 			tm->tm_mday = val;
440 			have_mday = 1;
441 			want_xday = 1;
442 			break;
443 		case 'F':
444 			if (!recursive ("%Y-%m-%d"))
445 				return NULL;
446 			want_xday = 1;
447 			break;
448 		case 'x':
449 #ifdef _NL_CURRENT
450 			if (*decided != raw)
451 			{
452 				if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
453 				{
454 					if (*decided == loc)
455 						return NULL;
456 				}
457 				else
458 				{
459 					if (decided == not
460 						&& strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
461 						*decided = loc;
462 					want_xday = 1;
463 					break;
464 				}
465 				*decided = raw;
466 			}
467 #endif
468 			/* Fall through.  */
469 		case 'D':
470 			/* Match standard day format.  */
471 			if (!recursive (HERE_D_FMT))
472 				return NULL;
473 			want_xday = 1;
474 			break;
475 		case 'k':
476 		case 'H':
477 			/* Match hour in 24-hour clock.  */
478 			get_number (0, 23, 2);
479 			tm->tm_hour = val;
480 			have_I = 0;
481 			break;
482 		case 'I':
483 			/* Match hour in 12-hour clock.  */
484 			get_number (1, 12, 2);
485 			tm->tm_hour = val % 12;
486 			have_I = 1;
487 			break;
488 		case 'j':
489 			/* Match day number of year.  */
490 			get_number (1, 366, 3);
491 			tm->tm_yday = val - 1;
492 			have_yday = 1;
493 			break;
494 		case 'm':
495 			/* Match number of month.  */
496 			get_number (1, 12, 2);
497 			tm->tm_mon = val - 1;
498 			have_mon = 1;
499 			want_xday = 1;
500 			break;
501 		case 'M':
502 			/* Match minute.  */
503 			get_number (0, 59, 2);
504 			tm->tm_min = val;
505 			break;
506 		case 'n':
507 		case 't':
508 			/* Match any white space.  */
509 			while (isspace (*rp))
510 				++rp;
511 			break;
512 		case 'p':
513 			/* Match locale's equivalent of AM/PM.  */
514 #ifdef _NL_CURRENT
515 			if (*decided != raw)
516 			{
517 				if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
518 				{
519 					if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
520 						*decided = loc;
521 					break;
522 				}
523 				if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
524 				{
525 					if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
526 						*decided = loc;
527 					is_pm = 1;
528 					break;
529 				}
530 				*decided = raw;
531 			}
532 #endif
533 			if (!match_string (HERE_AM_STR, rp))
534 			{
535 				if (match_string (HERE_PM_STR, rp))
536 					is_pm = 1;
537 				else
538 					return NULL;
539 			}
540 			break;
541 		case 'r':
542 #ifdef _NL_CURRENT
543 			if (*decided != raw)
544 			{
545 				if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
546 				{
547 					if (*decided == loc)
548 						return NULL;
549 				}
550 				else
551 				{
552 					if (*decided == not &&
553 						strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
554 						HERE_T_FMT_AMPM))
555 						*decided = loc;
556 					break;
557 				}
558 				*decided = raw;
559 			}
560 #endif
561 			if (!recursive (HERE_T_FMT_AMPM))
562 				return NULL;
563 			break;
564 		case 'R':
565 			if (!recursive ("%H:%M"))
566 				return NULL;
567 			break;
568 		case 's':
569 			{
570 				/* The number of seconds may be very high so we cannot use
571 				the `get_number' macro.  Instead read the number
572 				character for character and construct the result while
573 				doing this.  */
574 				time_t secs = 0;
575 				if (*rp < '0' || *rp > '9')
576 					/* We need at least one digit.  */
577 					return NULL;
578 
579 				do
580 				{
581 					secs *= 10;
582 					secs += *rp++ - '0';
583 				}
584 				while (*rp >= '0' && *rp <= '9');
585 
586 				if (localtime_r (&secs, tm) == NULL)
587 					/* Error in function.  */
588 					return NULL;
589 			}
590 			break;
591 		case 'S':
592 			get_number (0, 61, 2);
593 			tm->tm_sec = val;
594 			break;
595 		case 'X':
596 #ifdef _NL_CURRENT
597 			if (*decided != raw)
598 			{
599 				if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
600 				{
601 					if (*decided == loc)
602 						return NULL;
603 				}
604 				else
605 				{
606 					if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
607 						*decided = loc;
608 					break;
609 				}
610 				*decided = raw;
611 			}
612 #endif
613 			/* Fall through.  */
614 		case 'T':
615 			if (!recursive (HERE_T_FMT))
616 				return NULL;
617 			break;
618 		case 'u':
619 			get_number (1, 7, 1);
620 			tm->tm_wday = val % 7;
621 			have_wday = 1;
622 			break;
623 		case 'g':
624 			get_number (0, 99, 2);
625 			/* XXX This cannot determine any field in TM.  */
626 			break;
627 		case 'G':
628 			if (*rp < '0' || *rp > '9')
629 				return NULL;
630 			/* XXX Ignore the number since we would need some more
631 			information to compute a real date.  */
632 			do
633 			++rp;
634 			while (*rp >= '0' && *rp <= '9');
635 			break;
636 		case 'U':
637 		case 'V':
638 		case 'W':
639 			get_number (0, 53, 2);
640 			/* XXX This cannot determine any field in TM without some
641 			information.  */
642 			break;
643 		case 'w':
644 			/* Match number of weekday.  */
645 			get_number (0, 6, 1);
646 			tm->tm_wday = val;
647 			have_wday = 1;
648 			break;
649 		case 'y':
650 			/* Match year within century.  */
651 			get_number (0, 99, 2);
652 			/* The "Year 2000: The Millennium Rollover" paper suggests that
653 			values in the range 69-99 refer to the twentieth century.  */
654 			tm->tm_year = val >= 69 ? val : val + 100;
655 			/* Indicate that we want to use the century, if specified.  */
656 			want_century = 1;
657 			want_xday = 1;
658 			break;
659 		case 'Y':
660 			/* Match year including century number.  */
661 			get_number (0, 9999, 4);
662 			tm->tm_year = val - 1900;
663 			want_century = 0;
664 			want_xday = 1;
665 			break;
666 		case 'Z':
667 			/* XXX How to handle this?  */
668 			break;
669 		case 'E':
670 #ifdef _NL_CURRENT
671 			switch (*fmt++)
672 			{
673 			case 'c':
674 				/* Match locale's alternate date and time format.  */
675 				if (*decided != raw)
676 				{
677 					const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
678 
679 					if (*fmt == '\0')
680 						fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
681 
682 					if (!recursive (fmt))
683 					{
684 						if (*decided == loc)
685 							return NULL;
686 					}
687 					else
688 					{
689 						if (strcmp (fmt, HERE_D_T_FMT))
690 							*decided = loc;
691 						want_xday = 1;
692 						break;
693 					}
694 					*decided = raw;
695 				}
696 				/* The C locale has no era information, so use the
697 				normal representation.  */
698 				if (!recursive (HERE_D_T_FMT))
699 					return NULL;
700 				want_xday = 1;
701 				break;
702 			case 'C':
703 			case 'y':
704 			case 'Y':
705 				/* Match name of base year in locale's alternate
706 				representation.  */
707 				/* XXX This is currently not implemented.  It should
708 				use the value _NL_CURRENT (LC_TIME, ERA).  */
709 				break;
710 			case 'x':
711 				if (*decided != raw)
712 				{
713 					const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
714 
715 					if (*fmt == '\0')
716 						fmt = _NL_CURRENT (LC_TIME, D_FMT);
717 
718 					if (!recursive (fmt))
719 					{
720 						if (*decided == loc)
721 							return NULL;
722 					}
723 					else
724 					{
725 						if (strcmp (fmt, HERE_D_FMT))
726 							*decided = loc;
727 						break;
728 					}
729 					*decided = raw;
730 				}
731 				if (!recursive (HERE_D_FMT))
732 					return NULL;
733 				break;
734 			case 'X':
735 				if (*decided != raw)
736 				{
737 					const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
738 
739 					if (*fmt == '\0')
740 						fmt = _NL_CURRENT (LC_TIME, T_FMT);
741 
742 					if (!recursive (fmt))
743 					{
744 						if (*decided == loc)
745 							return NULL;
746 					}
747 					else
748 					{
749 						if (strcmp (fmt, HERE_T_FMT))
750 							*decided = loc;
751 						break;
752 					}
753 					*decided = raw;
754 				}
755 				if (!recursive (HERE_T_FMT))
756 					return NULL;
757 				break;
758 			default:
759 				return NULL;
760 			}
761 			break;
762 #else
763 			/* We have no information about the era format.  Just use
764 			the normal format.  */
765 			if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
766 				&& *fmt != 'x' && *fmt != 'X')
767 				/* This is an illegal format.  */
768 				return NULL;
769 
770 			goto start_over;
771 #endif
772 		case 'O':
773 			switch (*fmt++)
774 			{
775 			case 'd':
776 			case 'e':
777 				/* Match day of month using alternate numeric symbols.  */
778 				get_alt_number (1, 31, 2);
779 				tm->tm_mday = val;
780 				have_mday = 1;
781 				want_xday = 1;
782 				break;
783 			case 'H':
784 				/* Match hour in 24-hour clock using alternate numeric
785 				symbols.  */
786 				get_alt_number (0, 23, 2);
787 				tm->tm_hour = val;
788 				have_I = 0;
789 				break;
790 			case 'I':
791 				/* Match hour in 12-hour clock using alternate numeric
792 				symbols.  */
793 				get_alt_number (1, 12, 2);
794 				tm->tm_hour = val - 1;
795 				have_I = 1;
796 				break;
797 			case 'm':
798 				/* Match month using alternate numeric symbols.  */
799 				get_alt_number (1, 12, 2);
800 				tm->tm_mon = val - 1;
801 				have_mon = 1;
802 				want_xday = 1;
803 				break;
804 			case 'M':
805 				/* Match minutes using alternate numeric symbols.  */
806 				get_alt_number (0, 59, 2);
807 				tm->tm_min = val;
808 				break;
809 			case 'S':
810 				/* Match seconds using alternate numeric symbols.  */
811 				get_alt_number (0, 61, 2);
812 				tm->tm_sec = val;
813 				break;
814 			case 'U':
815 			case 'V':
816 			case 'W':
817 				get_alt_number (0, 53, 2);
818 				/* XXX This cannot determine any field in TM without
819 				further information.  */
820 				break;
821 			case 'w':
822 				/* Match number of weekday using alternate numeric symbols.  */
823 				get_alt_number (0, 6, 1);
824 				tm->tm_wday = val;
825 				have_wday = 1;
826 				break;
827 			case 'y':
828 				/* Match year within century using alternate numeric symbols.  */
829 				get_alt_number (0, 99, 2);
830 				tm->tm_year = val >= 69 ? val : val + 100;
831 				want_xday = 1;
832 				break;
833 			default:
834 				return NULL;
835 			}
836 			break;
837 		default:
838 			return NULL;
839 		}
840 	}
841 
842 	if (have_I && is_pm)
843 		tm->tm_hour += 12;
844 
845 	if (want_century && century != -1)
846 		tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
847 
848 	if (want_xday && !have_wday) {
849 		if ( !(have_mon && have_mday) && have_yday)  {
850 			/* we don't have tm_mon and/or tm_mday, compute them */
851 			int t_mon = 0;
852 			while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
853 				t_mon++;
854 			if (!have_mon)
855 				tm->tm_mon = t_mon - 1;
856 			if (!have_mday)
857 				tm->tm_mday = tm->tm_yday - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1;
858 		}
859 		day_of_the_week (tm);
860 	}
861 	if (want_xday && !have_yday)
862 		day_of_the_year (tm);
863 
864 	return (char *) rp;
865 }
866 
867 ABI_EXPORT
868 char *UT_strptime (const char *buf, const char *format, struct tm *tmtm);
869 
UT_strptime(const char * buf,const char * format,struct tm * tmtm)870 char *UT_strptime (const char *buf, const char *format, struct tm *tmtm)
871 {
872 	enum locale_status decided;
873 #ifdef _NL_CURRENT
874 	decided = not;
875 #else
876 	decided = raw;
877 #endif
878 	return UT_strptime_internal (buf, format, tmtm, &decided);
879 }
880