don't use Bash-specific ${parameter/pattern/string} expansion
[glibc.git] / time / strptime_l.c
blob00fc1ef59426fd093ca002bbfa15f7c9b71d184b
1 /* Copyright (C) 2002-2013 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 # include "../locale/localeinfo.h"
32 #endif
35 #if ! HAVE_LOCALTIME_R && ! defined localtime_r
36 # ifdef _LIBC
37 # define localtime_r __localtime_r
38 # else
39 /* Approximate localtime_r as best we can in its absence. */
40 # define localtime_r my_localtime_r
41 static struct tm *localtime_r (const time_t *, struct tm *);
42 static struct tm *
43 localtime_r (t, tp)
44 const time_t *t;
45 struct tm *tp;
47 struct tm *l = localtime (t);
48 if (! l)
49 return 0;
50 *tp = *l;
51 return tp;
53 # endif /* ! _LIBC */
54 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
57 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
58 #if defined __GNUC__ && __GNUC__ >= 2
59 # define match_string(cs1, s2) \
60 ({ size_t len = strlen (cs1); \
61 int result = __strncasecmp_l ((cs1), (s2), len, locale) == 0; \
62 if (result) (s2) += len; \
63 result; })
64 #else
65 /* Oh come on. Get a reasonable compiler. */
66 # define match_string(cs1, s2) \
67 (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
68 #endif
69 /* We intentionally do not use isdigit() for testing because this will
70 lead to problems with the wide character version. */
71 #define get_number(from, to, n) \
72 do { \
73 int __n = n; \
74 val = 0; \
75 while (ISSPACE (*rp)) \
76 ++rp; \
77 if (*rp < '0' || *rp > '9') \
78 return NULL; \
79 do { \
80 val *= 10; \
81 val += *rp++ - '0'; \
82 } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
83 if (val < from || val > to) \
84 return NULL; \
85 } while (0)
86 #ifdef _NL_CURRENT
87 # define get_alt_number(from, to, n) \
88 ({ \
89 __label__ do_normal; \
91 if (s.decided != raw) \
92 { \
93 val = _nl_parse_alt_digit (&rp HELPER_LOCALE_ARG); \
94 if (val == -1 && s.decided != loc) \
95 { \
96 s.decided = loc; \
97 goto do_normal; \
98 } \
99 if (val < from || val > to) \
100 return NULL; \
102 else \
104 do_normal: \
105 get_number (from, to, n); \
107 0; \
109 #else
110 # define get_alt_number(from, to, n) \
111 /* We don't have the alternate representation. */ \
112 get_number(from, to, n)
113 #endif
114 #define recursive(new_fmt) \
115 (*(new_fmt) != '\0' \
116 && (rp = __strptime_internal (rp, (new_fmt), tm, &s LOCALE_ARG)) != NULL)
119 #ifdef _LIBC
120 /* This is defined in locale/C-time.c in the GNU libc. */
121 extern const struct __locale_data _nl_C_LC_TIME attribute_hidden;
123 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
124 # define ab_weekday_name \
125 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
126 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
127 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
128 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
129 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
130 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
131 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
132 # define HERE_T_FMT_AMPM \
133 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
134 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
136 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
137 #else
138 static char const weekday_name[][10] =
140 "Sunday", "Monday", "Tuesday", "Wednesday",
141 "Thursday", "Friday", "Saturday"
143 static char const ab_weekday_name[][4] =
145 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
147 static char const month_name[][10] =
149 "January", "February", "March", "April", "May", "June",
150 "July", "August", "September", "October", "November", "December"
152 static char const ab_month_name[][4] =
154 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
155 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
157 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
158 # define HERE_D_FMT "%m/%d/%y"
159 # define HERE_AM_STR "AM"
160 # define HERE_PM_STR "PM"
161 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
162 # define HERE_T_FMT "%H:%M:%S"
164 static const unsigned short int __mon_yday[2][13] =
166 /* Normal years. */
167 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
168 /* Leap years. */
169 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
171 #endif
173 #if defined _LIBC
174 /* We use this code also for the extended locale handling where the
175 function gets as an additional argument the locale which has to be
176 used. To access the values we have to redefine the _NL_CURRENT
177 macro. */
178 # define strptime __strptime_l
179 # undef _NL_CURRENT
180 # define _NL_CURRENT(category, item) \
181 (current->values[_NL_ITEM_INDEX (item)].string)
182 # undef _NL_CURRENT_WORD
183 # define _NL_CURRENT_WORD(category, item) \
184 (current->values[_NL_ITEM_INDEX (item)].word)
185 # define LOCALE_PARAM , locale
186 # define LOCALE_ARG , locale
187 # define LOCALE_PARAM_PROTO , __locale_t locale
188 # define LOCALE_PARAM_DECL __locale_t locale;
189 # define HELPER_LOCALE_ARG , current
190 # define ISSPACE(Ch) __isspace_l (Ch, locale)
191 #else
192 # define LOCALE_PARAM
193 # define LOCALE_ARG
194 # define LOCALE_PARAM_DECL
195 # define LOCALE_PARAM_PROTO
196 # define HELPER_LOCALE_ARG
197 # define ISSPACE(Ch) isspace (Ch)
198 #endif
203 #ifndef __isleap
204 /* Nonzero if YEAR is a leap year (every 4 years,
205 except every 100th isn't, and every 400th is). */
206 # define __isleap(year) \
207 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
208 #endif
210 /* Compute the day of the week. */
211 static void
212 day_of_the_week (struct tm *tm)
214 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
215 difference between this data in the one on TM and so determine
216 the weekday. */
217 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
218 int wday = (-473
219 + (365 * (tm->tm_year - 70))
220 + (corr_year / 4)
221 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
222 + (((corr_year / 4) / 25) / 4)
223 + __mon_yday[0][tm->tm_mon]
224 + tm->tm_mday - 1);
225 tm->tm_wday = ((wday % 7) + 7) % 7;
228 /* Compute the day of the year. */
229 static void
230 day_of_the_year (struct tm *tm)
232 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
233 + (tm->tm_mday - 1));
237 #ifdef _LIBC
238 char *
239 internal_function
240 #else
241 static char *
242 #endif
243 __strptime_internal (rp, fmt, tmp, statep LOCALE_PARAM)
244 const char *rp;
245 const char *fmt;
246 struct tm *tmp;
247 void *statep;
248 LOCALE_PARAM_DECL
250 #ifdef _LIBC
251 struct __locale_data *const current = locale->__locales[LC_TIME];
252 #endif
254 const char *rp_backup;
255 const char *rp_longest;
256 int cnt;
257 int cnt_longest;
258 size_t val;
259 size_t num_eras;
260 struct era_entry *era = NULL;
261 enum ptime_locale_status { not, loc, raw } decided_longest;
262 struct __strptime_state
264 unsigned int have_I : 1;
265 unsigned int have_wday : 1;
266 unsigned int have_yday : 1;
267 unsigned int have_mon : 1;
268 unsigned int have_mday : 1;
269 unsigned int have_uweek : 1;
270 unsigned int have_wweek : 1;
271 unsigned int is_pm : 1;
272 unsigned int want_century : 1;
273 unsigned int want_era : 1;
274 unsigned int want_xday : 1;
275 enum ptime_locale_status decided : 2;
276 signed char week_no;
277 signed char century;
278 int era_cnt;
279 } s;
280 struct tm tmb;
281 struct tm *tm;
283 if (statep == NULL)
285 memset (&s, 0, sizeof (s));
286 s.century = -1;
287 s.era_cnt = -1;
288 #ifdef _NL_CURRENT
289 s.decided = not;
290 #else
291 s.decided = raw;
292 #endif
293 tm = tmp;
295 else
297 s = *(struct __strptime_state *) statep;
298 tmb = *tmp;
299 tm = &tmb;
302 while (*fmt != '\0')
304 /* A white space in the format string matches 0 more or white
305 space in the input string. */
306 if (ISSPACE (*fmt))
308 while (ISSPACE (*rp))
309 ++rp;
310 ++fmt;
311 continue;
314 /* Any character but `%' must be matched by the same character
315 in the iput string. */
316 if (*fmt != '%')
318 match_char (*fmt++, *rp++);
319 continue;
322 ++fmt;
323 if (statep != NULL)
325 /* In recursive calls silently discard strftime modifiers. */
326 while (*fmt == '-' || *fmt == '_' || *fmt == '0'
327 || *fmt == '^' || *fmt == '#')
328 ++fmt;
330 /* And field width. */
331 while (*fmt >= '0' && *fmt <= '9')
332 ++fmt;
335 #ifndef _NL_CURRENT
336 /* We need this for handling the `E' modifier. */
337 start_over:
338 #endif
340 /* Make back up of current processing pointer. */
341 rp_backup = rp;
343 switch (*fmt++)
345 case '%':
346 /* Match the `%' character itself. */
347 match_char ('%', *rp++);
348 break;
349 case 'a':
350 case 'A':
351 /* Match day of week. */
352 rp_longest = NULL;
353 decided_longest = s.decided;
354 cnt_longest = -1;
355 for (cnt = 0; cnt < 7; ++cnt)
357 const char *trp;
358 #ifdef _NL_CURRENT
359 if (s.decided !=raw)
361 trp = rp;
362 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), trp)
363 && trp > rp_longest)
365 rp_longest = trp;
366 cnt_longest = cnt;
367 if (s.decided == not
368 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
369 weekday_name[cnt]))
370 decided_longest = loc;
372 trp = rp;
373 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), trp)
374 && trp > rp_longest)
376 rp_longest = trp;
377 cnt_longest = cnt;
378 if (s.decided == not
379 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
380 ab_weekday_name[cnt]))
381 decided_longest = loc;
384 #endif
385 if (s.decided != loc
386 && (((trp = rp, match_string (weekday_name[cnt], trp))
387 && trp > rp_longest)
388 || ((trp = rp, match_string (ab_weekday_name[cnt], rp))
389 && trp > rp_longest)))
391 rp_longest = trp;
392 cnt_longest = cnt;
393 decided_longest = raw;
396 if (rp_longest == NULL)
397 /* Does not match a weekday name. */
398 return NULL;
399 rp = rp_longest;
400 s.decided = decided_longest;
401 tm->tm_wday = cnt_longest;
402 s.have_wday = 1;
403 break;
404 case 'b':
405 case 'B':
406 case 'h':
407 /* Match month name. */
408 rp_longest = NULL;
409 decided_longest = s.decided;
410 cnt_longest = -1;
411 for (cnt = 0; cnt < 12; ++cnt)
413 const char *trp;
414 #ifdef _NL_CURRENT
415 if (s.decided !=raw)
417 trp = rp;
418 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), trp)
419 && trp > rp_longest)
421 rp_longest = trp;
422 cnt_longest = cnt;
423 if (s.decided == not
424 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
425 month_name[cnt]))
426 decided_longest = loc;
428 trp = rp;
429 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), trp)
430 && trp > rp_longest)
432 rp_longest = trp;
433 cnt_longest = cnt;
434 if (s.decided == not
435 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
436 ab_month_name[cnt]))
437 decided_longest = loc;
440 #endif
441 if (s.decided != loc
442 && (((trp = rp, match_string (month_name[cnt], trp))
443 && trp > rp_longest)
444 || ((trp = rp, match_string (ab_month_name[cnt], trp))
445 && trp > rp_longest)))
447 rp_longest = trp;
448 cnt_longest = cnt;
449 decided_longest = raw;
452 if (rp_longest == NULL)
453 /* Does not match a month name. */
454 return NULL;
455 rp = rp_longest;
456 s.decided = decided_longest;
457 tm->tm_mon = cnt_longest;
458 s.have_mon = 1;
459 s.want_xday = 1;
460 break;
461 case 'c':
462 /* Match locale's date and time format. */
463 #ifdef _NL_CURRENT
464 if (s.decided != raw)
466 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
468 if (s.decided == loc)
469 return NULL;
470 else
471 rp = rp_backup;
473 else
475 if (s.decided == not &&
476 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
477 s.decided = loc;
478 s.want_xday = 1;
479 break;
481 s.decided = raw;
483 #endif
484 if (!recursive (HERE_D_T_FMT))
485 return NULL;
486 s.want_xday = 1;
487 break;
488 case 'C':
489 /* Match century number. */
490 match_century:
491 get_number (0, 99, 2);
492 s.century = val;
493 s.want_xday = 1;
494 break;
495 case 'd':
496 case 'e':
497 /* Match day of month. */
498 get_number (1, 31, 2);
499 tm->tm_mday = val;
500 s.have_mday = 1;
501 s.want_xday = 1;
502 break;
503 case 'F':
504 if (!recursive ("%Y-%m-%d"))
505 return NULL;
506 s.want_xday = 1;
507 break;
508 case 'x':
509 #ifdef _NL_CURRENT
510 if (s.decided != raw)
512 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
514 if (s.decided == loc)
515 return NULL;
516 else
517 rp = rp_backup;
519 else
521 if (s.decided == not
522 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
523 s.decided = loc;
524 s.want_xday = 1;
525 break;
527 s.decided = raw;
529 #endif
530 /* Fall through. */
531 case 'D':
532 /* Match standard day format. */
533 if (!recursive (HERE_D_FMT))
534 return NULL;
535 s.want_xday = 1;
536 break;
537 case 'k':
538 case 'H':
539 /* Match hour in 24-hour clock. */
540 get_number (0, 23, 2);
541 tm->tm_hour = val;
542 s.have_I = 0;
543 break;
544 case 'l':
545 /* Match hour in 12-hour clock. GNU extension. */
546 case 'I':
547 /* Match hour in 12-hour clock. */
548 get_number (1, 12, 2);
549 tm->tm_hour = val % 12;
550 s.have_I = 1;
551 break;
552 case 'j':
553 /* Match day number of year. */
554 get_number (1, 366, 3);
555 tm->tm_yday = val - 1;
556 s.have_yday = 1;
557 break;
558 case 'm':
559 /* Match number of month. */
560 get_number (1, 12, 2);
561 tm->tm_mon = val - 1;
562 s.have_mon = 1;
563 s.want_xday = 1;
564 break;
565 case 'M':
566 /* Match minute. */
567 get_number (0, 59, 2);
568 tm->tm_min = val;
569 break;
570 case 'n':
571 case 't':
572 /* Match any white space. */
573 while (ISSPACE (*rp))
574 ++rp;
575 break;
576 case 'p':
577 /* Match locale's equivalent of AM/PM. */
578 #ifdef _NL_CURRENT
579 if (s.decided != raw)
581 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
583 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
584 s.decided = loc;
585 s.is_pm = 0;
586 break;
588 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
590 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
591 s.decided = loc;
592 s.is_pm = 1;
593 break;
595 s.decided = raw;
597 #endif
598 if (!match_string (HERE_AM_STR, rp))
600 if (match_string (HERE_PM_STR, rp))
601 s.is_pm = 1;
602 else
603 return NULL;
605 else
606 s.is_pm = 0;
607 break;
608 case 'r':
609 #ifdef _NL_CURRENT
610 if (s.decided != raw)
612 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
614 if (s.decided == loc)
615 return NULL;
616 else
617 rp = rp_backup;
619 else
621 if (s.decided == not &&
622 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
623 HERE_T_FMT_AMPM))
624 s.decided = loc;
625 break;
627 s.decided = raw;
629 #endif
630 if (!recursive (HERE_T_FMT_AMPM))
631 return NULL;
632 break;
633 case 'R':
634 if (!recursive ("%H:%M"))
635 return NULL;
636 break;
637 case 's':
639 /* The number of seconds may be very high so we cannot use
640 the `get_number' macro. Instead read the number
641 character for character and construct the result while
642 doing this. */
643 time_t secs = 0;
644 if (*rp < '0' || *rp > '9')
645 /* We need at least one digit. */
646 return NULL;
650 secs *= 10;
651 secs += *rp++ - '0';
653 while (*rp >= '0' && *rp <= '9');
655 if (localtime_r (&secs, tm) == NULL)
656 /* Error in function. */
657 return NULL;
659 break;
660 case 'S':
661 get_number (0, 61, 2);
662 tm->tm_sec = val;
663 break;
664 case 'X':
665 #ifdef _NL_CURRENT
666 if (s.decided != raw)
668 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
670 if (s.decided == loc)
671 return NULL;
672 else
673 rp = rp_backup;
675 else
677 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
678 s.decided = loc;
679 break;
681 s.decided = raw;
683 #endif
684 /* Fall through. */
685 case 'T':
686 if (!recursive (HERE_T_FMT))
687 return NULL;
688 break;
689 case 'u':
690 get_number (1, 7, 1);
691 tm->tm_wday = val % 7;
692 s.have_wday = 1;
693 break;
694 case 'g':
695 get_number (0, 99, 2);
696 /* XXX This cannot determine any field in TM. */
697 break;
698 case 'G':
699 if (*rp < '0' || *rp > '9')
700 return NULL;
701 /* XXX Ignore the number since we would need some more
702 information to compute a real date. */
704 ++rp;
705 while (*rp >= '0' && *rp <= '9');
706 break;
707 case 'U':
708 get_number (0, 53, 2);
709 s.week_no = val;
710 s.have_uweek = 1;
711 break;
712 case 'W':
713 get_number (0, 53, 2);
714 s.week_no = val;
715 s.have_wweek = 1;
716 break;
717 case 'V':
718 get_number (0, 53, 2);
719 /* XXX This cannot determine any field in TM without some
720 information. */
721 break;
722 case 'w':
723 /* Match number of weekday. */
724 get_number (0, 6, 1);
725 tm->tm_wday = val;
726 s.have_wday = 1;
727 break;
728 case 'y':
729 match_year_in_century:
730 /* Match year within century. */
731 get_number (0, 99, 2);
732 /* The "Year 2000: The Millennium Rollover" paper suggests that
733 values in the range 69-99 refer to the twentieth century. */
734 tm->tm_year = val >= 69 ? val : val + 100;
735 /* Indicate that we want to use the century, if specified. */
736 s.want_century = 1;
737 s.want_xday = 1;
738 break;
739 case 'Y':
740 /* Match year including century number. */
741 get_number (0, 9999, 4);
742 tm->tm_year = val - 1900;
743 s.want_century = 0;
744 s.want_xday = 1;
745 break;
746 case 'Z':
747 /* XXX How to handle this? */
748 break;
749 case 'z':
750 /* We recognize two formats: if two digits are given, these
751 specify hours. If fours digits are used, minutes are
752 also specified. */
754 val = 0;
755 while (ISSPACE (*rp))
756 ++rp;
757 if (*rp != '+' && *rp != '-')
758 return NULL;
759 bool neg = *rp++ == '-';
760 int n = 0;
761 while (n < 4 && *rp >= '0' && *rp <= '9')
763 val = val * 10 + *rp++ - '0';
764 ++n;
766 if (n == 2)
767 val *= 100;
768 else if (n != 4)
769 /* Only two or four digits recognized. */
770 return NULL;
771 else
773 /* We have to convert the minutes into decimal. */
774 if (val % 100 >= 60)
775 return NULL;
776 val = (val / 100) * 100 + ((val % 100) * 50) / 30;
778 if (val > 1200)
779 return NULL;
780 tm->tm_gmtoff = (val * 3600) / 100;
781 if (neg)
782 tm->tm_gmtoff = -tm->tm_gmtoff;
784 break;
785 case 'E':
786 #ifdef _NL_CURRENT
787 switch (*fmt++)
789 case 'c':
790 /* Match locale's alternate date and time format. */
791 if (s.decided != raw)
793 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
795 if (*fmt == '\0')
796 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
798 if (!recursive (fmt))
800 if (s.decided == loc)
801 return NULL;
802 else
803 rp = rp_backup;
805 else
807 if (strcmp (fmt, HERE_D_T_FMT))
808 s.decided = loc;
809 s.want_xday = 1;
810 break;
812 s.decided = raw;
814 /* The C locale has no era information, so use the
815 normal representation. */
816 if (!recursive (HERE_D_T_FMT))
817 return NULL;
818 s.want_xday = 1;
819 break;
820 case 'C':
821 if (s.decided != raw)
823 if (s.era_cnt >= 0)
825 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
826 if (era != NULL && match_string (era->era_name, rp))
828 s.decided = loc;
829 break;
831 else
832 return NULL;
835 num_eras = _NL_CURRENT_WORD (LC_TIME,
836 _NL_TIME_ERA_NUM_ENTRIES);
837 for (s.era_cnt = 0; s.era_cnt < (int) num_eras;
838 ++s.era_cnt, rp = rp_backup)
840 era = _nl_select_era_entry (s.era_cnt
841 HELPER_LOCALE_ARG);
842 if (era != NULL && match_string (era->era_name, rp))
844 s.decided = loc;
845 break;
848 if (s.era_cnt != (int) num_eras)
849 break;
851 s.era_cnt = -1;
852 if (s.decided == loc)
853 return NULL;
855 s.decided = raw;
857 /* The C locale has no era information, so use the
858 normal representation. */
859 goto match_century;
860 case 'y':
861 if (s.decided != raw)
863 get_number(0, 9999, 4);
864 tm->tm_year = val;
865 s.want_era = 1;
866 s.want_xday = 1;
867 s.want_century = 1;
869 if (s.era_cnt >= 0)
871 assert (s.decided == loc);
873 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
874 bool match = false;
875 if (era != NULL)
877 int delta = ((tm->tm_year - era->offset)
878 * era->absolute_direction);
879 match = (delta >= 0
880 && delta < (((int64_t) era->stop_date[0]
881 - (int64_t) era->start_date[0])
882 * era->absolute_direction));
884 if (! match)
885 return NULL;
887 break;
890 num_eras = _NL_CURRENT_WORD (LC_TIME,
891 _NL_TIME_ERA_NUM_ENTRIES);
892 for (s.era_cnt = 0; s.era_cnt < (int) num_eras; ++s.era_cnt)
894 era = _nl_select_era_entry (s.era_cnt
895 HELPER_LOCALE_ARG);
896 if (era != NULL)
898 int delta = ((tm->tm_year - era->offset)
899 * era->absolute_direction);
900 if (delta >= 0
901 && delta < (((int64_t) era->stop_date[0]
902 - (int64_t) era->start_date[0])
903 * era->absolute_direction))
905 s.decided = loc;
906 break;
910 if (s.era_cnt != (int) num_eras)
911 break;
913 s.era_cnt = -1;
914 if (s.decided == loc)
915 return NULL;
917 s.decided = raw;
920 goto match_year_in_century;
921 case 'Y':
922 if (s.decided != raw)
924 num_eras = _NL_CURRENT_WORD (LC_TIME,
925 _NL_TIME_ERA_NUM_ENTRIES);
926 for (s.era_cnt = 0; s.era_cnt < (int) num_eras;
927 ++s.era_cnt, rp = rp_backup)
929 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
930 if (era != NULL && recursive (era->era_format))
931 break;
933 if (s.era_cnt == (int) num_eras)
935 s.era_cnt = -1;
936 if (s.decided == loc)
937 return NULL;
938 else
939 rp = rp_backup;
941 else
943 s.decided = loc;
944 break;
947 s.decided = raw;
949 get_number (0, 9999, 4);
950 tm->tm_year = val - 1900;
951 s.want_century = 0;
952 s.want_xday = 1;
953 break;
954 case 'x':
955 if (s.decided != raw)
957 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
959 if (*fmt == '\0')
960 fmt = _NL_CURRENT (LC_TIME, D_FMT);
962 if (!recursive (fmt))
964 if (s.decided == loc)
965 return NULL;
966 else
967 rp = rp_backup;
969 else
971 if (strcmp (fmt, HERE_D_FMT))
972 s.decided = loc;
973 break;
975 s.decided = raw;
977 if (!recursive (HERE_D_FMT))
978 return NULL;
979 break;
980 case 'X':
981 if (s.decided != raw)
983 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
985 if (*fmt == '\0')
986 fmt = _NL_CURRENT (LC_TIME, T_FMT);
988 if (!recursive (fmt))
990 if (s.decided == loc)
991 return NULL;
992 else
993 rp = rp_backup;
995 else
997 if (strcmp (fmt, HERE_T_FMT))
998 s.decided = loc;
999 break;
1001 s.decided = raw;
1003 if (!recursive (HERE_T_FMT))
1004 return NULL;
1005 break;
1006 default:
1007 return NULL;
1009 break;
1010 #else
1011 /* We have no information about the era format. Just use
1012 the normal format. */
1013 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
1014 && *fmt != 'x' && *fmt != 'X')
1015 /* This is an illegal format. */
1016 return NULL;
1018 goto start_over;
1019 #endif
1020 case 'O':
1021 switch (*fmt++)
1023 case 'd':
1024 case 'e':
1025 /* Match day of month using alternate numeric symbols. */
1026 get_alt_number (1, 31, 2);
1027 tm->tm_mday = val;
1028 s.have_mday = 1;
1029 s.want_xday = 1;
1030 break;
1031 case 'H':
1032 /* Match hour in 24-hour clock using alternate numeric
1033 symbols. */
1034 get_alt_number (0, 23, 2);
1035 tm->tm_hour = val;
1036 s.have_I = 0;
1037 break;
1038 case 'I':
1039 /* Match hour in 12-hour clock using alternate numeric
1040 symbols. */
1041 get_alt_number (1, 12, 2);
1042 tm->tm_hour = val % 12;
1043 s.have_I = 1;
1044 break;
1045 case 'm':
1046 /* Match month using alternate numeric symbols. */
1047 get_alt_number (1, 12, 2);
1048 tm->tm_mon = val - 1;
1049 s.have_mon = 1;
1050 s.want_xday = 1;
1051 break;
1052 case 'M':
1053 /* Match minutes using alternate numeric symbols. */
1054 get_alt_number (0, 59, 2);
1055 tm->tm_min = val;
1056 break;
1057 case 'S':
1058 /* Match seconds using alternate numeric symbols. */
1059 get_alt_number (0, 61, 2);
1060 tm->tm_sec = val;
1061 break;
1062 case 'U':
1063 get_alt_number (0, 53, 2);
1064 s.week_no = val;
1065 s.have_uweek = 1;
1066 break;
1067 case 'W':
1068 get_alt_number (0, 53, 2);
1069 s.week_no = val;
1070 s.have_wweek = 1;
1071 break;
1072 case 'V':
1073 get_alt_number (0, 53, 2);
1074 /* XXX This cannot determine any field in TM without
1075 further information. */
1076 break;
1077 case 'w':
1078 /* Match number of weekday using alternate numeric symbols. */
1079 get_alt_number (0, 6, 1);
1080 tm->tm_wday = val;
1081 s.have_wday = 1;
1082 break;
1083 case 'y':
1084 /* Match year within century using alternate numeric symbols. */
1085 get_alt_number (0, 99, 2);
1086 tm->tm_year = val >= 69 ? val : val + 100;
1087 s.want_xday = 1;
1088 break;
1089 default:
1090 return NULL;
1092 break;
1093 default:
1094 return NULL;
1098 if (statep != NULL)
1100 /* Recursive invocation, returning success, so
1101 update parent's struct tm and state. */
1102 *(struct __strptime_state *) statep = s;
1103 *tmp = tmb;
1104 return (char *) rp;
1107 if (s.have_I && s.is_pm)
1108 tm->tm_hour += 12;
1110 if (s.century != -1)
1112 if (s.want_century)
1113 tm->tm_year = tm->tm_year % 100 + (s.century - 19) * 100;
1114 else
1115 /* Only the century, but not the year. Strange, but so be it. */
1116 tm->tm_year = (s.century - 19) * 100;
1119 if (s.era_cnt != -1)
1121 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
1122 if (era == NULL)
1123 return NULL;
1124 if (s.want_era)
1125 tm->tm_year = (era->start_date[0]
1126 + ((tm->tm_year - era->offset)
1127 * era->absolute_direction));
1128 else
1129 /* Era start year assumed. */
1130 tm->tm_year = era->start_date[0];
1132 else
1133 if (s.want_era)
1135 /* No era found but we have seen an E modifier. Rectify some
1136 values. */
1137 if (s.want_century && s.century == -1 && tm->tm_year < 69)
1138 tm->tm_year += 100;
1141 if (s.want_xday && !s.have_wday)
1143 if ( !(s.have_mon && s.have_mday) && s.have_yday)
1145 /* We don't have tm_mon and/or tm_mday, compute them. */
1146 int t_mon = 0;
1147 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1148 t_mon++;
1149 if (!s.have_mon)
1150 tm->tm_mon = t_mon - 1;
1151 if (!s.have_mday)
1152 tm->tm_mday =
1153 (tm->tm_yday
1154 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1155 s.have_mon = 1;
1156 s.have_mday = 1;
1158 /* Don't crash in day_of_the_week if tm_mon is uninitialized. */
1159 if (s.have_mon || (unsigned) tm->tm_mon <= 11)
1160 day_of_the_week (tm);
1163 if (s.want_xday && !s.have_yday && (s.have_mon || (unsigned) tm->tm_mon <= 11))
1164 day_of_the_year (tm);
1166 if ((s.have_uweek || s.have_wweek) && s.have_wday)
1168 int save_wday = tm->tm_wday;
1169 int save_mday = tm->tm_mday;
1170 int save_mon = tm->tm_mon;
1171 int w_offset = s.have_uweek ? 0 : 1;
1173 tm->tm_mday = 1;
1174 tm->tm_mon = 0;
1175 day_of_the_week (tm);
1176 if (s.have_mday)
1177 tm->tm_mday = save_mday;
1178 if (s.have_mon)
1179 tm->tm_mon = save_mon;
1181 if (!s.have_yday)
1182 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1183 + (s.week_no - 1) * 7
1184 + (save_wday - w_offset + 7) % 7);
1186 if (!s.have_mday || !s.have_mon)
1188 int t_mon = 0;
1189 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1190 <= tm->tm_yday)
1191 t_mon++;
1192 if (!s.have_mon)
1193 tm->tm_mon = t_mon - 1;
1194 if (!s.have_mday)
1195 tm->tm_mday =
1196 (tm->tm_yday
1197 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1200 tm->tm_wday = save_wday;
1203 return (char *) rp;
1207 char *
1208 strptime (buf, format, tm LOCALE_PARAM)
1209 const char *buf;
1210 const char *format;
1211 struct tm *tm;
1212 LOCALE_PARAM_DECL
1214 return __strptime_internal (buf, format, tm, NULL LOCALE_ARG);
1217 #ifdef _LIBC
1218 weak_alias (__strptime_l, strptime_l)
1219 #endif