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