Fix typos.
[kopensolaris-gnu/glibc.git] / time / 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 Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 /* XXX This version of the implementation is not really complete.
22    Some of the fields cannot add information alone.  But if seeing
23    some of them in the same format (such as year, week and weekday)
24    this is enough information for determining the date.  */
25
26 #ifdef HAVE_CONFIG_H
27 # include <config.h>
28 #endif
29
30 #include <ctype.h>
31 #include <langinfo.h>
32 #include <limits.h>
33 #include <string.h>
34 #include <time.h>
35
36 #ifdef _LIBC
37 # include "../locale/localeinfo.h"
38 #endif
39
40
41 #ifndef __P
42 # if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
43 #  define __P(args) args
44 # else
45 #  define __P(args) ()
46 # endif  /* GCC.  */
47 #endif  /* Not __P.  */
48
49
50 #if ! HAVE_LOCALTIME_R && ! defined localtime_r
51 # ifdef _LIBC
52 #  define localtime_r __localtime_r
53 # else
54 /* Approximate localtime_r as best we can in its absence.  */
55 #  define localtime_r my_localtime_r
56 static struct tm *localtime_r __P ((const time_t *, struct tm *));
57 static struct tm *
58 localtime_r (t, tp)
59      const time_t *t;
60      struct tm *tp;
61 {
62   struct tm *l = localtime (t);
63   if (! l)
64     return 0;
65   *tp = *l;
66   return tp;
67 }
68 # endif /* ! _LIBC */
69 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
70
71
72 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
73 #if defined __GNUC__ && __GNUC__ >= 2
74 # define match_string(cs1, s2) \
75   ({ size_t len = strlen (cs1);                                               \
76      int result = strncasecmp ((cs1), (s2), len) == 0;                        \
77      if (result) (s2) += len;                                                 \
78      result; })
79 #else
80 /* Oh come on.  Get a reasonable compiler.  */
81 # define match_string(cs1, s2) \
82   (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
83 #endif
84 /* We intentionally do not use isdigit() for testing because this will
85    lead to problems with the wide character version.  */
86 #define get_number(from, to, n) \
87   do {                                                                        \
88     int __n = n;                                                              \
89     val = 0;                                                                  \
90     while (*rp == ' ')                                                        \
91       ++rp;                                                                   \
92     if (*rp < '0' || *rp > '9')                                               \
93       return NULL;                                                            \
94     do {                                                                      \
95       val *= 10;                                                              \
96       val += *rp++ - '0';                                                     \
97     } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9');        \
98     if (val < from || val > to)                                               \
99       return NULL;                                                            \
100   } while (0)
101 #ifdef _NL_CURRENT
102 # define get_alt_number(from, to, n) \
103   ({                                                                          \
104     __label__ do_normal;                                                      \
105     if (*decided != raw)                                                      \
106       {                                                                       \
107         const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS);                 \
108         int __n = n;                                                          \
109         int any = 0;                                                          \
110         while (*rp == ' ')                                                    \
111           ++rp;                                                               \
112         val = 0;                                                              \
113         do {                                                                  \
114           val *= 10;                                                          \
115           while (*alts != '\0')                                               \
116             {                                                                 \
117               size_t len = strlen (alts);                                     \
118               if (strncasecmp (alts, rp, len) == 0)                           \
119                 break;                                                        \
120               alts += len + 1;                                                \
121               ++val;                                                          \
122             }                                                                 \
123           if (*alts == '\0')                                                  \
124             {                                                                 \
125               if (*decided == not && ! any)                                   \
126                 goto do_normal;                                               \
127               /* If we haven't read anything it's an error.  */               \
128               if (! any)                                                      \
129                 return NULL;                                                  \
130               /* Correct the premature multiplication.  */                    \
131               val /= 10;                                                      \
132               break;                                                          \
133             }                                                                 \
134           else                                                                \
135             *decided = loc;                                                   \
136         } while (--__n > 0 && val * 10 <= to);                                \
137         if (val < from || val > to)                                           \
138           return NULL;                                                        \
139       }                                                                       \
140     else                                                                      \
141       {                                                                       \
142        do_normal:                                                             \
143         get_number (from, to, n);                                             \
144       }                                                                       \
145     0;                                                                        \
146   })
147 #else
148 # define get_alt_number(from, to, n) \
149   /* We don't have the alternate representation.  */                          \
150   get_number(from, to, n)
151 #endif
152 #define recursive(new_fmt) \
153   (*(new_fmt) != '\0'                                                         \
154    && (rp = strptime_internal (rp, (new_fmt), tm, decided, era_cnt)) != NULL)
155
156
157 #ifdef _LIBC
158 /* This is defined in locale/C-time.c in the GNU libc.  */
159 extern const struct locale_data _nl_C_LC_TIME;
160 extern const unsigned short int __mon_yday[2][13];
161
162 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
163 # define ab_weekday_name \
164   (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
165 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
166 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
167 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
168 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
169 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
170 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
171 # define HERE_T_FMT_AMPM \
172   (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
173 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
174
175 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
176 #else
177 static char const weekday_name[][10] =
178   {
179     "Sunday", "Monday", "Tuesday", "Wednesday",
180     "Thursday", "Friday", "Saturday"
181   };
182 static char const ab_weekday_name[][4] =
183   {
184     "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
185   };
186 static char const month_name[][10] =
187   {
188     "January", "February", "March", "April", "May", "June",
189     "July", "August", "September", "October", "November", "December"
190   };
191 static char const ab_month_name[][4] =
192   {
193     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
194     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
195   };
196 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
197 # define HERE_D_FMT "%m/%d/%y"
198 # define HERE_AM_STR "AM"
199 # define HERE_PM_STR "PM"
200 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
201 # define HERE_T_FMT "%H:%M:%S"
202
203 const unsigned short int __mon_yday[2][13] =
204   {
205     /* Normal years.  */
206     { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
207     /* Leap years.  */
208     { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
209   };
210 #endif
211
212 /* Status of lookup: do we use the locale data or the raw data?  */
213 enum locale_status { not, loc, raw };
214
215
216 #ifndef __isleap
217 /* Nonzero if YEAR is a leap year (every 4 years,
218    except every 100th isn't, and every 400th is).  */
219 # define __isleap(year) \
220   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
221 #endif
222
223 /* Compute the day of the week.  */
224 static void
225 day_of_the_week (struct tm *tm)
226 {
227   /* We know that January 1st 1970 was a Thursday (= 4).  Compute the
228      the difference between this data in the one on TM and so determine
229      the weekday.  */
230   int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
231   int wday = (-473
232               + (365 * (tm->tm_year - 70))
233               + (corr_year / 4)
234               - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
235               + (((corr_year / 4) / 25) / 4)
236               + __mon_yday[0][tm->tm_mon]
237               + tm->tm_mday - 1);
238   tm->tm_wday = ((wday % 7) + 7) % 7;
239 }
240
241 /* Compute the day of the year.  */
242 static void
243 day_of_the_year (struct tm *tm)
244 {
245   tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
246                  + (tm->tm_mday - 1));
247 }
248
249 static char *
250 #ifdef _LIBC
251 internal_function
252 #endif
253 strptime_internal __P ((const char *rp, const char *fmt, struct tm *tm,
254                         enum locale_status *decided, int era_cnt));
255
256 static char *
257 #ifdef _LIBC
258 internal_function
259 #endif
260 strptime_internal (rp, fmt, tm, decided, era_cnt)
261      const char *rp;
262      const char *fmt;
263      struct tm *tm;
264      enum locale_status *decided;
265      int era_cnt;
266 {
267   const char *rp_backup;
268   int cnt;
269   size_t val;
270   int have_I, is_pm;
271   int century, want_century;
272   int want_era;
273   int have_wday, want_xday;
274   int have_yday;
275   int have_mon, have_mday;
276   size_t num_eras;
277   struct era_entry *era;
278
279   have_I = is_pm = 0;
280   century = -1;
281   want_century = 0;
282   want_era = 0;
283   era = NULL;
284
285   have_wday = want_xday = have_yday = have_mon = have_mday = 0;
286
287   while (*fmt != '\0')
288     {
289       /* A white space in the format string matches 0 more or white
290          space in the input string.  */
291       if (isspace (*fmt))
292         {
293           while (isspace (*rp))
294             ++rp;
295           ++fmt;
296           continue;
297         }
298
299       /* Any character but `%' must be matched by the same character
300          in the iput string.  */
301       if (*fmt != '%')
302         {
303           match_char (*fmt++, *rp++);
304           continue;
305         }
306
307       ++fmt;
308 #ifndef _NL_CURRENT
309       /* We need this for handling the `E' modifier.  */
310     start_over:
311 #endif
312
313       /* Make back up of current processing pointer.  */
314       rp_backup = rp;
315
316       switch (*fmt++)
317         {
318         case '%':
319           /* Match the `%' character itself.  */
320           match_char ('%', *rp++);
321           break;
322         case 'a':
323         case 'A':
324           /* Match day of week.  */
325           for (cnt = 0; cnt < 7; ++cnt)
326             {
327 #ifdef _NL_CURRENT
328               if (*decided !=raw)
329                 {
330                   if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
331                     {
332                       if (*decided == not
333                           && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
334                                      weekday_name[cnt]))
335                         *decided = loc;
336                       break;
337                     }
338                   if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
339                     {
340                       if (*decided == not
341                           && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
342                                      ab_weekday_name[cnt]))
343                         *decided = loc;
344                       break;
345                     }
346                 }
347 #endif
348               if (*decided != loc
349                   && (match_string (weekday_name[cnt], rp)
350                       || match_string (ab_weekday_name[cnt], rp)))
351                 {
352                   *decided = raw;
353                   break;
354                 }
355             }
356           if (cnt == 7)
357             /* Does not match a weekday name.  */
358             return NULL;
359           tm->tm_wday = cnt;
360           have_wday = 1;
361           break;
362         case 'b':
363         case 'B':
364         case 'h':
365           /* Match month name.  */
366           for (cnt = 0; cnt < 12; ++cnt)
367             {
368 #ifdef _NL_CURRENT
369               if (*decided !=raw)
370                 {
371                   if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
372                     {
373                       if (*decided == not
374                           && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
375                                      month_name[cnt]))
376                         *decided = loc;
377                       break;
378                     }
379                   if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
380                     {
381                       if (*decided == not
382                           && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
383                                      ab_month_name[cnt]))
384                         *decided = loc;
385                       break;
386                     }
387                 }
388 #endif
389               if (match_string (month_name[cnt], rp)
390                   || match_string (ab_month_name[cnt], rp))
391                 {
392                   *decided = raw;
393                   break;
394                 }
395             }
396           if (cnt == 12)
397             /* Does not match a month name.  */
398             return NULL;
399           tm->tm_mon = cnt;
400           want_xday = 1;
401           break;
402         case 'c':
403           /* Match locale's date and time format.  */
404 #ifdef _NL_CURRENT
405           if (*decided != raw)
406             {
407               if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
408                 {
409                   if (*decided == loc)
410                     return NULL;
411                   else
412                     rp = rp_backup;
413                 }
414               else
415                 {
416                   if (*decided == not &&
417                       strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
418                     *decided = loc;
419                   want_xday = 1;
420                   break;
421                 }
422               *decided = raw;
423             }
424 #endif
425           if (!recursive (HERE_D_T_FMT))
426             return NULL;
427           want_xday = 1;
428           break;
429         case 'C':
430           /* Match century number.  */
431         match_century:
432           get_number (0, 99, 2);
433           century = val;
434           want_xday = 1;
435           break;
436         case 'd':
437         case 'e':
438           /* Match day of month.  */
439           get_number (1, 31, 2);
440           tm->tm_mday = val;
441           have_mday = 1;
442           want_xday = 1;
443           break;
444         case 'F':
445           if (!recursive ("%Y-%m-%d"))
446             return NULL;
447           want_xday = 1;
448           break;
449         case 'x':
450 #ifdef _NL_CURRENT
451           if (*decided != raw)
452             {
453               if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
454                 {
455                   if (*decided == loc)
456                     return NULL;
457                   else
458                     rp = rp_backup;
459                 }
460               else
461                 {
462                   if (*decided == not
463                       && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
464                     *decided = loc;
465                   want_xday = 1;
466                   break;
467                 }
468               *decided = raw;
469             }
470 #endif
471           /* Fall through.  */
472         case 'D':
473           /* Match standard day format.  */
474           if (!recursive (HERE_D_FMT))
475             return NULL;
476           want_xday = 1;
477           break;
478         case 'k':
479         case 'H':
480           /* Match hour in 24-hour clock.  */
481           get_number (0, 23, 2);
482           tm->tm_hour = val;
483           have_I = 0;
484           break;
485         case 'I':
486           /* Match hour in 12-hour clock.  */
487           get_number (1, 12, 2);
488           tm->tm_hour = val % 12;
489           have_I = 1;
490           break;
491         case 'j':
492           /* Match day number of year.  */
493           get_number (1, 366, 3);
494           tm->tm_yday = val - 1;
495           have_yday = 1;
496           break;
497         case 'm':
498           /* Match number of month.  */
499           get_number (1, 12, 2);
500           tm->tm_mon = val - 1;
501           have_mon = 1;
502           want_xday = 1;
503           break;
504         case 'M':
505           /* Match minute.  */
506           get_number (0, 59, 2);
507           tm->tm_min = val;
508           break;
509         case 'n':
510         case 't':
511           /* Match any white space.  */
512           while (isspace (*rp))
513             ++rp;
514           break;
515         case 'p':
516           /* Match locale's equivalent of AM/PM.  */
517 #ifdef _NL_CURRENT
518           if (*decided != raw)
519             {
520               if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
521                 {
522                   if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
523                     *decided = loc;
524                   break;
525                 }
526               if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
527                 {
528                   if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
529                     *decided = loc;
530                   is_pm = 1;
531                   break;
532                 }
533               *decided = raw;
534             }
535 #endif
536           if (!match_string (HERE_AM_STR, rp))
537             if (match_string (HERE_PM_STR, rp))
538               is_pm = 1;
539             else
540               return NULL;
541           break;
542         case 'r':
543 #ifdef _NL_CURRENT
544           if (*decided != raw)
545             {
546               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
547                 {
548                   if (*decided == loc)
549                     return NULL;
550                   else
551                     rp = rp_backup;
552                 }
553               else
554                 {
555                   if (*decided == not &&
556                       strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
557                               HERE_T_FMT_AMPM))
558                     *decided = loc;
559                   break;
560                 }
561               *decided = raw;
562             }
563 #endif
564           if (!recursive (HERE_T_FMT_AMPM))
565             return NULL;
566           break;
567         case 'R':
568           if (!recursive ("%H:%M"))
569             return NULL;
570           break;
571         case 's':
572           {
573             /* The number of seconds may be very high so we cannot use
574                the `get_number' macro.  Instead read the number
575                character for character and construct the result while
576                doing this.  */
577             time_t secs = 0;
578             if (*rp < '0' || *rp > '9')
579               /* We need at least one digit.  */
580               return NULL;
581
582             do
583               {
584                 secs *= 10;
585                 secs += *rp++ - '0';
586               }
587             while (*rp >= '0' && *rp <= '9');
588
589             if (localtime_r (&secs, tm) == NULL)
590               /* Error in function.  */
591               return NULL;
592           }
593           break;
594         case 'S':
595           get_number (0, 61, 2);
596           tm->tm_sec = val;
597           break;
598         case 'X':
599 #ifdef _NL_CURRENT
600           if (*decided != raw)
601             {
602               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
603                 {
604                   if (*decided == loc)
605                     return NULL;
606                   else
607                     rp = rp_backup;
608                 }
609               else
610                 {
611                   if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
612                     *decided = loc;
613                   break;
614                 }
615               *decided = raw;
616             }
617 #endif
618           /* Fall through.  */
619         case 'T':
620           if (!recursive (HERE_T_FMT))
621             return NULL;
622           break;
623         case 'u':
624           get_number (1, 7, 1);
625           tm->tm_wday = val % 7;
626           have_wday = 1;
627           break;
628         case 'g':
629           get_number (0, 99, 2);
630           /* XXX This cannot determine any field in TM.  */
631           break;
632         case 'G':
633           if (*rp < '0' || *rp > '9')
634             return NULL;
635           /* XXX Ignore the number since we would need some more
636              information to compute a real date.  */
637           do
638             ++rp;
639           while (*rp >= '0' && *rp <= '9');
640           break;
641         case 'U':
642         case 'V':
643         case 'W':
644           get_number (0, 53, 2);
645           /* XXX This cannot determine any field in TM without some
646              information.  */
647           break;
648         case 'w':
649           /* Match number of weekday.  */
650           get_number (0, 6, 1);
651           tm->tm_wday = val;
652           have_wday = 1;
653           break;
654         case 'y':
655         match_year_in_century:
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 < 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 == 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 < 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 == 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   if (era_cnt != -1)
945     {
946       era = _nl_select_era_entry(era_cnt);
947       if (want_era)
948         tm->tm_year = (era->start_date[0]
949                        + ((tm->tm_year - era->offset)
950                           * era->absolute_direction));
951       else
952         /* Era start year assumed.  */
953         tm->tm_year = era->start_date[0];
954     }
955   else
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 (char *) rp;
980 }
981
982
983 char *
984 strptime (buf, format, tm)
985      const char *buf;
986      const char *format;
987      struct tm *tm;
988 {
989   enum locale_status decided;
990
991 #ifdef _NL_CURRENT
992   decided = not;
993 #else
994   decided = raw;
995 #endif
996   return strptime_internal (buf, format, tm, &decided, -1);
997 }