181fd12ba9f84eaf50b17fa423c269485064ad10
[metze/samba/wip.git] / lib / replace / strptime.c
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 *
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
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
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
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
302       /* Make back up of current processing pointer.  */
303       rp_backup = rp;
304 #endif
305
306       switch (*fmt++)
307         {
308         case '%':
309           /* Match the `%' character itself.  */
310           match_char ('%', *rp++);
311           break;
312         case 'a':
313         case 'A':
314           /* Match day of week.  */
315           for (cnt = 0; cnt < 7; ++cnt)
316             {
317 #ifdef _NL_CURRENT
318               if (*decided !=raw)
319                 {
320                   if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
321                     {
322                       if (*decided == not
323                           && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
324                                      weekday_name[cnt]))
325                         *decided = loc;
326                       break;
327                     }
328                   if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
329                     {
330                       if (*decided == not
331                           && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
332                                      ab_weekday_name[cnt]))
333                         *decided = loc;
334                       break;
335                     }
336                 }
337 #endif
338               if (*decided != loc
339                   && (match_string (weekday_name[cnt], rp)
340                       || match_string (ab_weekday_name[cnt], rp)))
341                 {
342                   *decided = raw;
343                   break;
344                 }
345             }
346           if (cnt == 7)
347             /* Does not match a weekday name.  */
348             return NULL;
349           tm->tm_wday = cnt;
350           have_wday = 1;
351           break;
352         case 'b':
353         case 'B':
354         case 'h':
355           /* Match month name.  */
356           for (cnt = 0; cnt < 12; ++cnt)
357             {
358 #ifdef _NL_CURRENT
359               if (*decided !=raw)
360                 {
361                   if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
362                     {
363                       if (*decided == not
364                           && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
365                                      month_name[cnt]))
366                         *decided = loc;
367                       break;
368                     }
369                   if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
370                     {
371                       if (*decided == not
372                           && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
373                                      ab_month_name[cnt]))
374                         *decided = loc;
375                       break;
376                     }
377                 }
378 #endif
379               if (match_string (month_name[cnt], rp)
380                   || match_string (ab_month_name[cnt], rp))
381                 {
382                   *decided = raw;
383                   break;
384                 }
385             }
386           if (cnt == 12)
387             /* Does not match a month name.  */
388             return NULL;
389           tm->tm_mon = cnt;
390           want_xday = 1;
391           break;
392         case 'c':
393           /* Match locale's date and time format.  */
394 #ifdef _NL_CURRENT
395           if (*decided != raw)
396             {
397               if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
398                 {
399                   if (*decided == loc)
400                     return NULL;
401                   else
402                     rp = rp_backup;
403                 }
404               else
405                 {
406                   if (*decided == not &&
407                       strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
408                     *decided = loc;
409                   want_xday = 1;
410                   break;
411                 }
412               *decided = raw;
413             }
414 #endif
415           if (!recursive (HERE_D_T_FMT))
416             return NULL;
417           want_xday = 1;
418           break;
419         case 'C':
420           /* Match century number.  */
421 #ifdef _NL_CURRENT
422         match_century:
423 #endif
424           get_number (0, 99, 2);
425           century = val;
426           want_xday = 1;
427           break;
428         case 'd':
429         case 'e':
430           /* Match day of month.  */
431           get_number (1, 31, 2);
432           tm->tm_mday = val;
433           have_mday = 1;
434           want_xday = 1;
435           break;
436         case 'F':
437           if (!recursive ("%Y-%m-%d"))
438             return NULL;
439           want_xday = 1;
440           break;
441         case 'x':
442 #ifdef _NL_CURRENT
443           if (*decided != raw)
444             {
445               if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
446                 {
447                   if (*decided == loc)
448                     return NULL;
449                   else
450                     rp = rp_backup;
451                 }
452               else
453                 {
454                   if (*decided == not
455                       && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
456                     *decided = loc;
457                   want_xday = 1;
458                   break;
459                 }
460               *decided = raw;
461             }
462 #endif
463           /* Fall through.  */
464         case 'D':
465           /* Match standard day format.  */
466           if (!recursive (HERE_D_FMT))
467             return NULL;
468           want_xday = 1;
469           break;
470         case 'k':
471         case 'H':
472           /* Match hour in 24-hour clock.  */
473           get_number (0, 23, 2);
474           tm->tm_hour = val;
475           have_I = 0;
476           break;
477         case 'I':
478           /* Match hour in 12-hour clock.  */
479           get_number (1, 12, 2);
480           tm->tm_hour = val % 12;
481           have_I = 1;
482           break;
483         case 'j':
484           /* Match day number of year.  */
485           get_number (1, 366, 3);
486           tm->tm_yday = val - 1;
487           have_yday = 1;
488           break;
489         case 'm':
490           /* Match number of month.  */
491           get_number (1, 12, 2);
492           tm->tm_mon = val - 1;
493           have_mon = 1;
494           want_xday = 1;
495           break;
496         case 'M':
497           /* Match minute.  */
498           get_number (0, 59, 2);
499           tm->tm_min = val;
500           break;
501         case 'n':
502         case 't':
503           /* Match any white space.  */
504           while (isspace (*rp))
505             ++rp;
506           break;
507         case 'p':
508           /* Match locale's equivalent of AM/PM.  */
509 #ifdef _NL_CURRENT
510           if (*decided != raw)
511             {
512               if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
513                 {
514                   if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
515                     *decided = loc;
516                   break;
517                 }
518               if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
519                 {
520                   if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
521                     *decided = loc;
522                   is_pm = 1;
523                   break;
524                 }
525               *decided = raw;
526             }
527 #endif
528           if (!match_string (HERE_AM_STR, rp)) {
529             if (match_string (HERE_PM_STR, rp)) {
530               is_pm = 1;
531             } else {
532               return NULL;
533             }
534           }
535           break;
536         case 'r':
537 #ifdef _NL_CURRENT
538           if (*decided != raw)
539             {
540               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
541                 {
542                   if (*decided == loc)
543                     return NULL;
544                   else
545                     rp = rp_backup;
546                 }
547               else
548                 {
549                   if (*decided == not &&
550                       strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
551                               HERE_T_FMT_AMPM))
552                     *decided = loc;
553                   break;
554                 }
555               *decided = raw;
556             }
557 #endif
558           if (!recursive (HERE_T_FMT_AMPM))
559             return NULL;
560           break;
561         case 'R':
562           if (!recursive ("%H:%M"))
563             return NULL;
564           break;
565         case 's':
566           {
567             /* The number of seconds may be very high so we cannot use
568                the `get_number' macro.  Instead read the number
569                character for character and construct the result while
570                doing this.  */
571             time_t secs = 0;
572             if (*rp < '0' || *rp > '9')
573               /* We need at least one digit.  */
574               return NULL;
575
576             do
577               {
578                 secs *= 10;
579                 secs += *rp++ - '0';
580               }
581             while (*rp >= '0' && *rp <= '9');
582
583             if (localtime_r (&secs, tm) == NULL)
584               /* Error in function.  */
585               return NULL;
586           }
587           break;
588         case 'S':
589           get_number (0, 61, 2);
590           tm->tm_sec = val;
591           break;
592         case 'X':
593 #ifdef _NL_CURRENT
594           if (*decided != raw)
595             {
596               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
597                 {
598                   if (*decided == loc)
599                     return NULL;
600                   else
601                     rp = rp_backup;
602                 }
603               else
604                 {
605                   if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
606                     *decided = loc;
607                   break;
608                 }
609               *decided = raw;
610             }
611 #endif
612           /* Fall through.  */
613         case 'T':
614           if (!recursive (HERE_T_FMT))
615             return NULL;
616           break;
617         case 'u':
618           get_number (1, 7, 1);
619           tm->tm_wday = val % 7;
620           have_wday = 1;
621           break;
622         case 'g':
623           get_number (0, 99, 2);
624           /* XXX This cannot determine any field in TM.  */
625           break;
626         case 'G':
627           if (*rp < '0' || *rp > '9')
628             return NULL;
629           /* XXX Ignore the number since we would need some more
630              information to compute a real date.  */
631           do
632             ++rp;
633           while (*rp >= '0' && *rp <= '9');
634           break;
635         case 'U':
636         case 'V':
637         case 'W':
638           get_number (0, 53, 2);
639           /* XXX This cannot determine any field in TM without some
640              information.  */
641           break;
642         case 'w':
643           /* Match number of weekday.  */
644           get_number (0, 6, 1);
645           tm->tm_wday = val;
646           have_wday = 1;
647           break;
648         case 'y':
649 #ifdef _NL_CURRENT
650         match_year_in_century:
651 #endif
652           /* Match year within century.  */
653           get_number (0, 99, 2);
654           /* The "Year 2000: The Millennium Rollover" paper suggests that
655              values in the range 69-99 refer to the twentieth century.  */
656           tm->tm_year = val >= 69 ? val : val + 100;
657           /* Indicate that we want to use the century, if specified.  */
658           want_century = 1;
659           want_xday = 1;
660           break;
661         case 'Y':
662           /* Match year including century number.  */
663           get_number (0, 9999, 4);
664           tm->tm_year = val - 1900;
665           want_century = 0;
666           want_xday = 1;
667           break;
668         case 'Z':
669           /* XXX How to handle this?  */
670           break;
671         case 'E':
672 #ifdef _NL_CURRENT
673           switch (*fmt++)
674             {
675             case 'c':
676               /* Match locale's alternate date and time format.  */
677               if (*decided != raw)
678                 {
679                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
680
681                   if (*fmt == '\0')
682                     fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
683
684                   if (!recursive (fmt))
685                     {
686                       if (*decided == loc)
687                         return NULL;
688                       else
689                         rp = rp_backup;
690                     }
691                   else
692                     {
693                       if (strcmp (fmt, HERE_D_T_FMT))
694                         *decided = loc;
695                       want_xday = 1;
696                       break;
697                     }
698                   *decided = raw;
699                 }
700               /* The C locale has no era information, so use the
701                  normal representation.  */
702               if (!recursive (HERE_D_T_FMT))
703                 return NULL;
704               want_xday = 1;
705               break;
706             case 'C':
707               if (*decided != raw)
708                 {
709                   if (era_cnt >= 0)
710                     {
711                       era = _nl_select_era_entry (era_cnt);
712                       if (match_string (era->era_name, rp))
713                         {
714                           *decided = loc;
715                           break;
716                         }
717                       else
718                         return NULL;
719                     }
720                   else
721                     {
722                       num_eras = _NL_CURRENT_WORD (LC_TIME,
723                                                    _NL_TIME_ERA_NUM_ENTRIES);
724                       for (era_cnt = 0; era_cnt < (int) num_eras;
725                            ++era_cnt, rp = rp_backup)
726                         {
727                           era = _nl_select_era_entry (era_cnt);
728                           if (match_string (era->era_name, rp))
729                             {
730                               *decided = loc;
731                               break;
732                             }
733                         }
734                       if (era_cnt == (int) num_eras)
735                         {
736                           era_cnt = -1;
737                           if (*decided == loc)
738                             return NULL;
739                         }
740                       else
741                         break;
742                     }
743
744                   *decided = raw;
745                 }
746               /* The C locale has no era information, so use the
747                  normal representation.  */
748               goto match_century;
749             case 'y':
750               if (*decided == raw)
751                 goto match_year_in_century;
752
753               get_number(0, 9999, 4);
754               tm->tm_year = val;
755               want_era = 1;
756               want_xday = 1;
757               break;
758             case 'Y':
759               if (*decided != raw)
760                 {
761                   num_eras = _NL_CURRENT_WORD (LC_TIME,
762                                                _NL_TIME_ERA_NUM_ENTRIES);
763                   for (era_cnt = 0; era_cnt < (int) num_eras;
764                        ++era_cnt, rp = rp_backup)
765                     {
766                       era = _nl_select_era_entry (era_cnt);
767                       if (recursive (era->era_format))
768                         break;
769                     }
770                   if (era_cnt == (int) num_eras)
771                     {
772                       era_cnt = -1;
773                       if (*decided == loc)
774                         return NULL;
775                       else
776                         rp = rp_backup;
777                     }
778                   else
779                     {
780                       *decided = loc;
781                       era_cnt = -1;
782                       break;
783                     }
784
785                   *decided = raw;
786                 }
787               get_number (0, 9999, 4);
788               tm->tm_year = val - 1900;
789               want_century = 0;
790               want_xday = 1;
791               break;
792             case 'x':
793               if (*decided != raw)
794                 {
795                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
796
797                   if (*fmt == '\0')
798                     fmt = _NL_CURRENT (LC_TIME, D_FMT);
799
800                   if (!recursive (fmt))
801                     {
802                       if (*decided == loc)
803                         return NULL;
804                       else
805                         rp = rp_backup;
806                     }
807                   else
808                     {
809                       if (strcmp (fmt, HERE_D_FMT))
810                         *decided = loc;
811                       break;
812                     }
813                   *decided = raw;
814                 }
815               if (!recursive (HERE_D_FMT))
816                 return NULL;
817               break;
818             case 'X':
819               if (*decided != raw)
820                 {
821                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
822
823                   if (*fmt == '\0')
824                     fmt = _NL_CURRENT (LC_TIME, T_FMT);
825
826                   if (!recursive (fmt))
827                     {
828                       if (*decided == loc)
829                         return NULL;
830                       else
831                         rp = rp_backup;
832                     }
833                   else
834                     {
835                       if (strcmp (fmt, HERE_T_FMT))
836                         *decided = loc;
837                       break;
838                     }
839                   *decided = raw;
840                 }
841               if (!recursive (HERE_T_FMT))
842                 return NULL;
843               break;
844             default:
845               return NULL;
846             }
847           break;
848 #else
849           /* We have no information about the era format.  Just use
850              the normal format.  */
851           if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
852               && *fmt != 'x' && *fmt != 'X')
853             /* This is an illegal format.  */
854             return NULL;
855
856           goto start_over;
857 #endif
858         case 'O':
859           switch (*fmt++)
860             {
861             case 'd':
862             case 'e':
863               /* Match day of month using alternate numeric symbols.  */
864               get_alt_number (1, 31, 2);
865               tm->tm_mday = val;
866               have_mday = 1;
867               want_xday = 1;
868               break;
869             case 'H':
870               /* Match hour in 24-hour clock using alternate numeric
871                  symbols.  */
872               get_alt_number (0, 23, 2);
873               tm->tm_hour = val;
874               have_I = 0;
875               break;
876             case 'I':
877               /* Match hour in 12-hour clock using alternate numeric
878                  symbols.  */
879               get_alt_number (1, 12, 2);
880               tm->tm_hour = val - 1;
881               have_I = 1;
882               break;
883             case 'm':
884               /* Match month using alternate numeric symbols.  */
885               get_alt_number (1, 12, 2);
886               tm->tm_mon = val - 1;
887               have_mon = 1;
888               want_xday = 1;
889               break;
890             case 'M':
891               /* Match minutes using alternate numeric symbols.  */
892               get_alt_number (0, 59, 2);
893               tm->tm_min = val;
894               break;
895             case 'S':
896               /* Match seconds using alternate numeric symbols.  */
897               get_alt_number (0, 61, 2);
898               tm->tm_sec = val;
899               break;
900             case 'U':
901             case 'V':
902             case 'W':
903               get_alt_number (0, 53, 2);
904               /* XXX This cannot determine any field in TM without
905                  further information.  */
906               break;
907             case 'w':
908               /* Match number of weekday using alternate numeric symbols.  */
909               get_alt_number (0, 6, 1);
910               tm->tm_wday = val;
911               have_wday = 1;
912               break;
913             case 'y':
914               /* Match year within century using alternate numeric symbols.  */
915               get_alt_number (0, 99, 2);
916               tm->tm_year = val >= 69 ? val : val + 100;
917               want_xday = 1;
918               break;
919             default:
920               return NULL;
921             }
922           break;
923         default:
924           return NULL;
925         }
926     }
927
928   if (have_I && is_pm)
929     tm->tm_hour += 12;
930
931   if (century != -1)
932     {
933       if (want_century)
934         tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
935       else
936         /* Only the century, but not the year.  Strange, but so be it.  */
937         tm->tm_year = (century - 19) * 100;
938     }
939
940 #ifdef _NL_CURRENT
941   if (era_cnt != -1)
942     {
943       era = _nl_select_era_entry(era_cnt);
944       if (want_era)
945         tm->tm_year = (era->start_date[0]
946                        + ((tm->tm_year - era->offset)
947                           * era->absolute_direction));
948       else
949         /* Era start year assumed.  */
950         tm->tm_year = era->start_date[0];
951     }
952   else
953 #endif
954     if (want_era)
955       return NULL;
956
957   if (want_xday && !have_wday)
958     {
959       if ( !(have_mon && have_mday) && have_yday)
960         {
961           /* We don't have tm_mon and/or tm_mday, compute them.  */
962           int t_mon = 0;
963           while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
964               t_mon++;
965           if (!have_mon)
966               tm->tm_mon = t_mon - 1;
967           if (!have_mday)
968               tm->tm_mday =
969                 (tm->tm_yday
970                  - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
971         }
972       day_of_the_week (tm);
973     }
974   if (want_xday && !have_yday)
975     day_of_the_year (tm);
976
977   return discard_const_p(char, rp);
978 }
979
980
981 char *rep_strptime(const char *buf, const char *format, struct tm *tm)
982 {
983   enum locale_status decided;
984
985 #ifdef _NL_CURRENT
986   decided = not;
987 #else
988   decided = raw;
989 #endif
990   return strptime_internal (buf, format, tm, &decided, -1);
991 }