pyldb: avoid segfault when adding an element with no name
[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
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
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 }