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