Convert 113 more function definitions to prototype style (files with assertions).
[glibc.git] / time / strptime_l.c
blob454c6ad6c4616854b958d3b6c2ded517870341b9
1 /* Copyright (C) 2002-2015 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
185 # define LOCALE_ARG , locale
186 # define LOCALE_PARAM_PROTO , __locale_t locale
187 # define LOCALE_PARAM_DECL __locale_t locale;
188 # define HELPER_LOCALE_ARG , current
189 # define ISSPACE(Ch) __isspace_l (Ch, locale)
190 #else
191 # define LOCALE_PARAM
192 # define LOCALE_ARG
193 # define LOCALE_PARAM_DECL
194 # define LOCALE_PARAM_PROTO
195 # define HELPER_LOCALE_ARG
196 # define ISSPACE(Ch) isspace (Ch)
197 #endif
202 #ifndef __isleap
203 /* Nonzero if YEAR is a leap year (every 4 years,
204 except every 100th isn't, and every 400th is). */
205 # define __isleap(year) \
206 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
207 #endif
209 /* Compute the day of the week. */
210 static void
211 day_of_the_week (struct tm *tm)
213 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
214 difference between this data in the one on TM and so determine
215 the weekday. */
216 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
217 int wday = (-473
218 + (365 * (tm->tm_year - 70))
219 + (corr_year / 4)
220 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
221 + (((corr_year / 4) / 25) / 4)
222 + __mon_yday[0][tm->tm_mon]
223 + tm->tm_mday - 1);
224 tm->tm_wday = ((wday % 7) + 7) % 7;
227 /* Compute the day of the year. */
228 static void
229 day_of_the_year (struct tm *tm)
231 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
232 + (tm->tm_mday - 1));
236 #ifdef _LIBC
237 char *
238 internal_function
239 #else
240 static char *
241 #endif
242 __strptime_internal (rp, fmt, tmp, statep LOCALE_PARAM)
243 const char *rp;
244 const char *fmt;
245 struct tm *tmp;
246 void *statep;
247 LOCALE_PARAM_DECL
249 #ifdef _LIBC
250 struct __locale_data *const current = locale->__locales[LC_TIME];
251 #endif
253 const char *rp_backup;
254 const char *rp_longest;
255 int cnt;
256 int cnt_longest;
257 size_t val;
258 size_t num_eras;
259 struct era_entry *era = NULL;
260 enum ptime_locale_status { not, loc, raw } decided_longest;
261 struct __strptime_state
263 unsigned int have_I : 1;
264 unsigned int have_wday : 1;
265 unsigned int have_yday : 1;
266 unsigned int have_mon : 1;
267 unsigned int have_mday : 1;
268 unsigned int have_uweek : 1;
269 unsigned int have_wweek : 1;
270 unsigned int is_pm : 1;
271 unsigned int want_century : 1;
272 unsigned int want_era : 1;
273 unsigned int want_xday : 1;
274 enum ptime_locale_status decided : 2;
275 signed char week_no;
276 signed char century;
277 int era_cnt;
278 } s;
279 struct tm tmb;
280 struct tm *tm;
282 if (statep == NULL)
284 memset (&s, 0, sizeof (s));
285 s.century = -1;
286 s.era_cnt = -1;
287 #ifdef _NL_CURRENT
288 s.decided = not;
289 #else
290 s.decided = raw;
291 #endif
292 tm = tmp;
294 else
296 s = *(struct __strptime_state *) statep;
297 tmb = *tmp;
298 tm = &tmb;
301 while (*fmt != '\0')
303 /* A white space in the format string matches 0 more or white
304 space in the input string. */
305 if (ISSPACE (*fmt))
307 while (ISSPACE (*rp))
308 ++rp;
309 ++fmt;
310 continue;
313 /* Any character but `%' must be matched by the same character
314 in the iput string. */
315 if (*fmt != '%')
317 match_char (*fmt++, *rp++);
318 continue;
321 ++fmt;
322 /* We discard strftime modifiers. */
323 while (*fmt == '-' || *fmt == '_' || *fmt == '0'
324 || *fmt == '^' || *fmt == '#')
325 ++fmt;
327 /* And field width. */
328 while (*fmt >= '0' && *fmt <= '9')
329 ++fmt;
331 #ifndef _NL_CURRENT
332 /* We need this for handling the `E' modifier. */
333 start_over:
334 #endif
336 /* Make back up of current processing pointer. */
337 rp_backup = rp;
339 switch (*fmt++)
341 case '%':
342 /* Match the `%' character itself. */
343 match_char ('%', *rp++);
344 break;
345 case 'a':
346 case 'A':
347 /* Match day of week. */
348 rp_longest = NULL;
349 decided_longest = s.decided;
350 cnt_longest = -1;
351 for (cnt = 0; cnt < 7; ++cnt)
353 const char *trp;
354 #ifdef _NL_CURRENT
355 if (s.decided !=raw)
357 trp = rp;
358 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), trp)
359 && trp > rp_longest)
361 rp_longest = trp;
362 cnt_longest = cnt;
363 if (s.decided == not
364 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
365 weekday_name[cnt]))
366 decided_longest = loc;
368 trp = rp;
369 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), trp)
370 && trp > rp_longest)
372 rp_longest = trp;
373 cnt_longest = cnt;
374 if (s.decided == not
375 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
376 ab_weekday_name[cnt]))
377 decided_longest = loc;
380 #endif
381 if (s.decided != loc
382 && (((trp = rp, match_string (weekday_name[cnt], trp))
383 && trp > rp_longest)
384 || ((trp = rp, match_string (ab_weekday_name[cnt], rp))
385 && trp > rp_longest)))
387 rp_longest = trp;
388 cnt_longest = cnt;
389 decided_longest = raw;
392 if (rp_longest == NULL)
393 /* Does not match a weekday name. */
394 return NULL;
395 rp = rp_longest;
396 s.decided = decided_longest;
397 tm->tm_wday = cnt_longest;
398 s.have_wday = 1;
399 break;
400 case 'b':
401 case 'B':
402 case 'h':
403 /* Match month name. */
404 rp_longest = NULL;
405 decided_longest = s.decided;
406 cnt_longest = -1;
407 for (cnt = 0; cnt < 12; ++cnt)
409 const char *trp;
410 #ifdef _NL_CURRENT
411 if (s.decided !=raw)
413 trp = rp;
414 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), trp)
415 && trp > rp_longest)
417 rp_longest = trp;
418 cnt_longest = cnt;
419 if (s.decided == not
420 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
421 month_name[cnt]))
422 decided_longest = loc;
424 trp = rp;
425 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), trp)
426 && trp > rp_longest)
428 rp_longest = trp;
429 cnt_longest = cnt;
430 if (s.decided == not
431 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
432 ab_month_name[cnt]))
433 decided_longest = loc;
436 #endif
437 if (s.decided != loc
438 && (((trp = rp, match_string (month_name[cnt], trp))
439 && trp > rp_longest)
440 || ((trp = rp, match_string (ab_month_name[cnt], trp))
441 && trp > rp_longest)))
443 rp_longest = trp;
444 cnt_longest = cnt;
445 decided_longest = raw;
448 if (rp_longest == NULL)
449 /* Does not match a month name. */
450 return NULL;
451 rp = rp_longest;
452 s.decided = decided_longest;
453 tm->tm_mon = cnt_longest;
454 s.have_mon = 1;
455 s.want_xday = 1;
456 break;
457 case 'c':
458 /* Match locale's date and time format. */
459 #ifdef _NL_CURRENT
460 if (s.decided != raw)
462 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
464 if (s.decided == loc)
465 return NULL;
466 else
467 rp = rp_backup;
469 else
471 if (s.decided == not &&
472 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
473 s.decided = loc;
474 s.want_xday = 1;
475 break;
477 s.decided = raw;
479 #endif
480 if (!recursive (HERE_D_T_FMT))
481 return NULL;
482 s.want_xday = 1;
483 break;
484 case 'C':
485 /* Match century number. */
486 match_century:
487 get_number (0, 99, 2);
488 s.century = val;
489 s.want_xday = 1;
490 break;
491 case 'd':
492 case 'e':
493 /* Match day of month. */
494 get_number (1, 31, 2);
495 tm->tm_mday = val;
496 s.have_mday = 1;
497 s.want_xday = 1;
498 break;
499 case 'F':
500 if (!recursive ("%Y-%m-%d"))
501 return NULL;
502 s.want_xday = 1;
503 break;
504 case 'x':
505 #ifdef _NL_CURRENT
506 if (s.decided != raw)
508 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
510 if (s.decided == loc)
511 return NULL;
512 else
513 rp = rp_backup;
515 else
517 if (s.decided == not
518 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
519 s.decided = loc;
520 s.want_xday = 1;
521 break;
523 s.decided = raw;
525 #endif
526 /* Fall through. */
527 case 'D':
528 /* Match standard day format. */
529 if (!recursive (HERE_D_FMT))
530 return NULL;
531 s.want_xday = 1;
532 break;
533 case 'k':
534 case 'H':
535 /* Match hour in 24-hour clock. */
536 get_number (0, 23, 2);
537 tm->tm_hour = val;
538 s.have_I = 0;
539 break;
540 case 'l':
541 /* Match hour in 12-hour clock. GNU extension. */
542 case 'I':
543 /* Match hour in 12-hour clock. */
544 get_number (1, 12, 2);
545 tm->tm_hour = val % 12;
546 s.have_I = 1;
547 break;
548 case 'j':
549 /* Match day number of year. */
550 get_number (1, 366, 3);
551 tm->tm_yday = val - 1;
552 s.have_yday = 1;
553 break;
554 case 'm':
555 /* Match number of month. */
556 get_number (1, 12, 2);
557 tm->tm_mon = val - 1;
558 s.have_mon = 1;
559 s.want_xday = 1;
560 break;
561 case 'M':
562 /* Match minute. */
563 get_number (0, 59, 2);
564 tm->tm_min = val;
565 break;
566 case 'n':
567 case 't':
568 /* Match any white space. */
569 while (ISSPACE (*rp))
570 ++rp;
571 break;
572 case 'p':
573 /* Match locale's equivalent of AM/PM. */
574 #ifdef _NL_CURRENT
575 if (s.decided != raw)
577 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
579 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
580 s.decided = loc;
581 s.is_pm = 0;
582 break;
584 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
586 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
587 s.decided = loc;
588 s.is_pm = 1;
589 break;
591 s.decided = raw;
593 #endif
594 if (!match_string (HERE_AM_STR, rp))
596 if (match_string (HERE_PM_STR, rp))
597 s.is_pm = 1;
598 else
599 return NULL;
601 else
602 s.is_pm = 0;
603 break;
604 case 'r':
605 #ifdef _NL_CURRENT
606 if (s.decided != raw)
608 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
610 if (s.decided == loc)
611 return NULL;
612 else
613 rp = rp_backup;
615 else
617 if (s.decided == not &&
618 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
619 HERE_T_FMT_AMPM))
620 s.decided = loc;
621 break;
623 s.decided = raw;
625 #endif
626 if (!recursive (HERE_T_FMT_AMPM))
627 return NULL;
628 break;
629 case 'R':
630 if (!recursive ("%H:%M"))
631 return NULL;
632 break;
633 case 's':
635 /* The number of seconds may be very high so we cannot use
636 the `get_number' macro. Instead read the number
637 character for character and construct the result while
638 doing this. */
639 time_t secs = 0;
640 if (*rp < '0' || *rp > '9')
641 /* We need at least one digit. */
642 return NULL;
646 secs *= 10;
647 secs += *rp++ - '0';
649 while (*rp >= '0' && *rp <= '9');
651 if (localtime_r (&secs, tm) == NULL)
652 /* Error in function. */
653 return NULL;
655 break;
656 case 'S':
657 get_number (0, 61, 2);
658 tm->tm_sec = val;
659 break;
660 case 'X':
661 #ifdef _NL_CURRENT
662 if (s.decided != raw)
664 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
666 if (s.decided == loc)
667 return NULL;
668 else
669 rp = rp_backup;
671 else
673 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
674 s.decided = loc;
675 break;
677 s.decided = raw;
679 #endif
680 /* Fall through. */
681 case 'T':
682 if (!recursive (HERE_T_FMT))
683 return NULL;
684 break;
685 case 'u':
686 get_number (1, 7, 1);
687 tm->tm_wday = val % 7;
688 s.have_wday = 1;
689 break;
690 case 'g':
691 get_number (0, 99, 2);
692 /* XXX This cannot determine any field in TM. */
693 break;
694 case 'G':
695 if (*rp < '0' || *rp > '9')
696 return NULL;
697 /* XXX Ignore the number since we would need some more
698 information to compute a real date. */
700 ++rp;
701 while (*rp >= '0' && *rp <= '9');
702 break;
703 case 'U':
704 get_number (0, 53, 2);
705 s.week_no = val;
706 s.have_uweek = 1;
707 break;
708 case 'W':
709 get_number (0, 53, 2);
710 s.week_no = val;
711 s.have_wweek = 1;
712 break;
713 case 'V':
714 get_number (0, 53, 2);
715 /* XXX This cannot determine any field in TM without some
716 information. */
717 break;
718 case 'w':
719 /* Match number of weekday. */
720 get_number (0, 6, 1);
721 tm->tm_wday = val;
722 s.have_wday = 1;
723 break;
724 case 'y':
725 match_year_in_century:
726 /* Match year within century. */
727 get_number (0, 99, 2);
728 /* The "Year 2000: The Millennium Rollover" paper suggests that
729 values in the range 69-99 refer to the twentieth century. */
730 tm->tm_year = val >= 69 ? val : val + 100;
731 /* Indicate that we want to use the century, if specified. */
732 s.want_century = 1;
733 s.want_xday = 1;
734 break;
735 case 'Y':
736 /* Match year including century number. */
737 get_number (0, 9999, 4);
738 tm->tm_year = val - 1900;
739 s.want_century = 0;
740 s.want_xday = 1;
741 break;
742 case 'Z':
743 /* Read timezone but perform no conversion. */
744 while (ISSPACE (*rp))
745 rp++;
746 while (!ISSPACE (*rp) && *rp != '\0')
747 rp++;
748 break;
749 case 'z':
750 /* We recognize four formats:
751 1. Two digits specify hours.
752 2. Four digits specify hours and minutes.
753 3. Two digits, ':', and two digits specify hours and minutes.
754 4. 'Z' is equivalent to +0000. */
756 val = 0;
757 while (ISSPACE (*rp))
758 ++rp;
759 if (*rp == 'Z')
761 ++rp;
762 tm->tm_gmtoff = 0;
763 break;
765 if (*rp != '+' && *rp != '-')
766 return NULL;
767 bool neg = *rp++ == '-';
768 int n = 0;
769 while (n < 4 && *rp >= '0' && *rp <= '9')
771 val = val * 10 + *rp++ - '0';
772 ++n;
773 if (*rp == ':' && n == 2 && isdigit (*(rp + 1)))
774 ++rp;
776 if (n == 2)
777 val *= 100;
778 else if (n != 4)
779 /* Only two or four digits recognized. */
780 return NULL;
781 else if (val % 100 >= 60)
782 /* Minutes valid range is 0 through 59. */
783 return NULL;
784 tm->tm_gmtoff = (val / 100) * 3600 + (val % 100) * 60;
785 if (neg)
786 tm->tm_gmtoff = -tm->tm_gmtoff;
788 break;
789 case 'E':
790 #ifdef _NL_CURRENT
791 switch (*fmt++)
793 case 'c':
794 /* Match locale's alternate date and time format. */
795 if (s.decided != raw)
797 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
799 if (*fmt == '\0')
800 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
802 if (!recursive (fmt))
804 if (s.decided == loc)
805 return NULL;
806 else
807 rp = rp_backup;
809 else
811 if (strcmp (fmt, HERE_D_T_FMT))
812 s.decided = loc;
813 s.want_xday = 1;
814 break;
816 s.decided = raw;
818 /* The C locale has no era information, so use the
819 normal representation. */
820 if (!recursive (HERE_D_T_FMT))
821 return NULL;
822 s.want_xday = 1;
823 break;
824 case 'C':
825 if (s.decided != raw)
827 if (s.era_cnt >= 0)
829 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
830 if (era != NULL && match_string (era->era_name, rp))
832 s.decided = loc;
833 break;
835 else
836 return NULL;
839 num_eras = _NL_CURRENT_WORD (LC_TIME,
840 _NL_TIME_ERA_NUM_ENTRIES);
841 for (s.era_cnt = 0; s.era_cnt < (int) num_eras;
842 ++s.era_cnt, rp = rp_backup)
844 era = _nl_select_era_entry (s.era_cnt
845 HELPER_LOCALE_ARG);
846 if (era != NULL && match_string (era->era_name, rp))
848 s.decided = loc;
849 break;
852 if (s.era_cnt != (int) num_eras)
853 break;
855 s.era_cnt = -1;
856 if (s.decided == loc)
857 return NULL;
859 s.decided = raw;
861 /* The C locale has no era information, so use the
862 normal representation. */
863 goto match_century;
864 case 'y':
865 if (s.decided != raw)
867 get_number(0, 9999, 4);
868 tm->tm_year = val;
869 s.want_era = 1;
870 s.want_xday = 1;
871 s.want_century = 1;
873 if (s.era_cnt >= 0)
875 assert (s.decided == loc);
877 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
878 bool match = false;
879 if (era != NULL)
881 int delta = ((tm->tm_year - era->offset)
882 * era->absolute_direction);
883 match = (delta >= 0
884 && delta < (((int64_t) era->stop_date[0]
885 - (int64_t) era->start_date[0])
886 * era->absolute_direction));
888 if (! match)
889 return NULL;
891 break;
894 num_eras = _NL_CURRENT_WORD (LC_TIME,
895 _NL_TIME_ERA_NUM_ENTRIES);
896 for (s.era_cnt = 0; s.era_cnt < (int) num_eras; ++s.era_cnt)
898 era = _nl_select_era_entry (s.era_cnt
899 HELPER_LOCALE_ARG);
900 if (era != NULL)
902 int delta = ((tm->tm_year - era->offset)
903 * era->absolute_direction);
904 if (delta >= 0
905 && delta < (((int64_t) era->stop_date[0]
906 - (int64_t) era->start_date[0])
907 * era->absolute_direction))
909 s.decided = loc;
910 break;
914 if (s.era_cnt != (int) num_eras)
915 break;
917 s.era_cnt = -1;
918 if (s.decided == loc)
919 return NULL;
921 s.decided = raw;
924 goto match_year_in_century;
925 case 'Y':
926 if (s.decided != raw)
928 num_eras = _NL_CURRENT_WORD (LC_TIME,
929 _NL_TIME_ERA_NUM_ENTRIES);
930 for (s.era_cnt = 0; s.era_cnt < (int) num_eras;
931 ++s.era_cnt, rp = rp_backup)
933 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
934 if (era != NULL && recursive (era->era_format))
935 break;
937 if (s.era_cnt == (int) num_eras)
939 s.era_cnt = -1;
940 if (s.decided == loc)
941 return NULL;
942 else
943 rp = rp_backup;
945 else
947 s.decided = loc;
948 break;
951 s.decided = raw;
953 get_number (0, 9999, 4);
954 tm->tm_year = val - 1900;
955 s.want_century = 0;
956 s.want_xday = 1;
957 break;
958 case 'x':
959 if (s.decided != raw)
961 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
963 if (*fmt == '\0')
964 fmt = _NL_CURRENT (LC_TIME, D_FMT);
966 if (!recursive (fmt))
968 if (s.decided == loc)
969 return NULL;
970 else
971 rp = rp_backup;
973 else
975 if (strcmp (fmt, HERE_D_FMT))
976 s.decided = loc;
977 break;
979 s.decided = raw;
981 if (!recursive (HERE_D_FMT))
982 return NULL;
983 break;
984 case 'X':
985 if (s.decided != raw)
987 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
989 if (*fmt == '\0')
990 fmt = _NL_CURRENT (LC_TIME, T_FMT);
992 if (!recursive (fmt))
994 if (s.decided == loc)
995 return NULL;
996 else
997 rp = rp_backup;
999 else
1001 if (strcmp (fmt, HERE_T_FMT))
1002 s.decided = loc;
1003 break;
1005 s.decided = raw;
1007 if (!recursive (HERE_T_FMT))
1008 return NULL;
1009 break;
1010 default:
1011 return NULL;
1013 break;
1014 #else
1015 /* We have no information about the era format. Just use
1016 the normal format. */
1017 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
1018 && *fmt != 'x' && *fmt != 'X')
1019 /* This is an illegal format. */
1020 return NULL;
1022 goto start_over;
1023 #endif
1024 case 'O':
1025 switch (*fmt++)
1027 case 'd':
1028 case 'e':
1029 /* Match day of month using alternate numeric symbols. */
1030 get_alt_number (1, 31, 2);
1031 tm->tm_mday = val;
1032 s.have_mday = 1;
1033 s.want_xday = 1;
1034 break;
1035 case 'H':
1036 /* Match hour in 24-hour clock using alternate numeric
1037 symbols. */
1038 get_alt_number (0, 23, 2);
1039 tm->tm_hour = val;
1040 s.have_I = 0;
1041 break;
1042 case 'I':
1043 /* Match hour in 12-hour clock using alternate numeric
1044 symbols. */
1045 get_alt_number (1, 12, 2);
1046 tm->tm_hour = val % 12;
1047 s.have_I = 1;
1048 break;
1049 case 'm':
1050 /* Match month using alternate numeric symbols. */
1051 get_alt_number (1, 12, 2);
1052 tm->tm_mon = val - 1;
1053 s.have_mon = 1;
1054 s.want_xday = 1;
1055 break;
1056 case 'M':
1057 /* Match minutes using alternate numeric symbols. */
1058 get_alt_number (0, 59, 2);
1059 tm->tm_min = val;
1060 break;
1061 case 'S':
1062 /* Match seconds using alternate numeric symbols. */
1063 get_alt_number (0, 61, 2);
1064 tm->tm_sec = val;
1065 break;
1066 case 'U':
1067 get_alt_number (0, 53, 2);
1068 s.week_no = val;
1069 s.have_uweek = 1;
1070 break;
1071 case 'W':
1072 get_alt_number (0, 53, 2);
1073 s.week_no = val;
1074 s.have_wweek = 1;
1075 break;
1076 case 'V':
1077 get_alt_number (0, 53, 2);
1078 /* XXX This cannot determine any field in TM without
1079 further information. */
1080 break;
1081 case 'w':
1082 /* Match number of weekday using alternate numeric symbols. */
1083 get_alt_number (0, 6, 1);
1084 tm->tm_wday = val;
1085 s.have_wday = 1;
1086 break;
1087 case 'y':
1088 /* Match year within century using alternate numeric symbols. */
1089 get_alt_number (0, 99, 2);
1090 tm->tm_year = val >= 69 ? val : val + 100;
1091 s.want_xday = 1;
1092 break;
1093 default:
1094 return NULL;
1096 break;
1097 default:
1098 return NULL;
1102 if (statep != NULL)
1104 /* Recursive invocation, returning success, so
1105 update parent's struct tm and state. */
1106 *(struct __strptime_state *) statep = s;
1107 *tmp = tmb;
1108 return (char *) rp;
1111 if (s.have_I && s.is_pm)
1112 tm->tm_hour += 12;
1114 if (s.century != -1)
1116 if (s.want_century)
1117 tm->tm_year = tm->tm_year % 100 + (s.century - 19) * 100;
1118 else
1119 /* Only the century, but not the year. Strange, but so be it. */
1120 tm->tm_year = (s.century - 19) * 100;
1123 if (s.era_cnt != -1)
1125 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
1126 if (era == NULL)
1127 return NULL;
1128 if (s.want_era)
1129 tm->tm_year = (era->start_date[0]
1130 + ((tm->tm_year - era->offset)
1131 * era->absolute_direction));
1132 else
1133 /* Era start year assumed. */
1134 tm->tm_year = era->start_date[0];
1136 else
1137 if (s.want_era)
1139 /* No era found but we have seen an E modifier. Rectify some
1140 values. */
1141 if (s.want_century && s.century == -1 && tm->tm_year < 69)
1142 tm->tm_year += 100;
1145 if (s.want_xday && !s.have_wday)
1147 if ( !(s.have_mon && s.have_mday) && s.have_yday)
1149 /* We don't have tm_mon and/or tm_mday, compute them. */
1150 int t_mon = 0;
1151 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1152 t_mon++;
1153 if (!s.have_mon)
1154 tm->tm_mon = t_mon - 1;
1155 if (!s.have_mday)
1156 tm->tm_mday =
1157 (tm->tm_yday
1158 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1159 s.have_mon = 1;
1160 s.have_mday = 1;
1162 /* Don't crash in day_of_the_week if tm_mon is uninitialized. */
1163 if (s.have_mon || (unsigned) tm->tm_mon <= 11)
1164 day_of_the_week (tm);
1167 if (s.want_xday && !s.have_yday && (s.have_mon || (unsigned) tm->tm_mon <= 11))
1168 day_of_the_year (tm);
1170 if ((s.have_uweek || s.have_wweek) && s.have_wday)
1172 int save_wday = tm->tm_wday;
1173 int save_mday = tm->tm_mday;
1174 int save_mon = tm->tm_mon;
1175 int w_offset = s.have_uweek ? 0 : 1;
1177 tm->tm_mday = 1;
1178 tm->tm_mon = 0;
1179 day_of_the_week (tm);
1180 if (s.have_mday)
1181 tm->tm_mday = save_mday;
1182 if (s.have_mon)
1183 tm->tm_mon = save_mon;
1185 if (!s.have_yday)
1186 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1187 + (s.week_no - 1) * 7
1188 + (save_wday - w_offset + 7) % 7);
1190 if (!s.have_mday || !s.have_mon)
1192 int t_mon = 0;
1193 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1194 <= tm->tm_yday)
1195 t_mon++;
1196 if (!s.have_mon)
1197 tm->tm_mon = t_mon - 1;
1198 if (!s.have_mday)
1199 tm->tm_mday =
1200 (tm->tm_yday
1201 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1204 tm->tm_wday = save_wday;
1207 return (char *) rp;
1211 char *
1212 strptime (buf, format, tm LOCALE_PARAM)
1213 const char *buf;
1214 const char *format;
1215 struct tm *tm;
1216 LOCALE_PARAM_DECL
1218 return __strptime_internal (buf, format, tm, NULL LOCALE_ARG);
1221 #ifdef _LIBC
1222 weak_alias (__strptime_l, strptime_l)
1223 #endif