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