Statistics
| Revision:

root / trunk / nntpgrab_core / strptime.c @ 1858

History | View | Annotate | Download (30.3 KB)

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 , 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
#ifdef WIN32
31
#define strncasecmp                strnicmp
32
#endif
33

                
34
#include 
35
#include 
36
#include 
37
#include 
38

                
39
#ifdef _LIBC
40
# include "../locale/localeinfo.h"
41
#endif
42

                
43
#include "strptime.h"
44

                
45
#ifndef __P
46
# if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
47
# define __P(args) args
48
# else
49
# define __P(args) ()
50
# endif  /* GCC. */
51
#endif  /* Not __P. */
52

                
53
#if ! HAVE_LOCALTIME_R && ! defined localtime_r
54
# ifdef _LIBC
55
# define localtime_r __localtime_r
56
# else
57
/* Approximate localtime_r as best we can in its absence. */
58
# define localtime_r my_localtime_r
59
static struct tm *localtime_r __P ((const time_t *, struct tm *));
60
static struct tm *
61
localtime_r (t, tp)
62
     const time_t *t;
63
     struct tm *tp;
64
{
65
  struct tm *l = localtime (t);
66
  if (! l)
67
    return 0;
68
  *tp = *l;
69
  return tp;
70
}
71
# endif /* ! _LIBC */
72
#endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
73

                
74

                
75
#define match_char(ch1, ch2) if (ch1 != ch2) return NULL
76
#if defined __GNUC__ && __GNUC__ >= 2
77
# define match_string(cs1, s2) \
78
  ({ size_t len = strlen (cs1);                                                      \
79
     int result = strncasecmp ((cs1), (s2), len) == 0;                              \
80
     if (result) (s2) += len;                                                      \
81
     result; })
82
#else
83
/* Oh come on. Get a reasonable compiler. */
84
# define match_string(cs1, s2) \
85
  (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
86
#endif
87
/* We intentionally do not use isdigit() for testing because this will 
88
   lead to problems with the wide character version.  */
89
#define get_number(from, to, n) \
90
  do {                                                                              \
91
    int __n = n;                                                              \
92
    val = 0;                                                                      \
93
    while (*rp == ' ')                                                              \
94
      ++rp;                                                                      \
95
    if (*rp < '0' || *rp > '9')                                                      \
96
      return NULL;                                                              \
97
    do {                                                                      \
98
      val *= 10;                                                              \
99
      val += *rp++ - '0';                                                      \
100
    } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9');              \
101
    if (val < from || val > to)                                                      \
102
      return NULL;                                                              \
103
  } while (0)
104
#ifdef _NL_CURRENT
105
# define get_alt_number(from, to, n) \
106
  ({                                                                              \
107
    __label__ do_normal;                                                      \
108
    if (*decided != raw)                                                      \
109
      {                                                                              \
110
        const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS);                      \
111
        int __n = n;                                                              \
112
        int any = 0;                                                              \
113
        while (*rp == ' ')                                                      \
114
          ++rp;                                                                      \
115
        val = 0;                                                              \
116
        do {                                                                      \
117
          val *= 10;                                                              \
118
          while (*alts != '\0')                                                      \
119
            {                                                                      \
120
              size_t len = strlen (alts);                                      \
121
              if (strncasecmp (alts, rp, len) == 0)                              \
122
                break;                                                              \
123
              alts += len + 1;                                                      \
124
              ++val;                                                              \
125
            }                                                                      \
126
          if (*alts == '\0')                                                      \
127
            {                                                                      \
128
              if (*decided == not && ! any)                                      \
129
                goto do_normal;                                                      \
130
              /* If we haven't read anything it's an error. */                      \
131
              if (! any)                                                      \
132
                return NULL;                                                      \
133
              /* Correct the premature multiplication. */                      \
134
              val /= 10;                                                      \
135
              break;                                                              \
136
            }                                                                      \
137
          else                                                                      \
138
            *decided = loc;                                                      \
139
        } while (--__n > 0 && val * 10 <= to);                                      \
140
        if (val < from || val > to)                                              \
141
          return NULL;                                                              \
142
      }                                                                              \
143
    else                                                                      \
144
      {                                                                              \
145
       do_normal:                                                              \
146
        get_number (from, to, n);                                              \
147
      }                                                                              \
148
    0;                                                                              \
149
  })
150
#else
151
# define get_alt_number(from, to, n) \
152
  /* We don't have the alternate representation. */                              \
153
  get_number(from, to, n)
154
#endif
155
#define recursive(new_fmt) \
156
  (*(new_fmt) != '\0'                                                              \
157
   && (rp = strptime_internal (rp, (new_fmt), tm, decided, era_cnt)) != NULL)
158

                
159

                
160
#ifdef _LIBC
161
/* This is defined in locale/C-time.c in the GNU libc. */
162
extern const struct locale_data _nl_C_LC_TIME;
163
extern const unsigned short int __mon_yday[2][13];
164

                
165
# define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
166
# define ab_weekday_name \
167
  (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
168
# define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
169
# define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
170
# define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
171
# define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
172
# define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
173
# define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
174
# define HERE_T_FMT_AMPM \
175
  (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
176
# define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
177

                
178
# define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
179
#else
180
static char const weekday_name[][10] =
181
  {
182
    "Sunday", "Monday", "Tuesday", "Wednesday",
183
    "Thursday", "Friday", "Saturday"
184
  };
185
static char const ab_weekday_name[][4] =
186
  {
187
    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
188
  };
189
static char const month_name[][10] =
190
  {
191
    "January", "February", "March", "April", "May", "June",
192
    "July", "August", "September", "October", "November", "December"
193
  };
194
static char const ab_month_name[][4] =
195
  {
196
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
197
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
198
  };
199
# define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
200
# define HERE_D_FMT "%m/%d/%y"
201
# define HERE_AM_STR "AM"
202
# define HERE_PM_STR "PM"
203
# define HERE_T_FMT_AMPM "%I:%M:%S %p"
204
# define HERE_T_FMT "%H:%M:%S"
205

                
206
const unsigned short int __mon_yday[2][13] =
207
  {
208
    /* Normal years. */
209
    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
210
    /* Leap years. */
211
    { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
212
  };
213
#endif
214

                
215
/* Status of lookup: do we use the locale data or the raw data? */
216
enum locale_status { not, loc, raw };
217

                
218

                
219
#ifndef __isleap
220
/* Nonzero if YEAR is a leap year (every 4 years, 
221
   except every 100th isn't, and every 400th is).  */
222
# define __isleap(year)        \
223
  ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
224
#endif
225

                
226
/* Compute the day of the week. */
227
static void
228
day_of_the_week (struct tm *tm)
229
{
230
  /* We know that January 1st 1970 was a Thursday (= 4). Compute the 
231
     the difference between this data in the one on TM and so determine
232
     the weekday.  */
233
  int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
234
  int wday = (-473
235
              + (365 * (tm->tm_year - 70))
236
              + (corr_year / 4)
237
              - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
238
              + (((corr_year / 4) / 25) / 4)
239
              + __mon_yday[0][tm->tm_mon]
240
              + tm->tm_mday - 1);
241
  tm->tm_wday = ((wday % 7) + 7) % 7;
242
}
243

                
244
/* Compute the day of the year. */
245
static void
246
day_of_the_year (struct tm *tm)
247
{
248
  tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
249
                 + (tm->tm_mday - 1));
250
}
251

                
252
static char *
253
#ifdef _LIBC
254
internal_function
255
#endif
256
strptime_internal __P ((const char *rp, const char *fmt, struct tm *tm,
257
                        enum locale_status *decided, int era_cnt));
258

                
259
static char *
260
#ifdef _LIBC
261
internal_function
262
#endif
263
strptime_internal (rp, fmt, tm, decided, era_cnt)
264
     const char *rp;
265
     const char *fmt;
266
     struct tm *tm;
267
     enum locale_status *decided;
268
     int era_cnt;
269
{
270
  int cnt;
271
  size_t val;
272
  int have_I, is_pm;
273
  int century, want_century;
274
  int want_era;
275
  int have_wday, want_xday;
276
  int have_yday;
277
  int have_mon, have_mday;
278
#ifdef _NL_CURRENT
279
  const char *rp_backup;
280
  size_t num_eras;
281
  struct era_entry *era;
282
#endif
283

                
284
  have_I = is_pm = 0;
285
  century = -1;
286
  want_century = 0;
287
  want_era = 0;
288
#ifdef _NL_CURRENT
289
  era = NULL;
290
#endif
291

                
292
  have_wday = want_xday = have_yday = have_mon = have_mday = 0;
293

                
294
  while (*fmt != '\0')
295
    {
296
      /* A white space in the format string matches 0 more or white 
297
         space in the input string.  */
298
      if (isspace (*fmt))
299
        {
300
          while (isspace (*rp))
301
            ++rp;
302
          ++fmt;
303
          continue;
304
        }
305

                
306
      /* Any character but `%' must be matched by the same character 
307
         in the iput string.  */
308
      if (*fmt != '%')
309
        {
310
          match_char (*fmt++, *rp++);
311
          continue;
312
        }
313

                
314
      ++fmt;
315
#ifndef _NL_CURRENT
316
      /* We need this for handling the `E' modifier. */
317
    start_over:
318
#endif
319

                
320
#ifdef _NL_CURRENT
321
      /* Make back up of current processing pointer. */
322
      rp_backup = rp;
323
#endif
324

                
325
      switch (*fmt++)
326
        {
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
            {
336
#ifdef _NL_CURRENT
337
              if (*decided !=raw)
338
                {
339
                  if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
340
                    {
341
                      if (*decided == not
342
                          && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
343
                                     weekday_name[cnt]))
344
                        *decided = loc;
345
                      break;
346
                    }
347
                  if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
348
                    {
349
                      if (*decided == not
350
                          && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
351
                                     ab_weekday_name[cnt]))
352
                        *decided = loc;
353
                      break;
354
                    }
355
                }
356
#endif
357
              if (*decided != loc
358
                  && (match_string (weekday_name[cnt], rp)
359
                      || match_string (ab_weekday_name[cnt], rp)))
360
                {
361
                  *decided = raw;
362
                  break;
363
                }
364
            }
365
          if (cnt == 7)
366
            /* Does not match a weekday name. */
367
            return NULL;
368
          tm->tm_wday = cnt;
369
          have_wday = 1;
370
          break;
371
        case 'b':
372
        case 'B':
373
        case 'h':
374
          /* Match month name. */
375
          for (cnt = 0; cnt < 12; ++cnt)
376
            {
377
#ifdef _NL_CURRENT
378
              if (*decided !=raw)
379
                {
380
                  if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
381
                    {
382
                      if (*decided == not
383
                          && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
384
                                     month_name[cnt]))
385
                        *decided = loc;
386
                      break;
387
                    }
388
                  if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
389
                    {
390
                      if (*decided == not
391
                          && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
392
                                     ab_month_name[cnt]))
393
                        *decided = loc;
394
                      break;
395
                    }
396
                }
397
#endif
398
              if (match_string (month_name[cnt], rp)
399
                  || match_string (ab_month_name[cnt], rp))
400
                {
401
                  *decided = raw;
402
                  break;
403
                }
404
            }
405
          if (cnt == 12)
406
            /* Does not match a month name. */
407
            return NULL;
408
          tm->tm_mon = cnt;
409
          want_xday = 1;
410
          break;
411
        case 'c':
412
          /* Match locale's date and time format. */
413
#ifdef _NL_CURRENT
414
          if (*decided != raw)
415
            {
416
              if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
417
                {
418
                  if (*decided == loc)
419
                    return NULL;
420
                  else
421
                    rp = rp_backup;
422
                }
423
              else
424
                {
425
                  if (*decided == not &&
426
                      strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
427
                    *decided = loc;
428
                  want_xday = 1;
429
                  break;
430
                }
431
              *decided = raw;
432
            }
433
#endif
434
          if (!recursive (HERE_D_T_FMT))
435
            return NULL;
436
          want_xday = 1;
437
          break;
438
        case 'C':
439
          /* Match century number. */
440
#ifdef _NL_CURRENT
441
        match_century:
442
#endif
443
          get_number (0, 99, 2);
444
          century = val;
445
          want_xday = 1;
446
          break;
447
        case 'd':
448
        case 'e':
449
          /* Match day of month. */
450
          get_number (1, 31, 2);
451
          tm->tm_mday = val;
452
          have_mday = 1;
453
          want_xday = 1;
454
          break;
455
        case 'F':
456
          if (!recursive ("%Y-%m-%d"))
457
            return NULL;
458
          want_xday = 1;
459
          break;
460
        case 'x':
461
#ifdef _NL_CURRENT
462
          if (*decided != raw)
463
            {
464
              if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
465
                {
466
                  if (*decided == loc)
467
                    return NULL;
468
                  else
469
                    rp = rp_backup;
470
                }
471
              else
472
                {
473
                  if (*decided == not
474
                      && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
475
                    *decided = loc;
476
                  want_xday = 1;
477
                  break;
478
                }
479
              *decided = raw;
480
            }
481
#endif
482
          /* Fall through. */
483
        case 'D':
484
          /* Match standard day format. */
485
          if (!recursive (HERE_D_FMT))
486
            return NULL;
487
          want_xday = 1;
488
          break;
489
        case 'k':
490
        case 'H':
491
          /* Match hour in 24-hour clock. */
492
          get_number (0, 23, 2);
493
          tm->tm_hour = val;
494
          have_I = 0;
495
          break;
496
        case 'I':
497
          /* Match hour in 12-hour clock. */
498
          get_number (1, 12, 2);
499
          tm->tm_hour = val % 12;
500
          have_I = 1;
501
          break;
502
        case 'j':
503
          /* Match day number of year. */
504
          get_number (1, 366, 3);
505
          tm->tm_yday = val - 1;
506
          have_yday = 1;
507
          break;
508
        case 'm':
509
          /* Match number of month. */
510
          get_number (1, 12, 2);
511
          tm->tm_mon = val - 1;
512
          have_mon = 1;
513
          want_xday = 1;
514
          break;
515
        case 'M':
516
          /* Match minute. */
517
          get_number (0, 59, 2);
518
          tm->tm_min = val;
519
          break;
520
        case 'n':
521
        case 't':
522
          /* Match any white space. */
523
          while (isspace (*rp))
524
            ++rp;
525
          break;
526
        case 'p':
527
          /* Match locale's equivalent of AM/PM. */
528
#ifdef _NL_CURRENT
529
          if (*decided != raw)
530
            {
531
              if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
532
                {
533
                  if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
534
                    *decided = loc;
535
                  break;
536
                }
537
              if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
538
                {
539
                  if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
540
                    *decided = loc;
541
                  is_pm = 1;
542
                  break;
543
                }
544
              *decided = raw;
545
            }
546
#endif
547
          if (!match_string (HERE_AM_STR, rp)) {
548
            if (match_string (HERE_PM_STR, rp)) {
549
              is_pm = 1;
550
            } else {
551
              return NULL;
552
        }
553
      }
554
          break;
555
        case 'r':
556
#ifdef _NL_CURRENT
557
          if (*decided != raw)
558
            {
559
              if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
560
                {
561
                  if (*decided == loc)
562
                    return NULL;
563
                  else
564
                    rp = rp_backup;
565
                }
566
              else
567
                {
568
                  if (*decided == not &&
569
                      strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
570
                              HERE_T_FMT_AMPM))
571
                    *decided = loc;
572
                  break;
573
                }
574
              *decided = raw;
575
            }
576
#endif
577
          if (!recursive (HERE_T_FMT_AMPM))
578
            return NULL;
579
          break;
580
        case 'R':
581
          if (!recursive ("%H:%M"))
582
            return NULL;
583
          break;
584
        case 's':
585
          {
586
            /* The number of seconds may be very high so we cannot use 
587
               the `get_number' macro.  Instead read the number
588
               character for character and construct the result while
589
               doing this.  */
590
            time_t secs = 0;
591
            if (*rp < '0' || *rp > '9')
592
              /* We need at least one digit. */
593
              return NULL;
594

                
595
            do
596
              {
597
                secs *= 10;
598
                secs += *rp++ - '0';
599
              }
600
            while (*rp >= '0' && *rp <= '9');
601

                
602
            if (localtime_r (&secs, tm) == NULL)
603
              /* Error in function. */
604
              return NULL;
605
          }
606
          break;
607
        case 'S':
608
          get_number (0, 61, 2);
609
          tm->tm_sec = val;
610
          break;
611
        case 'X':
612
#ifdef _NL_CURRENT
613
          if (*decided != raw)
614
            {
615
              if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
616
                {
617
                  if (*decided == loc)
618
                    return NULL;
619
                  else
620
                    rp = rp_backup;
621
                }
622
              else
623
                {
624
                  if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
625
                    *decided = loc;
626
                  break;
627
                }
628
              *decided = raw;
629
            }
630
#endif
631
          /* Fall through. */
632
        case 'T':
633
          if (!recursive (HERE_T_FMT))
634
            return NULL;
635
          break;
636
        case 'u':
637
          get_number (1, 7, 1);
638
          tm->tm_wday = val % 7;
639
          have_wday = 1;
640
          break;
641
        case 'g':
642
          get_number (0, 99, 2);
643
          /* XXX This cannot determine any field in TM. */
644
          break;
645
        case 'G':
646
          if (*rp < '0' || *rp > '9')
647
            return NULL;
648
          /* XXX Ignore the number since we would need some more 
649
             information to compute a real date.  */
650
          do
651
            ++rp;
652
          while (*rp >= '0' && *rp <= '9');
653
          break;
654
        case 'U':
655
        case 'V':
656
        case 'W':
657
          get_number (0, 53, 2);
658
          /* XXX This cannot determine any field in TM without some 
659
             information.  */
660
          break;
661
        case 'w':
662
          /* Match number of weekday. */
663
          get_number (0, 6, 1);
664
          tm->tm_wday = val;
665
          have_wday = 1;
666
          break;
667
        case 'y':
668
#ifdef _NL_CURRENT
669
        match_year_in_century:
670
#endif
671
          /* Match year within century. */
672
          get_number (0, 99, 2);
673
          /* The "Year 2000: The Millennium Rollover" paper suggests that 
674
             values in the range 69-99 refer to the twentieth century.  */
675
          tm->tm_year = val >= 69 ? val : val + 100;
676
          /* Indicate that we want to use the century, if specified. */
677
          want_century = 1;
678
          want_xday = 1;
679
          break;
680
        case 'Y':
681
          /* Match year including century number. */
682
          get_number (0, 9999, 4);
683
          tm->tm_year = val - 1900;
684
          want_century = 0;
685
          want_xday = 1;
686
          break;
687
        case 'Z':
688
          /* XXX How to handle this? */
689
          break;
690
        case 'E':
691
#ifdef _NL_CURRENT
692
          switch (*fmt++)
693
            {
694
            case 'c':
695
              /* Match locale's alternate date and time format. */
696
              if (*decided != raw)
697
                {
698
                  const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
699

                
700
                  if (*fmt == '\0')
701
                    fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
702

                
703
                  if (!recursive (fmt))
704
                    {
705
                      if (*decided == loc)
706
                        return NULL;
707
                      else
708
                        rp = rp_backup;
709
                    }
710
                  else
711
                    {
712
                      if (strcmp (fmt, HERE_D_T_FMT))
713
                        *decided = loc;
714
                      want_xday = 1;
715
                      break;
716
                    }
717
                  *decided = raw;
718
                }
719
              /* The C locale has no era information, so use the 
720
                 normal representation.  */
721
              if (!recursive (HERE_D_T_FMT))
722
                return NULL;
723
              want_xday = 1;
724
              break;
725
            case 'C':
726
              if (*decided != raw)
727
                {
728
                  if (era_cnt >= 0)
729
                    {
730
                      era = _nl_select_era_entry (era_cnt);
731
                      if (match_string (era->era_name, rp))
732
                        {
733
                          *decided = loc;
734
                          break;
735
                        }
736
                      else
737
                        return NULL;
738
                    }
739
                  else
740
                    {
741
                      num_eras = _NL_CURRENT_WORD (LC_TIME,
742
                                                   _NL_TIME_ERA_NUM_ENTRIES);
743
                      for (era_cnt = 0; era_cnt < (int) num_eras;
744
                           ++era_cnt, rp = rp_backup)
745
                        {
746
                          era = _nl_select_era_entry (era_cnt);
747
                          if (match_string (era->era_name, rp))
748
                            {
749
                              *decided = loc;
750
                              break;
751
                            }
752
                        }
753
                      if (era_cnt == (int) num_eras)
754
                        {
755
                          era_cnt = -1;
756
                          if (*decided == loc)
757
                            return NULL;
758
                        }
759
                      else
760
                        break;
761
                    }
762

                
763
                  *decided = raw;
764
                }
765
              /* The C locale has no era information, so use the 
766
                 normal representation.  */
767
              goto match_century;
768
            case 'y':
769
              if (*decided == raw)
770
                goto match_year_in_century;
771

                
772
              get_number(0, 9999, 4);
773
              tm->tm_year = val;
774
              want_era = 1;
775
              want_xday = 1;
776
              break;
777
            case 'Y':
778
              if (*decided != raw)
779
                {
780
                  num_eras = _NL_CURRENT_WORD (LC_TIME,
781
                                               _NL_TIME_ERA_NUM_ENTRIES);
782
                  for (era_cnt = 0; era_cnt < (int) num_eras;
783
                       ++era_cnt, rp = rp_backup)
784
                    {
785
                      era = _nl_select_era_entry (era_cnt);
786
                      if (recursive (era->era_format))
787
                        break;
788
                    }
789
                  if (era_cnt == (int) num_eras)
790
                    {
791
                      era_cnt = -1;
792
                      if (*decided == loc)
793
                        return NULL;
794
                      else
795
                        rp = rp_backup;
796
                    }
797
                  else
798
                    {
799
                      *decided = loc;
800
                      era_cnt = -1;
801
                      break;
802
                    }
803

                
804
                  *decided = raw;
805
                }
806
              get_number (0, 9999, 4);
807
              tm->tm_year = val - 1900;
808
              want_century = 0;
809
              want_xday = 1;
810
              break;
811
            case 'x':
812
              if (*decided != raw)
813
                {
814
                  const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
815

                
816
                  if (*fmt == '\0')
817
                    fmt = _NL_CURRENT (LC_TIME, D_FMT);
818

                
819
                  if (!recursive (fmt))
820
                    {
821
                      if (*decided == loc)
822
                        return NULL;
823
                      else
824
                        rp = rp_backup;
825
                    }
826
                  else
827
                    {
828
                      if (strcmp (fmt, HERE_D_FMT))
829
                        *decided = loc;
830
                      break;
831
                    }
832
                  *decided = raw;
833
                }
834
              if (!recursive (HERE_D_FMT))
835
                return NULL;
836
              break;
837
            case 'X':
838
              if (*decided != raw)
839
                {
840
                  const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
841

                
842
                  if (*fmt == '\0')
843
                    fmt = _NL_CURRENT (LC_TIME, T_FMT);
844

                
845
                  if (!recursive (fmt))
846
                    {
847
                      if (*decided == loc)
848
                        return NULL;
849
                      else
850
                        rp = rp_backup;
851
                    }
852
                  else
853
                    {
854
                      if (strcmp (fmt, HERE_T_FMT))
855
                        *decided = loc;
856
                      break;
857
                    }
858
                  *decided = raw;
859
                }
860
              if (!recursive (HERE_T_FMT))
861
                return NULL;
862
              break;
863
            default:
864
              return NULL;
865
            }
866
          break;
867
#else
868
          /* We have no information about the era format. Just use 
869
             the normal format.  */
870
          if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
871
              && *fmt != 'x' && *fmt != 'X')
872
            /* This is an illegal format. */
873
            return NULL;
874

                
875
          goto start_over;
876
#endif
877
        case 'O':
878
          switch (*fmt++)
879
            {
880
            case 'd':
881
            case 'e':
882
              /* Match day of month using alternate numeric symbols. */
883
              get_alt_number (1, 31, 2);
884
              tm->tm_mday = val;
885
              have_mday = 1;
886
              want_xday = 1;
887
              break;
888
            case 'H':
889
              /* Match hour in 24-hour clock using alternate numeric 
890
                 symbols.  */
891
              get_alt_number (0, 23, 2);
892
              tm->tm_hour = val;
893
              have_I = 0;
894
              break;
895
            case 'I':
896
              /* Match hour in 12-hour clock using alternate numeric 
897
                 symbols.  */
898
              get_alt_number (1, 12, 2);
899
              tm->tm_hour = val - 1;
900
              have_I = 1;
901
              break;
902
            case 'm':
903
              /* Match month using alternate numeric symbols. */
904
              get_alt_number (1, 12, 2);
905
              tm->tm_mon = val - 1;
906
              have_mon = 1;
907
              want_xday = 1;
908
              break;
909
            case 'M':
910
              /* Match minutes using alternate numeric symbols. */
911
              get_alt_number (0, 59, 2);
912
              tm->tm_min = val;
913
              break;
914
            case 'S':
915
              /* Match seconds using alternate numeric symbols. */
916
              get_alt_number (0, 61, 2);
917
              tm->tm_sec = val;
918
              break;
919
            case 'U':
920
            case 'V':
921
            case 'W':
922
              get_alt_number (0, 53, 2);
923
              /* XXX This cannot determine any field in TM without 
924
                 further information.  */
925
              break;
926
            case 'w':
927
              /* Match number of weekday using alternate numeric symbols. */
928
              get_alt_number (0, 6, 1);
929
              tm->tm_wday = val;
930
              have_wday = 1;
931
              break;
932
            case 'y':
933
              /* Match year within century using alternate numeric symbols. */
934
              get_alt_number (0, 99, 2);
935
              tm->tm_year = val >= 69 ? val : val + 100;
936
              want_xday = 1;
937
              break;
938
            default:
939
              return NULL;
940
            }
941
          break;
942
        default:
943
          return NULL;
944
        }
945
    }
946

                
947
  if (have_I && is_pm)
948
    tm->tm_hour += 12;
949

                
950
  if (century != -1)
951
    {
952
      if (want_century)
953
        tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
954
      else
955
        /* Only the century, but not the year. Strange, but so be it. */
956
        tm->tm_year = (century - 19) * 100;
957
    }
958

                
959
#ifdef _NL_CURRENT
960
  if (era_cnt != -1)
961
    {
962
      era = _nl_select_era_entry(era_cnt);
963
      if (want_era)
964
        tm->tm_year = (era->start_date[0]
965
                       + ((tm->tm_year - era->offset)
966
                          * era->absolute_direction));
967
      else
968
        /* Era start year assumed. */
969
        tm->tm_year = era->start_date[0];
970
    }
971
  else
972
#endif
973
    if (want_era)
974
      return NULL;
975

                
976
  if (want_xday && !have_wday)
977
    {
978
      if ( !(have_mon && have_mday) && have_yday)
979
        {
980
          /* We don't have tm_mon and/or tm_mday, compute them. */
981
          int t_mon = 0;
982
          while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
983
              t_mon++;
984
          if (!have_mon)
985
              tm->tm_mon = t_mon - 1;
986
          if (!have_mday)
987
              tm->tm_mday =
988
                (tm->tm_yday
989
                 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
990
        }
991
      day_of_the_week (tm);
992
    }
993
  if (want_xday && !have_yday)
994
    day_of_the_year (tm);
995

                
996
  return (char *) rp;
997
}
998

                
999

                
1000
char *
1001
strptime (const char *buf, const char *format, struct tm *tm)
1002
{
1003
  enum locale_status decided;
1004

                
1005
#ifdef _NL_CURRENT
1006
  decided = not;
1007
#else
1008
  decided = raw;
1009
#endif
1010
  return strptime_internal (buf, format, tm, &decided, -1);
1011
}