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