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