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