1 /* Copyright (C) 2002, 2004, 2005, 2007 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program 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
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation,
16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
17
18 /* Minor modifications by acab@clamav.net */
19
20 #define strncasecmp _strnicmp
21 #ifndef _LIBC
22 //# include <config.h>
23 #endif
24
25 #include <time.h>
26
27 #include <assert.h>
28 #include <ctype.h>
29 #ifdef _LIBC
30 #include <langinfo.h>
31 #endif
32 #include <limits.h>
33 #include <string.h>
34 //#include <stdbool.h>
35
36 #ifdef _LIBC
37 #include "../locale/localeinfo.h"
38 #endif
39
40 #ifndef _LIBC
41 enum ptime_locale_status { not,
42 loc,
43 raw };
44 #endif
45
localtime_r(time_t const * t,struct tm * tp)46 struct tm *localtime_r(time_t const *t, struct tm *tp)
47 {
48 struct tm *copy = localtime(t);
49 if (!copy) return NULL;
50 memcpy(tp, copy, sizeof(*tp));
51 return tp;
52 }
53
54 typedef int bool;
55
56 #define match_char(ch1, ch2) \
57 if (ch1 != ch2) return NULL
58 #if defined _LIBC && 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 (*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; \
90 \
91 if (*decided != raw) { \
92 val = _nl_parse_alt_digit(&rp HELPER_LOCALE_ARG); \
93 if (val == -1 && *decided != loc) { \
94 *decided = loc; \
95 goto do_normal; \
96 } \
97 if (val < from || val > to) \
98 return NULL; \
99 } else { \
100 do_normal: \
101 get_number(from, to, n); \
102 } \
103 0; \
104 })
105 #else
106 #define get_alt_number(from, to, n) \
107 /* We don't have the alternate representation. */ \
108 get_number(from, to, n)
109 #endif
110 #define recursive(new_fmt) \
111 (*(new_fmt) != '\0' && (rp = __strptime_internal(rp, (new_fmt), tm, \
112 decided, era_cnt LOCALE_ARG)) != NULL)
113
114 #ifdef _LIBC
115 /* This is defined in locale/C-time.c in the GNU libc. */
116 extern const struct locale_data _nl_C_LC_TIME attribute_hidden;
117
118 #define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX(DAY_1)].string)
119 #define ab_weekday_name \
120 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX(ABDAY_1)].string)
121 #define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX(MON_1)].string)
122 #define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX(ABMON_1)].string)
123 #define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX(D_T_FMT)].string)
124 #define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX(D_FMT)].string)
125 #define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX(AM_STR)].string)
126 #define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX(PM_STR)].string)
127 #define HERE_T_FMT_AMPM \
128 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX(T_FMT_AMPM)].string)
129 #define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX(T_FMT)].string)
130
131 #define strncasecmp(s1, s2, n) __strncasecmp(s1, s2, n)
132 #else
133 static char const weekday_name[][10] =
134 {
135 "Sunday", "Monday", "Tuesday", "Wednesday",
136 "Thursday", "Friday", "Saturday"};
137 static char const ab_weekday_name[][4] =
138 {
139 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
140 static char const month_name[][10] =
141 {
142 "January", "February", "March", "April", "May", "June",
143 "July", "August", "September", "October", "November", "December"};
144 static char const ab_month_name[][4] =
145 {
146 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
147 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
148 #define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
149 #define HERE_D_FMT "%m/%d/%y"
150 #define HERE_AM_STR "AM"
151 #define HERE_PM_STR "PM"
152 #define HERE_T_FMT_AMPM "%I:%M:%S %p"
153 #define HERE_T_FMT "%H:%M:%S"
154
155 static const unsigned short int __mon_yday[2][13] =
156 {
157 /* Normal years. */
158 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
159 /* Leap years. */
160 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}};
161 #endif
162
163 #if defined _LIBC
164 /* We use this code also for the extended locale handling where the
165 function gets as an additional argument the locale which has to be
166 used. To access the values we have to redefine the _NL_CURRENT
167 macro. */
168 #define strptime __strptime_l
169 #undef _NL_CURRENT
170 #define _NL_CURRENT(category, item) \
171 (current->values[_NL_ITEM_INDEX(item)].string)
172 #undef _NL_CURRENT_WORD
173 #define _NL_CURRENT_WORD(category, item) \
174 (current->values[_NL_ITEM_INDEX(item)].word)
175 #define LOCALE_PARAM , locale
176 #define LOCALE_ARG , locale
177 #define LOCALE_PARAM_PROTO , __locale_t locale
178 #define LOCALE_PARAM_DECL __locale_t locale;
179 #define HELPER_LOCALE_ARG , current
180 #define ISSPACE(Ch) __isspace_l(Ch, locale)
181 #else
182 #define LOCALE_PARAM
183 #define LOCALE_ARG
184 #define LOCALE_PARAM_DECL
185 #define LOCALE_PARAM_PROTO
186 #define HELPER_LOCALE_ARG
187 #define ISSPACE(Ch) isspace(Ch)
188 #endif
189
190 #ifndef __isleap
191 /* Nonzero if YEAR is a leap year (every 4 years,
192 except every 100th isn't, and every 400th is). */
193 #define __isleap(year) \
194 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
195 #endif
196
197 /* Compute the day of the week. */
198 static void
day_of_the_week(struct tm * tm)199 day_of_the_week(struct tm *tm)
200 {
201 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
202 the difference between this data in the one on TM and so determine
203 the weekday. */
204 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
205 int wday = (-473 + (365 * (tm->tm_year - 70)) + (corr_year / 4) - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0) + (((corr_year / 4) / 25) / 4) + __mon_yday[0][tm->tm_mon] + tm->tm_mday - 1);
206 tm->tm_wday = ((wday % 7) + 7) % 7;
207 }
208
209 /* Compute the day of the year. */
210 static void
day_of_the_year(struct tm * tm)211 day_of_the_year(struct tm *tm)
212 {
213 tm->tm_yday = (__mon_yday[__isleap(1900 + tm->tm_year)][tm->tm_mon] + (tm->tm_mday - 1));
214 }
215
216 #ifdef _LIBC
217 char *
218 internal_function
219 #else
220 static char *
221 #endif
222 __strptime_internal(rp, fmt, tm, decided, era_cnt LOCALE_PARAM)
223 const char *rp;
224 const char *fmt;
225 struct tm *tm;
226 enum ptime_locale_status *decided;
227 int era_cnt;
228 LOCALE_PARAM_DECL
229 {
230 #ifdef _LIBC
231 struct locale_data *const current = locale->__locales[LC_TIME];
232 #endif
233
234 const char *rp_backup;
235 int cnt;
236 size_t val;
237 int have_I, is_pm;
238 int century, want_century;
239 int want_era;
240 int have_wday, want_xday;
241 int have_yday;
242 int have_mon, have_mday;
243 int have_uweek, have_wweek;
244 int week_no;
245 size_t num_eras;
246 struct era_entry *era;
247
248 have_I = is_pm = 0;
249 century = -1;
250 want_century = 0;
251 want_era = 0;
252 era = NULL;
253 week_no = 0;
254
255 have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0;
256 have_wweek = 0;
257
258 while (*fmt != '\0') {
259 /* A white space in the format string matches 0 more or white
260 space in the input string. */
261 if (ISSPACE(*fmt)) {
262 while (ISSPACE(*rp))
263 ++rp;
264 ++fmt;
265 continue;
266 }
267
268 /* Any character but `%' must be matched by the same character
269 in the iput string. */
270 if (*fmt != '%') {
271 match_char(*fmt++, *rp++);
272 continue;
273 }
274
275 ++fmt;
276 #ifndef _NL_CURRENT
277 /* We need this for handling the `E' modifier. */
278 start_over:
279 #endif
280
281 /* Make back up of current processing pointer. */
282 rp_backup = rp;
283
284 switch (*fmt++) {
285 case '%':
286 /* Match the `%' character itself. */
287 match_char('%', *rp++);
288 break;
289 case 'a':
290 case 'A':
291 /* Match day of week. */
292 for (cnt = 0; cnt < 7; ++cnt) {
293 #ifdef _NL_CURRENT
294 if (*decided != raw) {
295 if (match_string(_NL_CURRENT(LC_TIME, DAY_1 + cnt), rp)) {
296 if (*decided == not&&strcmp(_NL_CURRENT(LC_TIME, DAY_1 + cnt),
297 weekday_name[cnt]))
298 *decided = loc;
299 break;
300 }
301 if (match_string(_NL_CURRENT(LC_TIME, ABDAY_1 + cnt), rp)) {
302 if (*decided == not&&strcmp(_NL_CURRENT(LC_TIME, ABDAY_1 + cnt),
303 ab_weekday_name[cnt]))
304 *decided = loc;
305 break;
306 }
307 }
308 #endif
309 if (*decided != loc && (match_string(weekday_name[cnt], rp) || match_string(ab_weekday_name[cnt], rp))) {
310 *decided = raw;
311 break;
312 }
313 }
314 if (cnt == 7)
315 /* Does not match a weekday name. */
316 return NULL;
317 tm->tm_wday = cnt;
318 have_wday = 1;
319 break;
320 case 'b':
321 case 'B':
322 case 'h':
323 /* Match month name. */
324 for (cnt = 0; cnt < 12; ++cnt) {
325 #ifdef _NL_CURRENT
326 if (*decided != raw) {
327 if (match_string(_NL_CURRENT(LC_TIME, MON_1 + cnt), rp)) {
328 if (*decided == not&&strcmp(_NL_CURRENT(LC_TIME, MON_1 + cnt),
329 month_name[cnt]))
330 *decided = loc;
331 break;
332 }
333 if (match_string(_NL_CURRENT(LC_TIME, ABMON_1 + cnt), rp)) {
334 if (*decided == not&&strcmp(_NL_CURRENT(LC_TIME, ABMON_1 + cnt),
335 ab_month_name[cnt]))
336 *decided = loc;
337 break;
338 }
339 }
340 #endif
341 if (match_string(month_name[cnt], rp) || match_string(ab_month_name[cnt], rp)) {
342 *decided = raw;
343 break;
344 }
345 }
346 if (cnt == 12)
347 /* Does not match a month name. */
348 return NULL;
349 tm->tm_mon = cnt;
350 want_xday = 1;
351 break;
352 case 'c':
353 /* Match locale's date and time format. */
354 #ifdef _NL_CURRENT
355 if (*decided != raw) {
356 if (!recursive(_NL_CURRENT(LC_TIME, D_T_FMT))) {
357 if (*decided == loc)
358 return NULL;
359 else
360 rp = rp_backup;
361 } else {
362 if (*decided == not&&strcmp(_NL_CURRENT(LC_TIME, D_T_FMT), HERE_D_T_FMT))
363 *decided = loc;
364 want_xday = 1;
365 break;
366 }
367 *decided = raw;
368 }
369 #endif
370 if (!recursive(HERE_D_T_FMT))
371 return NULL;
372 want_xday = 1;
373 break;
374 case 'C':
375 /* Match century number. */
376 match_century:
377 get_number(0, 99, 2);
378 century = val;
379 want_xday = 1;
380 break;
381 case 'd':
382 case 'e':
383 /* Match day of month. */
384 get_number(1, 31, 2);
385 tm->tm_mday = val;
386 have_mday = 1;
387 want_xday = 1;
388 break;
389 case 'F':
390 if (!recursive("%Y-%m-%d"))
391 return NULL;
392 want_xday = 1;
393 break;
394 case 'x':
395 #ifdef _NL_CURRENT
396 if (*decided != raw) {
397 if (!recursive(_NL_CURRENT(LC_TIME, D_FMT))) {
398 if (*decided == loc)
399 return NULL;
400 else
401 rp = rp_backup;
402 } else {
403 if (*decided == not&&strcmp(_NL_CURRENT(LC_TIME, D_FMT), HERE_D_FMT))
404 *decided = loc;
405 want_xday = 1;
406 break;
407 }
408 *decided = raw;
409 }
410 #endif
411 /* Fall through. */
412 case 'D':
413 /* Match standard day format. */
414 if (!recursive(HERE_D_FMT))
415 return NULL;
416 want_xday = 1;
417 break;
418 case 'k':
419 case 'H':
420 /* Match hour in 24-hour clock. */
421 get_number(0, 23, 2);
422 tm->tm_hour = val;
423 have_I = 0;
424 break;
425 case 'l':
426 /* Match hour in 12-hour clock. GNU extension. */
427 case 'I':
428 /* Match hour in 12-hour clock. */
429 get_number(1, 12, 2);
430 tm->tm_hour = val % 12;
431 have_I = 1;
432 break;
433 case 'j':
434 /* Match day number of year. */
435 get_number(1, 366, 3);
436 tm->tm_yday = val - 1;
437 have_yday = 1;
438 break;
439 case 'm':
440 /* Match number of month. */
441 get_number(1, 12, 2);
442 tm->tm_mon = val - 1;
443 have_mon = 1;
444 want_xday = 1;
445 break;
446 case 'M':
447 /* Match minute. */
448 get_number(0, 59, 2);
449 tm->tm_min = val;
450 break;
451 case 'n':
452 case 't':
453 /* Match any white space. */
454 while (ISSPACE(*rp))
455 ++rp;
456 break;
457 case 'p':
458 /* Match locale's equivalent of AM/PM. */
459 #ifdef _NL_CURRENT
460 if (*decided != raw) {
461 if (match_string(_NL_CURRENT(LC_TIME, AM_STR), rp)) {
462 if (strcmp(_NL_CURRENT(LC_TIME, AM_STR), HERE_AM_STR))
463 *decided = loc;
464 break;
465 }
466 if (match_string(_NL_CURRENT(LC_TIME, PM_STR), rp)) {
467 if (strcmp(_NL_CURRENT(LC_TIME, PM_STR), HERE_PM_STR))
468 *decided = loc;
469 is_pm = 1;
470 break;
471 }
472 *decided = raw;
473 }
474 #endif
475 if (!match_string(HERE_AM_STR, rp)) {
476 if (match_string(HERE_PM_STR, rp))
477 is_pm = 1;
478 else
479 return NULL;
480 }
481 break;
482 case 'r':
483 #ifdef _NL_CURRENT
484 if (*decided != raw) {
485 if (!recursive(_NL_CURRENT(LC_TIME, T_FMT_AMPM))) {
486 if (*decided == loc)
487 return NULL;
488 else
489 rp = rp_backup;
490 } else {
491 if (*decided == not&&strcmp(_NL_CURRENT(LC_TIME, T_FMT_AMPM),
492 HERE_T_FMT_AMPM))
493 *decided = loc;
494 break;
495 }
496 *decided = raw;
497 }
498 #endif
499 if (!recursive(HERE_T_FMT_AMPM))
500 return NULL;
501 break;
502 case 'R':
503 if (!recursive("%H:%M"))
504 return NULL;
505 break;
506 case 's': {
507 /* The number of seconds may be very high so we cannot use
508 the `get_number' macro. Instead read the number
509 character for character and construct the result while
510 doing this. */
511 time_t secs = 0;
512 if (*rp < '0' || *rp > '9')
513 /* We need at least one digit. */
514 return NULL;
515
516 do {
517 secs *= 10;
518 secs += *rp++ - '0';
519 } while (*rp >= '0' && *rp <= '9');
520
521 if (localtime_r(&secs, tm) == NULL)
522 /* Error in function. */
523 return NULL;
524 } break;
525 case 'S':
526 get_number(0, 61, 2);
527 tm->tm_sec = val;
528 break;
529 case 'X':
530 #ifdef _NL_CURRENT
531 if (*decided != raw) {
532 if (!recursive(_NL_CURRENT(LC_TIME, T_FMT))) {
533 if (*decided == loc)
534 return NULL;
535 else
536 rp = rp_backup;
537 } else {
538 if (strcmp(_NL_CURRENT(LC_TIME, T_FMT), HERE_T_FMT))
539 *decided = loc;
540 break;
541 }
542 *decided = raw;
543 }
544 #endif
545 /* Fall through. */
546 case 'T':
547 if (!recursive(HERE_T_FMT))
548 return NULL;
549 break;
550 case 'u':
551 get_number(1, 7, 1);
552 tm->tm_wday = val % 7;
553 have_wday = 1;
554 break;
555 case 'g':
556 get_number(0, 99, 2);
557 /* XXX This cannot determine any field in TM. */
558 break;
559 case 'G':
560 if (*rp < '0' || *rp > '9')
561 return NULL;
562 /* XXX Ignore the number since we would need some more
563 information to compute a real date. */
564 do
565 ++rp;
566 while (*rp >= '0' && *rp <= '9');
567 break;
568 case 'U':
569 get_number(0, 53, 2);
570 week_no = val;
571 have_uweek = 1;
572 break;
573 case 'W':
574 get_number(0, 53, 2);
575 week_no = val;
576 have_wweek = 1;
577 break;
578 case 'V':
579 get_number(0, 53, 2);
580 /* XXX This cannot determine any field in TM without some
581 information. */
582 break;
583 case 'w':
584 /* Match number of weekday. */
585 get_number(0, 6, 1);
586 tm->tm_wday = val;
587 have_wday = 1;
588 break;
589 case 'y':
590 match_year_in_century:
591 /* Match year within century. */
592 get_number(0, 99, 2);
593 /* The "Year 2000: The Millennium Rollover" paper suggests that
594 values in the range 69-99 refer to the twentieth century. */
595 tm->tm_year = val >= 69 ? val : val + 100;
596 /* Indicate that we want to use the century, if specified. */
597 want_century = 1;
598 want_xday = 1;
599 break;
600 case 'Y':
601 /* Match year including century number. */
602 get_number(0, 9999, 4);
603 tm->tm_year = val - 1900;
604 want_century = 0;
605 want_xday = 1;
606 break;
607 case 'Z':
608 /* XXX How to handle this? */
609 break;
610 case 'z':
611 /* We recognize two formats: if two digits are given, these
612 specify hours. If fours digits are used, minutes are
613 also specified. */
614 {
615 bool neg;
616 int n;
617
618 val = 0;
619 while (*rp == ' ')
620 ++rp;
621 if (*rp != '+' && *rp != '-')
622 return NULL;
623 neg = *rp++ == '-';
624 n = 0;
625 while (n < 4 && *rp >= '0' && *rp <= '9') {
626 val = val * 10 + *rp++ - '0';
627 ++n;
628 }
629 if (n == 2)
630 val *= 100;
631 else if (n != 4)
632 /* Only two or four digits recognized. */
633 return NULL;
634 else {
635 /* We have to convert the minutes into decimal. */
636 if (val % 100 >= 60)
637 return NULL;
638 val = (val / 100) * 100 + ((val % 100) * 50) / 30;
639 }
640 if (val > 1200)
641 return NULL;
642 #if defined _LIBC || HAVE_TM_GMTOFF
643 tm->tm_gmtoff = (val * 3600) / 100;
644 if (neg)
645 tm->tm_gmtoff = -tm->tm_gmtoff;
646 #endif
647 }
648 break;
649 case 'E':
650 #ifdef _NL_CURRENT
651 switch (*fmt++) {
652 case 'c':
653 /* Match locale's alternate date and time format. */
654 if (*decided != raw) {
655 const char *fmt = _NL_CURRENT(LC_TIME, ERA_D_T_FMT);
656
657 if (*fmt == '\0')
658 fmt = _NL_CURRENT(LC_TIME, D_T_FMT);
659
660 if (!recursive(fmt)) {
661 if (*decided == loc)
662 return NULL;
663 else
664 rp = rp_backup;
665 } else {
666 if (strcmp(fmt, HERE_D_T_FMT))
667 *decided = loc;
668 want_xday = 1;
669 break;
670 }
671 *decided = raw;
672 }
673 /* The C locale has no era information, so use the
674 normal representation. */
675 if (!recursive(HERE_D_T_FMT))
676 return NULL;
677 want_xday = 1;
678 break;
679 case 'C':
680 if (*decided != raw) {
681 if (era_cnt >= 0) {
682 era = _nl_select_era_entry(era_cnt HELPER_LOCALE_ARG);
683 if (era != NULL && match_string(era->era_name, rp)) {
684 *decided = loc;
685 break;
686 } else
687 return NULL;
688 }
689
690 num_eras = _NL_CURRENT_WORD(LC_TIME,
691 _NL_TIME_ERA_NUM_ENTRIES);
692 for (era_cnt = 0; era_cnt < (int)num_eras;
693 ++era_cnt, rp = rp_backup) {
694 era = _nl_select_era_entry(era_cnt
695 HELPER_LOCALE_ARG);
696 if (era != NULL && match_string(era->era_name, rp)) {
697 *decided = loc;
698 break;
699 }
700 }
701 if (era_cnt != (int)num_eras)
702 break;
703
704 era_cnt = -1;
705 if (*decided == loc)
706 return NULL;
707
708 *decided = raw;
709 }
710 /* The C locale has no era information, so use the
711 normal representation. */
712 goto match_century;
713 case 'y':
714 if (*decided != raw) {
715 get_number(0, 9999, 4);
716 tm->tm_year = val;
717 want_era = 1;
718 want_xday = 1;
719 want_century = 1;
720
721 if (era_cnt >= 0) {
722 assert(*decided == loc);
723
724 era = _nl_select_era_entry(era_cnt HELPER_LOCALE_ARG);
725 bool match = false;
726 if (era != NULL) {
727 int delta = ((tm->tm_year - era->offset) * era->absolute_direction);
728 match = (delta >= 0 && delta < (((int64_t)era->stop_date[0] - (int64_t)era->start_date[0]) * era->absolute_direction));
729 }
730 if (!match)
731 return NULL;
732
733 break;
734 }
735
736 num_eras = _NL_CURRENT_WORD(LC_TIME,
737 _NL_TIME_ERA_NUM_ENTRIES);
738 for (era_cnt = 0; era_cnt < (int)num_eras; ++era_cnt) {
739 era = _nl_select_era_entry(era_cnt
740 HELPER_LOCALE_ARG);
741 if (era != NULL) {
742 int delta = ((tm->tm_year - era->offset) * era->absolute_direction);
743 if (delta >= 0 && delta < (((int64_t)era->stop_date[0] - (int64_t)era->start_date[0]) * era->absolute_direction)) {
744 *decided = loc;
745 break;
746 }
747 }
748 }
749 if (era_cnt != (int)num_eras)
750 break;
751
752 era_cnt = -1;
753 if (*decided == loc)
754 return NULL;
755
756 *decided = raw;
757 }
758
759 goto match_year_in_century;
760 case 'Y':
761 if (*decided != raw) {
762 num_eras = _NL_CURRENT_WORD(LC_TIME,
763 _NL_TIME_ERA_NUM_ENTRIES);
764 for (era_cnt = 0; era_cnt < (int)num_eras;
765 ++era_cnt, rp = rp_backup) {
766 era = _nl_select_era_entry(era_cnt HELPER_LOCALE_ARG);
767 if (era != NULL && recursive(era->era_format))
768 break;
769 }
770 if (era_cnt == (int)num_eras) {
771 era_cnt = -1;
772 if (*decided == loc)
773 return NULL;
774 else
775 rp = rp_backup;
776 } else {
777 *decided = loc;
778 era_cnt = -1;
779 break;
780 }
781
782 *decided = raw;
783 }
784 get_number(0, 9999, 4);
785 tm->tm_year = val - 1900;
786 want_century = 0;
787 want_xday = 1;
788 break;
789 case 'x':
790 if (*decided != raw) {
791 const char *fmt = _NL_CURRENT(LC_TIME, ERA_D_FMT);
792
793 if (*fmt == '\0')
794 fmt = _NL_CURRENT(LC_TIME, D_FMT);
795
796 if (!recursive(fmt)) {
797 if (*decided == loc)
798 return NULL;
799 else
800 rp = rp_backup;
801 } else {
802 if (strcmp(fmt, HERE_D_FMT))
803 *decided = loc;
804 break;
805 }
806 *decided = raw;
807 }
808 if (!recursive(HERE_D_FMT))
809 return NULL;
810 break;
811 case 'X':
812 if (*decided != raw) {
813 const char *fmt = _NL_CURRENT(LC_TIME, ERA_T_FMT);
814
815 if (*fmt == '\0')
816 fmt = _NL_CURRENT(LC_TIME, T_FMT);
817
818 if (!recursive(fmt)) {
819 if (*decided == loc)
820 return NULL;
821 else
822 rp = rp_backup;
823 } else {
824 if (strcmp(fmt, HERE_T_FMT))
825 *decided = loc;
826 break;
827 }
828 *decided = raw;
829 }
830 if (!recursive(HERE_T_FMT))
831 return NULL;
832 break;
833 default:
834 return NULL;
835 }
836 break;
837 #else
838 /* We have no information about the era format. Just use
839 the normal format. */
840 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y' && *fmt != 'x' && *fmt != 'X')
841 /* This is an illegal format. */
842 return NULL;
843
844 goto start_over;
845 #endif
846 case 'O':
847 switch (*fmt++) {
848 case 'd':
849 case 'e':
850 /* Match day of month using alternate numeric symbols. */
851 get_alt_number(1, 31, 2);
852 tm->tm_mday = val;
853 have_mday = 1;
854 want_xday = 1;
855 break;
856 case 'H':
857 /* Match hour in 24-hour clock using alternate numeric
858 symbols. */
859 get_alt_number(0, 23, 2);
860 tm->tm_hour = val;
861 have_I = 0;
862 break;
863 case 'I':
864 /* Match hour in 12-hour clock using alternate numeric
865 symbols. */
866 get_alt_number(1, 12, 2);
867 tm->tm_hour = val % 12;
868 have_I = 1;
869 break;
870 case 'm':
871 /* Match month using alternate numeric symbols. */
872 get_alt_number(1, 12, 2);
873 tm->tm_mon = val - 1;
874 have_mon = 1;
875 want_xday = 1;
876 break;
877 case 'M':
878 /* Match minutes using alternate numeric symbols. */
879 get_alt_number(0, 59, 2);
880 tm->tm_min = val;
881 break;
882 case 'S':
883 /* Match seconds using alternate numeric symbols. */
884 get_alt_number(0, 61, 2);
885 tm->tm_sec = val;
886 break;
887 case 'U':
888 get_alt_number(0, 53, 2);
889 week_no = val;
890 have_uweek = 1;
891 break;
892 case 'W':
893 get_alt_number(0, 53, 2);
894 week_no = val;
895 have_wweek = 1;
896 break;
897 case 'V':
898 get_alt_number(0, 53, 2);
899 /* XXX This cannot determine any field in TM without
900 further information. */
901 break;
902 case 'w':
903 /* Match number of weekday using alternate numeric symbols. */
904 get_alt_number(0, 6, 1);
905 tm->tm_wday = val;
906 have_wday = 1;
907 break;
908 case 'y':
909 /* Match year within century using alternate numeric symbols. */
910 get_alt_number(0, 99, 2);
911 tm->tm_year = val >= 69 ? val : val + 100;
912 want_xday = 1;
913 break;
914 default:
915 return NULL;
916 }
917 break;
918 default:
919 return NULL;
920 }
921 }
922
923 if (have_I && is_pm)
924 tm->tm_hour += 12;
925
926 if (century != -1) {
927 if (want_century)
928 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
929 else
930 /* Only the century, but not the year. Strange, but so be it. */
931 tm->tm_year = (century - 19) * 100;
932 }
933
934 if (era_cnt != -1) {
935 #ifdef _NL_CURRENT
936 era = _nl_select_era_entry(era_cnt HELPER_LOCALE_ARG);
937 if (era == NULL)
938 return NULL;
939 if (want_era)
940 tm->tm_year = (era->start_date[0] + ((tm->tm_year - era->offset) * era->absolute_direction));
941 else
942 /* Era start year assumed. */
943 tm->tm_year = era->start_date[0];
944 #endif
945 } else if (want_era) {
946 /* No era found but we have seen an E modifier. Rectify some
947 values. */
948 if (want_century && century == -1 && tm->tm_year < 69)
949 tm->tm_year += 100;
950 }
951
952 if (want_xday && !have_wday) {
953 if (!(have_mon && have_mday) && have_yday) {
954 /* We don't have tm_mon and/or tm_mday, compute them. */
955 int t_mon = 0;
956 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
957 t_mon++;
958 if (!have_mon)
959 tm->tm_mon = t_mon - 1;
960 if (!have_mday)
961 tm->tm_mday =
962 (tm->tm_yday - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
963 }
964 day_of_the_week(tm);
965 }
966
967 if (want_xday && !have_yday)
968 day_of_the_year(tm);
969
970 if ((have_uweek || have_wweek) && have_wday) {
971 int save_wday = tm->tm_wday;
972 int save_mday = tm->tm_mday;
973 int save_mon = tm->tm_mon;
974 int w_offset = have_uweek ? 0 : 1;
975
976 tm->tm_mday = 1;
977 tm->tm_mon = 0;
978 day_of_the_week(tm);
979 if (have_mday)
980 tm->tm_mday = save_mday;
981 if (have_mon)
982 tm->tm_mon = save_mon;
983
984 if (!have_yday)
985 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7 + (week_no - 1) * 7 + save_wday - w_offset);
986
987 if (!have_mday || !have_mon) {
988 int t_mon = 0;
989 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
990 t_mon++;
991 if (!have_mon)
992 tm->tm_mon = t_mon - 1;
993 if (!have_mday)
994 tm->tm_mday =
995 (tm->tm_yday - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
996 }
997
998 tm->tm_wday = save_wday;
999 }
1000
1001 return (char *)rp;
1002 }
1003
1004 char *
1005 strptime(buf, format, tm LOCALE_PARAM)
1006 const char *buf;
1007 const char *format;
1008 struct tm *tm;
1009 LOCALE_PARAM_DECL
1010 {
1011 enum ptime_locale_status decided;
1012
1013 #ifdef _NL_CURRENT
1014 decided = not;
1015 #else
1016 decided = raw;
1017 #endif
1018 return __strptime_internal(buf, format, tm, &decided, -1 LOCALE_ARG);
1019 }
1020
1021 #ifdef _LIBC
1022 weak_alias(__strptime_l, strptime_l)
1023 #endif
1024