Support defining strfromf64, strfromf32x aliases.
[glibc.git] / time / strptime_l.c
blob3afc33a74ec85b117c7c30720a0f47cb4e05bf1b
1 /* Copyright (C) 2002-2017 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <http://www.gnu.org/licenses/>. */
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
22 #include <assert.h>
23 #include <ctype.h>
24 #include <langinfo.h>
25 #include <limits.h>
26 #include <string.h>
27 #include <time.h>
28 #include <stdbool.h>
30 #ifdef _LIBC
31 # define HAVE_LOCALTIME_R 0
32 # include "../locale/localeinfo.h"
33 #endif
36 #if ! HAVE_LOCALTIME_R && ! defined localtime_r
37 # ifdef _LIBC
38 # define localtime_r __localtime_r
39 # else
40 /* Approximate localtime_r as best we can in its absence. */
41 # define localtime_r my_localtime_r
42 static struct tm *localtime_r (const time_t *, struct tm *);
43 static struct tm *
44 localtime_r (const time_t *t, struct tm *tp)
46 struct tm *l = localtime (t);
47 if (! l)
48 return 0;
49 *tp = *l;
50 return tp;
52 # endif /* ! _LIBC */
53 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
56 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
57 #if defined __GNUC__ && __GNUC__ >= 2
58 # define match_string(cs1, s2) \
59 ({ size_t len = strlen (cs1); \
60 int result = __strncasecmp_l ((cs1), (s2), len, locale) == 0; \
61 if (result) (s2) += len; \
62 result; })
63 #else
64 /* Oh come on. Get a reasonable compiler. */
65 # define match_string(cs1, s2) \
66 (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
67 #endif
68 /* We intentionally do not use isdigit() for testing because this will
69 lead to problems with the wide character version. */
70 #define get_number(from, to, n) \
71 do { \
72 int __n = n; \
73 val = 0; \
74 while (ISSPACE (*rp)) \
75 ++rp; \
76 if (*rp < '0' || *rp > '9') \
77 return NULL; \
78 do { \
79 val *= 10; \
80 val += *rp++ - '0'; \
81 } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
82 if (val < from || val > to) \
83 return NULL; \
84 } while (0)
85 #ifdef _NL_CURRENT
86 # define get_alt_number(from, to, n) \
87 ({ \
88 __label__ do_normal; \
90 if (s.decided != raw) \
91 { \
92 val = _nl_parse_alt_digit (&rp HELPER_LOCALE_ARG); \
93 if (val == -1 && s.decided != loc) \
94 { \
95 s.decided = loc; \
96 goto do_normal; \
97 } \
98 if (val < from || val > to) \
99 return NULL; \
101 else \
103 do_normal: \
104 get_number (from, to, n); \
106 0; \
108 #else
109 # define get_alt_number(from, to, n) \
110 /* We don't have the alternate representation. */ \
111 get_number(from, to, n)
112 #endif
113 #define recursive(new_fmt) \
114 (*(new_fmt) != '\0' \
115 && (rp = __strptime_internal (rp, (new_fmt), tm, &s LOCALE_ARG)) != NULL)
118 #ifdef _LIBC
119 /* This is defined in locale/C-time.c in the GNU libc. */
120 extern const struct __locale_data _nl_C_LC_TIME attribute_hidden;
122 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
123 # define ab_weekday_name \
124 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
125 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
126 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
127 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
128 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
129 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
130 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
131 # define HERE_T_FMT_AMPM \
132 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
133 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
135 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
136 #else
137 static char const weekday_name[][10] =
139 "Sunday", "Monday", "Tuesday", "Wednesday",
140 "Thursday", "Friday", "Saturday"
142 static char const ab_weekday_name[][4] =
144 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
146 static char const month_name[][10] =
148 "January", "February", "March", "April", "May", "June",
149 "July", "August", "September", "October", "November", "December"
151 static char const ab_month_name[][4] =
153 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
154 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
156 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
157 # define HERE_D_FMT "%m/%d/%y"
158 # define HERE_AM_STR "AM"
159 # define HERE_PM_STR "PM"
160 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
161 # define HERE_T_FMT "%H:%M:%S"
163 static const unsigned short int __mon_yday[2][13] =
165 /* Normal years. */
166 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
167 /* Leap years. */
168 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
170 #endif
172 #if defined _LIBC
173 /* We use this code also for the extended locale handling where the
174 function gets as an additional argument the locale which has to be
175 used. To access the values we have to redefine the _NL_CURRENT
176 macro. */
177 # define strptime __strptime_l
178 # undef _NL_CURRENT
179 # define _NL_CURRENT(category, item) \
180 (current->values[_NL_ITEM_INDEX (item)].string)
181 # undef _NL_CURRENT_WORD
182 # define _NL_CURRENT_WORD(category, item) \
183 (current->values[_NL_ITEM_INDEX (item)].word)
184 # define LOCALE_PARAM , locale_t locale
185 # define LOCALE_ARG , locale
186 # define HELPER_LOCALE_ARG , current
187 # define ISSPACE(Ch) __isspace_l (Ch, locale)
188 #else
189 # define LOCALE_PARAM
190 # define LOCALE_ARG
191 # define HELPER_LOCALE_ARG
192 # define ISSPACE(Ch) isspace (Ch)
193 #endif
198 #ifndef __isleap
199 /* Nonzero if YEAR is a leap year (every 4 years,
200 except every 100th isn't, and every 400th is). */
201 # define __isleap(year) \
202 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
203 #endif
205 /* Compute the day of the week. */
206 static void
207 day_of_the_week (struct tm *tm)
209 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
210 difference between this data in the one on TM and so determine
211 the weekday. */
212 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
213 int wday = (-473
214 + (365 * (tm->tm_year - 70))
215 + (corr_year / 4)
216 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
217 + (((corr_year / 4) / 25) / 4)
218 + __mon_yday[0][tm->tm_mon]
219 + tm->tm_mday - 1);
220 tm->tm_wday = ((wday % 7) + 7) % 7;
223 /* Compute the day of the year. */
224 static void
225 day_of_the_year (struct tm *tm)
227 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
228 + (tm->tm_mday - 1));
232 #ifdef _LIBC
233 char *
234 #else
235 static char *
236 #endif
237 __strptime_internal (const char *rp, const char *fmt, struct tm *tmp,
238 void *statep LOCALE_PARAM)
240 #ifdef _LIBC
241 struct __locale_data *const current = locale->__locales[LC_TIME];
242 #endif
244 const char *rp_backup;
245 const char *rp_longest;
246 int cnt;
247 int cnt_longest;
248 size_t val;
249 size_t num_eras;
250 struct era_entry *era = NULL;
251 enum ptime_locale_status { not, loc, raw } decided_longest;
252 struct __strptime_state
254 unsigned int have_I : 1;
255 unsigned int have_wday : 1;
256 unsigned int have_yday : 1;
257 unsigned int have_mon : 1;
258 unsigned int have_mday : 1;
259 unsigned int have_uweek : 1;
260 unsigned int have_wweek : 1;
261 unsigned int is_pm : 1;
262 unsigned int want_century : 1;
263 unsigned int want_era : 1;
264 unsigned int want_xday : 1;
265 enum ptime_locale_status decided : 2;
266 signed char week_no;
267 signed char century;
268 int era_cnt;
269 } s;
270 struct tm tmb;
271 struct tm *tm;
273 if (statep == NULL)
275 memset (&s, 0, sizeof (s));
276 s.century = -1;
277 s.era_cnt = -1;
278 #ifdef _NL_CURRENT
279 s.decided = not;
280 #else
281 s.decided = raw;
282 #endif
283 tm = tmp;
285 else
287 s = *(struct __strptime_state *) statep;
288 tmb = *tmp;
289 tm = &tmb;
292 while (*fmt != '\0')
294 /* A white space in the format string matches 0 more or white
295 space in the input string. */
296 if (ISSPACE (*fmt))
298 while (ISSPACE (*rp))
299 ++rp;
300 ++fmt;
301 continue;
304 /* Any character but `%' must be matched by the same character
305 in the iput string. */
306 if (*fmt != '%')
308 match_char (*fmt++, *rp++);
309 continue;
312 ++fmt;
313 /* We discard strftime modifiers. */
314 while (*fmt == '-' || *fmt == '_' || *fmt == '0'
315 || *fmt == '^' || *fmt == '#')
316 ++fmt;
318 /* And field width. */
319 while (*fmt >= '0' && *fmt <= '9')
320 ++fmt;
322 #ifndef _NL_CURRENT
323 /* We need this for handling the `E' modifier. */
324 start_over:
325 #endif
327 /* Make back up of current processing pointer. */
328 rp_backup = rp;
330 switch (*fmt++)
332 case '%':
333 /* Match the `%' character itself. */
334 match_char ('%', *rp++);
335 break;
336 case 'a':
337 case 'A':
338 /* Match day of week. */
339 rp_longest = NULL;
340 decided_longest = s.decided;
341 cnt_longest = -1;
342 for (cnt = 0; cnt < 7; ++cnt)
344 const char *trp;
345 #ifdef _NL_CURRENT
346 if (s.decided !=raw)
348 trp = rp;
349 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), trp)
350 && trp > rp_longest)
352 rp_longest = trp;
353 cnt_longest = cnt;
354 if (s.decided == not
355 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
356 weekday_name[cnt]))
357 decided_longest = loc;
359 trp = rp;
360 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), trp)
361 && trp > rp_longest)
363 rp_longest = trp;
364 cnt_longest = cnt;
365 if (s.decided == not
366 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
367 ab_weekday_name[cnt]))
368 decided_longest = loc;
371 #endif
372 if (s.decided != loc
373 && (((trp = rp, match_string (weekday_name[cnt], trp))
374 && trp > rp_longest)
375 || ((trp = rp, match_string (ab_weekday_name[cnt], rp))
376 && trp > rp_longest)))
378 rp_longest = trp;
379 cnt_longest = cnt;
380 decided_longest = raw;
383 if (rp_longest == NULL)
384 /* Does not match a weekday name. */
385 return NULL;
386 rp = rp_longest;
387 s.decided = decided_longest;
388 tm->tm_wday = cnt_longest;
389 s.have_wday = 1;
390 break;
391 case 'b':
392 case 'B':
393 case 'h':
394 /* Match month name. */
395 rp_longest = NULL;
396 decided_longest = s.decided;
397 cnt_longest = -1;
398 for (cnt = 0; cnt < 12; ++cnt)
400 const char *trp;
401 #ifdef _NL_CURRENT
402 if (s.decided !=raw)
404 trp = rp;
405 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), trp)
406 && trp > rp_longest)
408 rp_longest = trp;
409 cnt_longest = cnt;
410 if (s.decided == not
411 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
412 month_name[cnt]))
413 decided_longest = loc;
415 trp = rp;
416 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), trp)
417 && trp > rp_longest)
419 rp_longest = trp;
420 cnt_longest = cnt;
421 if (s.decided == not
422 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
423 ab_month_name[cnt]))
424 decided_longest = loc;
427 #endif
428 if (s.decided != loc
429 && (((trp = rp, match_string (month_name[cnt], trp))
430 && trp > rp_longest)
431 || ((trp = rp, match_string (ab_month_name[cnt], trp))
432 && trp > rp_longest)))
434 rp_longest = trp;
435 cnt_longest = cnt;
436 decided_longest = raw;
439 if (rp_longest == NULL)
440 /* Does not match a month name. */
441 return NULL;
442 rp = rp_longest;
443 s.decided = decided_longest;
444 tm->tm_mon = cnt_longest;
445 s.have_mon = 1;
446 s.want_xday = 1;
447 break;
448 case 'c':
449 /* Match locale's date and time format. */
450 #ifdef _NL_CURRENT
451 if (s.decided != raw)
453 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
455 if (s.decided == loc)
456 return NULL;
457 else
458 rp = rp_backup;
460 else
462 if (s.decided == not &&
463 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
464 s.decided = loc;
465 s.want_xday = 1;
466 break;
468 s.decided = raw;
470 #endif
471 if (!recursive (HERE_D_T_FMT))
472 return NULL;
473 s.want_xday = 1;
474 break;
475 case 'C':
476 /* Match century number. */
477 match_century:
478 get_number (0, 99, 2);
479 s.century = val;
480 s.want_xday = 1;
481 break;
482 case 'd':
483 case 'e':
484 /* Match day of month. */
485 get_number (1, 31, 2);
486 tm->tm_mday = val;
487 s.have_mday = 1;
488 s.want_xday = 1;
489 break;
490 case 'F':
491 if (!recursive ("%Y-%m-%d"))
492 return NULL;
493 s.want_xday = 1;
494 break;
495 case 'x':
496 #ifdef _NL_CURRENT
497 if (s.decided != raw)
499 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
501 if (s.decided == loc)
502 return NULL;
503 else
504 rp = rp_backup;
506 else
508 if (s.decided == not
509 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
510 s.decided = loc;
511 s.want_xday = 1;
512 break;
514 s.decided = raw;
516 #endif
517 /* Fall through. */
518 case 'D':
519 /* Match standard day format. */
520 if (!recursive (HERE_D_FMT))
521 return NULL;
522 s.want_xday = 1;
523 break;
524 case 'k':
525 case 'H':
526 /* Match hour in 24-hour clock. */
527 get_number (0, 23, 2);
528 tm->tm_hour = val;
529 s.have_I = 0;
530 break;
531 case 'l':
532 /* Match hour in 12-hour clock. GNU extension. */
533 case 'I':
534 /* Match hour in 12-hour clock. */
535 get_number (1, 12, 2);
536 tm->tm_hour = val % 12;
537 s.have_I = 1;
538 break;
539 case 'j':
540 /* Match day number of year. */
541 get_number (1, 366, 3);
542 tm->tm_yday = val - 1;
543 s.have_yday = 1;
544 break;
545 case 'm':
546 /* Match number of month. */
547 get_number (1, 12, 2);
548 tm->tm_mon = val - 1;
549 s.have_mon = 1;
550 s.want_xday = 1;
551 break;
552 case 'M':
553 /* Match minute. */
554 get_number (0, 59, 2);
555 tm->tm_min = val;
556 break;
557 case 'n':
558 case 't':
559 /* Match any white space. */
560 while (ISSPACE (*rp))
561 ++rp;
562 break;
563 case 'p':
564 /* Match locale's equivalent of AM/PM. */
565 #ifdef _NL_CURRENT
566 if (s.decided != raw)
568 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
570 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
571 s.decided = loc;
572 s.is_pm = 0;
573 break;
575 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
577 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
578 s.decided = loc;
579 s.is_pm = 1;
580 break;
582 s.decided = raw;
584 #endif
585 if (!match_string (HERE_AM_STR, rp))
587 if (match_string (HERE_PM_STR, rp))
588 s.is_pm = 1;
589 else
590 return NULL;
592 else
593 s.is_pm = 0;
594 break;
595 case 'r':
596 #ifdef _NL_CURRENT
597 if (s.decided != raw)
599 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
601 if (s.decided == loc)
602 return NULL;
603 else
604 rp = rp_backup;
606 else
608 if (s.decided == not &&
609 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
610 HERE_T_FMT_AMPM))
611 s.decided = loc;
612 break;
614 s.decided = raw;
616 #endif
617 if (!recursive (HERE_T_FMT_AMPM))
618 return NULL;
619 break;
620 case 'R':
621 if (!recursive ("%H:%M"))
622 return NULL;
623 break;
624 case 's':
626 /* The number of seconds may be very high so we cannot use
627 the `get_number' macro. Instead read the number
628 character for character and construct the result while
629 doing this. */
630 time_t secs = 0;
631 if (*rp < '0' || *rp > '9')
632 /* We need at least one digit. */
633 return NULL;
637 secs *= 10;
638 secs += *rp++ - '0';
640 while (*rp >= '0' && *rp <= '9');
642 if (localtime_r (&secs, tm) == NULL)
643 /* Error in function. */
644 return NULL;
646 break;
647 case 'S':
648 get_number (0, 61, 2);
649 tm->tm_sec = val;
650 break;
651 case 'X':
652 #ifdef _NL_CURRENT
653 if (s.decided != raw)
655 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
657 if (s.decided == loc)
658 return NULL;
659 else
660 rp = rp_backup;
662 else
664 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
665 s.decided = loc;
666 break;
668 s.decided = raw;
670 #endif
671 /* Fall through. */
672 case 'T':
673 if (!recursive (HERE_T_FMT))
674 return NULL;
675 break;
676 case 'u':
677 get_number (1, 7, 1);
678 tm->tm_wday = val % 7;
679 s.have_wday = 1;
680 break;
681 case 'g':
682 get_number (0, 99, 2);
683 /* XXX This cannot determine any field in TM. */
684 break;
685 case 'G':
686 if (*rp < '0' || *rp > '9')
687 return NULL;
688 /* XXX Ignore the number since we would need some more
689 information to compute a real date. */
691 ++rp;
692 while (*rp >= '0' && *rp <= '9');
693 break;
694 case 'U':
695 get_number (0, 53, 2);
696 s.week_no = val;
697 s.have_uweek = 1;
698 break;
699 case 'W':
700 get_number (0, 53, 2);
701 s.week_no = val;
702 s.have_wweek = 1;
703 break;
704 case 'V':
705 get_number (0, 53, 2);
706 /* XXX This cannot determine any field in TM without some
707 information. */
708 break;
709 case 'w':
710 /* Match number of weekday. */
711 get_number (0, 6, 1);
712 tm->tm_wday = val;
713 s.have_wday = 1;
714 break;
715 case 'y':
716 match_year_in_century:
717 /* Match year within century. */
718 get_number (0, 99, 2);
719 /* The "Year 2000: The Millennium Rollover" paper suggests that
720 values in the range 69-99 refer to the twentieth century. */
721 tm->tm_year = val >= 69 ? val : val + 100;
722 /* Indicate that we want to use the century, if specified. */
723 s.want_century = 1;
724 s.want_xday = 1;
725 break;
726 case 'Y':
727 /* Match year including century number. */
728 get_number (0, 9999, 4);
729 tm->tm_year = val - 1900;
730 s.want_century = 0;
731 s.want_xday = 1;
732 break;
733 case 'Z':
734 /* Read timezone but perform no conversion. */
735 while (ISSPACE (*rp))
736 rp++;
737 while (!ISSPACE (*rp) && *rp != '\0')
738 rp++;
739 break;
740 case 'z':
741 /* We recognize four formats:
742 1. Two digits specify hours.
743 2. Four digits specify hours and minutes.
744 3. Two digits, ':', and two digits specify hours and minutes.
745 4. 'Z' is equivalent to +0000. */
747 val = 0;
748 while (ISSPACE (*rp))
749 ++rp;
750 if (*rp == 'Z')
752 ++rp;
753 tm->tm_gmtoff = 0;
754 break;
756 if (*rp != '+' && *rp != '-')
757 return NULL;
758 bool neg = *rp++ == '-';
759 int n = 0;
760 while (n < 4 && *rp >= '0' && *rp <= '9')
762 val = val * 10 + *rp++ - '0';
763 ++n;
764 if (*rp == ':' && n == 2 && isdigit (*(rp + 1)))
765 ++rp;
767 if (n == 2)
768 val *= 100;
769 else if (n != 4)
770 /* Only two or four digits recognized. */
771 return NULL;
772 else if (val % 100 >= 60)
773 /* Minutes valid range is 0 through 59. */
774 return NULL;
775 tm->tm_gmtoff = (val / 100) * 3600 + (val % 100) * 60;
776 if (neg)
777 tm->tm_gmtoff = -tm->tm_gmtoff;
779 break;
780 case 'E':
781 #ifdef _NL_CURRENT
782 switch (*fmt++)
784 case 'c':
785 /* Match locale's alternate date and time format. */
786 if (s.decided != raw)
788 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
790 if (*fmt == '\0')
791 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
793 if (!recursive (fmt))
795 if (s.decided == loc)
796 return NULL;
797 else
798 rp = rp_backup;
800 else
802 if (strcmp (fmt, HERE_D_T_FMT))
803 s.decided = loc;
804 s.want_xday = 1;
805 break;
807 s.decided = raw;
809 /* The C locale has no era information, so use the
810 normal representation. */
811 if (!recursive (HERE_D_T_FMT))
812 return NULL;
813 s.want_xday = 1;
814 break;
815 case 'C':
816 if (s.decided != raw)
818 if (s.era_cnt >= 0)
820 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
821 if (era != NULL && match_string (era->era_name, rp))
823 s.decided = loc;
824 break;
826 else
827 return NULL;
830 num_eras = _NL_CURRENT_WORD (LC_TIME,
831 _NL_TIME_ERA_NUM_ENTRIES);
832 for (s.era_cnt = 0; s.era_cnt < (int) num_eras;
833 ++s.era_cnt, rp = rp_backup)
835 era = _nl_select_era_entry (s.era_cnt
836 HELPER_LOCALE_ARG);
837 if (era != NULL && match_string (era->era_name, rp))
839 s.decided = loc;
840 break;
843 if (s.era_cnt != (int) num_eras)
844 break;
846 s.era_cnt = -1;
847 if (s.decided == loc)
848 return NULL;
850 s.decided = raw;
852 /* The C locale has no era information, so use the
853 normal representation. */
854 goto match_century;
855 case 'y':
856 if (s.decided != raw)
858 get_number(0, 9999, 4);
859 tm->tm_year = val;
860 s.want_era = 1;
861 s.want_xday = 1;
862 s.want_century = 1;
864 if (s.era_cnt >= 0)
866 assert (s.decided == loc);
868 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
869 bool match = false;
870 if (era != NULL)
872 int delta = ((tm->tm_year - era->offset)
873 * era->absolute_direction);
874 match = (delta >= 0
875 && delta < (((int64_t) era->stop_date[0]
876 - (int64_t) era->start_date[0])
877 * era->absolute_direction));
879 if (! match)
880 return NULL;
882 break;
885 num_eras = _NL_CURRENT_WORD (LC_TIME,
886 _NL_TIME_ERA_NUM_ENTRIES);
887 for (s.era_cnt = 0; s.era_cnt < (int) num_eras; ++s.era_cnt)
889 era = _nl_select_era_entry (s.era_cnt
890 HELPER_LOCALE_ARG);
891 if (era != NULL)
893 int delta = ((tm->tm_year - era->offset)
894 * era->absolute_direction);
895 if (delta >= 0
896 && delta < (((int64_t) era->stop_date[0]
897 - (int64_t) era->start_date[0])
898 * era->absolute_direction))
900 s.decided = loc;
901 break;
905 if (s.era_cnt != (int) num_eras)
906 break;
908 s.era_cnt = -1;
909 if (s.decided == loc)
910 return NULL;
912 s.decided = raw;
915 goto match_year_in_century;
916 case 'Y':
917 if (s.decided != raw)
919 num_eras = _NL_CURRENT_WORD (LC_TIME,
920 _NL_TIME_ERA_NUM_ENTRIES);
921 for (s.era_cnt = 0; s.era_cnt < (int) num_eras;
922 ++s.era_cnt, rp = rp_backup)
924 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
925 if (era != NULL && recursive (era->era_format))
926 break;
928 if (s.era_cnt == (int) num_eras)
930 s.era_cnt = -1;
931 if (s.decided == loc)
932 return NULL;
933 else
934 rp = rp_backup;
936 else
938 s.decided = loc;
939 break;
942 s.decided = raw;
944 get_number (0, 9999, 4);
945 tm->tm_year = val - 1900;
946 s.want_century = 0;
947 s.want_xday = 1;
948 break;
949 case 'x':
950 if (s.decided != raw)
952 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
954 if (*fmt == '\0')
955 fmt = _NL_CURRENT (LC_TIME, D_FMT);
957 if (!recursive (fmt))
959 if (s.decided == loc)
960 return NULL;
961 else
962 rp = rp_backup;
964 else
966 if (strcmp (fmt, HERE_D_FMT))
967 s.decided = loc;
968 break;
970 s.decided = raw;
972 if (!recursive (HERE_D_FMT))
973 return NULL;
974 break;
975 case 'X':
976 if (s.decided != raw)
978 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
980 if (*fmt == '\0')
981 fmt = _NL_CURRENT (LC_TIME, T_FMT);
983 if (!recursive (fmt))
985 if (s.decided == loc)
986 return NULL;
987 else
988 rp = rp_backup;
990 else
992 if (strcmp (fmt, HERE_T_FMT))
993 s.decided = loc;
994 break;
996 s.decided = raw;
998 if (!recursive (HERE_T_FMT))
999 return NULL;
1000 break;
1001 default:
1002 return NULL;
1004 break;
1005 #else
1006 /* We have no information about the era format. Just use
1007 the normal format. */
1008 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
1009 && *fmt != 'x' && *fmt != 'X')
1010 /* This is an illegal format. */
1011 return NULL;
1013 goto start_over;
1014 #endif
1015 case 'O':
1016 switch (*fmt++)
1018 case 'd':
1019 case 'e':
1020 /* Match day of month using alternate numeric symbols. */
1021 get_alt_number (1, 31, 2);
1022 tm->tm_mday = val;
1023 s.have_mday = 1;
1024 s.want_xday = 1;
1025 break;
1026 case 'H':
1027 /* Match hour in 24-hour clock using alternate numeric
1028 symbols. */
1029 get_alt_number (0, 23, 2);
1030 tm->tm_hour = val;
1031 s.have_I = 0;
1032 break;
1033 case 'I':
1034 /* Match hour in 12-hour clock using alternate numeric
1035 symbols. */
1036 get_alt_number (1, 12, 2);
1037 tm->tm_hour = val % 12;
1038 s.have_I = 1;
1039 break;
1040 case 'm':
1041 /* Match month using alternate numeric symbols. */
1042 get_alt_number (1, 12, 2);
1043 tm->tm_mon = val - 1;
1044 s.have_mon = 1;
1045 s.want_xday = 1;
1046 break;
1047 case 'M':
1048 /* Match minutes using alternate numeric symbols. */
1049 get_alt_number (0, 59, 2);
1050 tm->tm_min = val;
1051 break;
1052 case 'S':
1053 /* Match seconds using alternate numeric symbols. */
1054 get_alt_number (0, 61, 2);
1055 tm->tm_sec = val;
1056 break;
1057 case 'U':
1058 get_alt_number (0, 53, 2);
1059 s.week_no = val;
1060 s.have_uweek = 1;
1061 break;
1062 case 'W':
1063 get_alt_number (0, 53, 2);
1064 s.week_no = val;
1065 s.have_wweek = 1;
1066 break;
1067 case 'V':
1068 get_alt_number (0, 53, 2);
1069 /* XXX This cannot determine any field in TM without
1070 further information. */
1071 break;
1072 case 'w':
1073 /* Match number of weekday using alternate numeric symbols. */
1074 get_alt_number (0, 6, 1);
1075 tm->tm_wday = val;
1076 s.have_wday = 1;
1077 break;
1078 case 'y':
1079 /* Match year within century using alternate numeric symbols. */
1080 get_alt_number (0, 99, 2);
1081 tm->tm_year = val >= 69 ? val : val + 100;
1082 s.want_xday = 1;
1083 break;
1084 default:
1085 return NULL;
1087 break;
1088 default:
1089 return NULL;
1093 if (statep != NULL)
1095 /* Recursive invocation, returning success, so
1096 update parent's struct tm and state. */
1097 *(struct __strptime_state *) statep = s;
1098 *tmp = tmb;
1099 return (char *) rp;
1102 if (s.have_I && s.is_pm)
1103 tm->tm_hour += 12;
1105 if (s.century != -1)
1107 if (s.want_century)
1108 tm->tm_year = tm->tm_year % 100 + (s.century - 19) * 100;
1109 else
1110 /* Only the century, but not the year. Strange, but so be it. */
1111 tm->tm_year = (s.century - 19) * 100;
1114 if (s.era_cnt != -1)
1116 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
1117 if (era == NULL)
1118 return NULL;
1119 if (s.want_era)
1120 tm->tm_year = (era->start_date[0]
1121 + ((tm->tm_year - era->offset)
1122 * era->absolute_direction));
1123 else
1124 /* Era start year assumed. */
1125 tm->tm_year = era->start_date[0];
1127 else
1128 if (s.want_era)
1130 /* No era found but we have seen an E modifier. Rectify some
1131 values. */
1132 if (s.want_century && s.century == -1 && tm->tm_year < 69)
1133 tm->tm_year += 100;
1136 if (s.want_xday && !s.have_wday)
1138 if ( !(s.have_mon && s.have_mday) && s.have_yday)
1140 /* We don't have tm_mon and/or tm_mday, compute them. */
1141 int t_mon = 0;
1142 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1143 t_mon++;
1144 if (!s.have_mon)
1145 tm->tm_mon = t_mon - 1;
1146 if (!s.have_mday)
1147 tm->tm_mday =
1148 (tm->tm_yday
1149 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1150 s.have_mon = 1;
1151 s.have_mday = 1;
1153 /* Don't crash in day_of_the_week if tm_mon is uninitialized. */
1154 if (s.have_mon || (unsigned) tm->tm_mon <= 11)
1155 day_of_the_week (tm);
1158 if (s.want_xday && !s.have_yday && (s.have_mon || (unsigned) tm->tm_mon <= 11))
1159 day_of_the_year (tm);
1161 if ((s.have_uweek || s.have_wweek) && s.have_wday)
1163 int save_wday = tm->tm_wday;
1164 int save_mday = tm->tm_mday;
1165 int save_mon = tm->tm_mon;
1166 int w_offset = s.have_uweek ? 0 : 1;
1168 tm->tm_mday = 1;
1169 tm->tm_mon = 0;
1170 day_of_the_week (tm);
1171 if (s.have_mday)
1172 tm->tm_mday = save_mday;
1173 if (s.have_mon)
1174 tm->tm_mon = save_mon;
1176 if (!s.have_yday)
1177 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1178 + (s.week_no - 1) * 7
1179 + (save_wday - w_offset + 7) % 7);
1181 if (!s.have_mday || !s.have_mon)
1183 int t_mon = 0;
1184 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1185 <= tm->tm_yday)
1186 t_mon++;
1187 if (!s.have_mon)
1188 tm->tm_mon = t_mon - 1;
1189 if (!s.have_mday)
1190 tm->tm_mday =
1191 (tm->tm_yday
1192 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1195 tm->tm_wday = save_wday;
1198 return (char *) rp;
1202 char *
1203 strptime (const char *buf, const char *format, struct tm *tm LOCALE_PARAM)
1205 return __strptime_internal (buf, format, tm, NULL LOCALE_ARG);
1208 #ifdef _LIBC
1209 weak_alias (__strptime_l, strptime_l)
1210 #endif