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