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