(HERE_D_FMT): Fix typo.
[kopensolaris-gnu/glibc.git] / time / strptime.c
1 /* Convert a string representation of time to a time value.
2    Copyright (C) 1996, 1997, 1998 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 <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 #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 *
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   *tp = *l;
65   return tp;
66 }
67 #endif /* ! _LIBC */
68 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
69
70
71 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
72 #if defined __GNUC__ && __GNUC__ >= 2
73 # define match_string(cs1, s2) \
74   ({ size_t len = strlen (cs1);                                               \
75      int result = strncasecmp ((cs1), (s2), len) == 0;                        \
76      if (result) (s2) += len;                                                 \
77      result; })
78 #else
79 /* Oh come on.  Get a reasonable compiler.  */
80 # define match_string(cs1, s2) \
81   (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
82 #endif
83 /* We intentionally do not use isdigit() for testing because this will
84    lead to problems with the wide character version.  */
85 #define get_number(from, to) \
86   do {                                                                        \
87     val = 0;                                                                  \
88     if (*rp < '0' || *rp > '9')                                               \
89       return NULL;                                                            \
90     do {                                                                      \
91       val *= 10;                                                              \
92       val += *rp++ - '0';                                                     \
93     } while (val * 10 <= to && *rp >= '0' && *rp <= '9');                     \
94     if (val < from || val > to)                                               \
95       return NULL;                                                            \
96   } while (0)
97 #ifdef _NL_CURRENT
98 # define get_alt_number(from, to) \
99   do {                                                                        \
100     if (*decided != raw)                                                      \
101       {                                                                       \
102         const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS);                 \
103         val = 0;                                                              \
104         while (*alts != '\0')                                                 \
105           {                                                                   \
106             size_t len = strlen (alts);                                       \
107             if (strncasecmp (alts, rp, len) == 0)                             \
108               break;                                                          \
109             alts = strchr (alts, '\0') + 1;                                   \
110             ++val;                                                            \
111           }                                                                   \
112         if (*alts == '\0')                                                    \
113           {                                                                   \
114             if (*decided == loc && val != 0)                                  \
115               return NULL;                                                    \
116           }                                                                   \
117         else                                                                  \
118           {                                                                   \
119             *decided = loc;                                                   \
120             break;                                                            \
121           }                                                                   \
122       }                                                                       \
123     get_number (from, to);                                                    \
124   } while (0)
125 #else
126 # define get_alt_number(from, to) \
127   /* We don't have the alternate representation.  */                          \
128   get_number(from, to)
129 #endif
130 #define recursive(new_fmt) \
131   (*(new_fmt) != '\0'                                                         \
132    && (rp = strptime_internal (rp, (new_fmt), tm, decided)) != 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;
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 #endif
180
181 /* Status of lookup: do we use the locale data or the raw data?  */
182 enum locale_status { not, loc, raw };
183
184 static char *
185 #ifdef _LIBC
186 internal_function
187 #endif
188 strptime_internal __P ((const char *buf, const char *format, struct tm *tm,
189                         enum locale_status *decided));
190
191 static char *
192 #ifdef _LIBC
193 internal_function
194 #endif
195 strptime_internal (buf, format, tm, decided)
196      const char *buf;
197      const char *format;
198      struct tm *tm;
199      enum locale_status *decided;
200 {
201   const char *rp;
202   const char *fmt;
203   int cnt;
204   size_t val;
205   int have_I, is_pm;
206
207   rp = buf;
208   fmt = format;
209   have_I = is_pm = 0;
210
211   while (*fmt != '\0')
212     {
213       /* A white space in the format string matches 0 more or white
214          space in the input string.  */
215       if (isspace (*fmt))
216         {
217           while (isspace (*rp))
218             ++rp;
219           ++fmt;
220           continue;
221         }
222
223       /* Any character but `%' must be matched by the same character
224          in the iput string.  */
225       if (*fmt != '%')
226         {
227           match_char (*fmt++, *rp++);
228           continue;
229         }
230
231       ++fmt;
232 #ifndef _NL_CURRENT
233       /* We need this for handling the `E' modifier.  */
234     start_over:
235 #endif
236       switch (*fmt++)
237         {
238         case '%':
239           /* Match the `%' character itself.  */
240           match_char ('%', *rp++);
241           break;
242         case 'a':
243         case 'A':
244           /* Match day of week.  */
245           for (cnt = 0; cnt < 7; ++cnt)
246             {
247 #ifdef _NL_CURRENT
248               if (*decided !=raw)
249                 {
250                   if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
251                     {
252                       if (*decided == not
253                           && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
254                                      weekday_name[cnt]))
255                         *decided = loc;
256                       break;
257                     }
258                   if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
259                     {
260                       if (*decided == not
261                           && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
262                                      ab_weekday_name[cnt]))
263                         *decided = loc;
264                       break;
265                     }
266                 }
267 #endif
268               if (*decided != loc
269                   && (match_string (weekday_name[cnt], rp)
270                       || match_string (ab_weekday_name[cnt], rp)))
271                 {
272                   *decided = raw;
273                   break;
274                 }
275             }
276           if (cnt == 7)
277             /* Does not match a weekday name.  */
278             return NULL;
279           tm->tm_wday = cnt;
280           break;
281         case 'b':
282         case 'B':
283         case 'h':
284           /* Match month name.  */
285           for (cnt = 0; cnt < 12; ++cnt)
286             {
287 #ifdef _NL_CURRENT
288               if (*decided !=raw)
289                 {
290                   if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
291                     {
292                       if (*decided == not
293                           && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
294                                      month_name[cnt]))
295                         *decided = loc;
296                       break;
297                     }
298                   if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
299                     {
300                       if (*decided == not
301                           && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
302                                      ab_month_name[cnt]))
303                         *decided = loc;
304                       break;
305                     }
306                 }
307 #endif
308               if (match_string (month_name[cnt], rp)
309                   || match_string (ab_month_name[cnt], rp))
310                 {
311                   *decided = raw;
312                   break;
313                 }
314             }
315           if (cnt == 12)
316             /* Does not match a month name.  */
317             return NULL;
318           tm->tm_mon = cnt;
319           break;
320         case 'c':
321           /* Match locale's date and time format.  */
322 #ifdef _NL_CURRENT
323           if (*decided != raw)
324             {
325               if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
326                 {
327                   if (*decided == loc)
328                     return NULL;
329                 }
330               else
331                 {
332                   if (*decided == not &&
333                       strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
334                     *decided = loc;
335                   break;
336                 }
337               *decided = raw;
338             }
339 #endif
340           if (!recursive (HERE_D_T_FMT))
341             return NULL;
342           break;
343         case 'C':
344           /* Match century number.  */
345           get_number (0, 99);
346           /* We don't need the number.  */
347           break;
348         case 'd':
349         case 'e':
350           /* Match day of month.  */
351           get_number (1, 31);
352           tm->tm_mday = val;
353           break;
354         case 'x':
355 #ifdef _NL_CURRENT
356           if (*decided != raw)
357             {
358               if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
359                 {
360                   if (*decided == loc)
361                     return NULL;
362                 }
363               else
364                 {
365                   if (decided == not
366                       && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
367                     *decided = loc;
368                   break;
369                 }
370               *decided = raw;
371             }
372 #endif
373           /* Fall through.  */
374         case 'D':
375           /* Match standard day format.  */
376           if (!recursive (HERE_D_FMT))
377             return NULL;
378           break;
379         case 'H':
380           /* Match hour in 24-hour clock.  */
381           get_number (0, 23);
382           tm->tm_hour = val;
383           have_I = 0;
384           break;
385         case 'I':
386           /* Match hour in 12-hour clock.  */
387           get_number (1, 12);
388           tm->tm_hour = val % 12;
389           have_I = 1;
390           break;
391         case 'j':
392           /* Match day number of year.  */
393           get_number (1, 366);
394           tm->tm_yday = val - 1;
395           break;
396         case 'm':
397           /* Match number of month.  */
398           get_number (1, 12);
399           tm->tm_mon = val - 1;
400           break;
401         case 'M':
402           /* Match minute.  */
403           get_number (0, 59);
404           tm->tm_min = val;
405           break;
406         case 'n':
407         case 't':
408           /* Match any white space.  */
409           while (isspace (*rp))
410             ++rp;
411           break;
412         case 'p':
413           /* Match locale's equivalent of AM/PM.  */
414 #ifdef _NL_CURRENT
415           if (*decided != raw)
416             {
417               if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
418                 {
419                   if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
420                     *decided = loc;
421                   break;
422                 }
423               if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
424                 {
425                   if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
426                     *decided = loc;
427                   is_pm = 1;
428                   break;
429                 }
430               *decided = raw;
431             }
432 #endif
433           if (!match_string (HERE_AM_STR, rp))
434             if (match_string (HERE_PM_STR, rp))
435               is_pm = 1;
436             else
437               return NULL;
438           break;
439         case 'r':
440 #ifdef _NL_CURRENT
441           if (*decided != raw)
442             {
443               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
444                 {
445                   if (*decided == loc)
446                     return NULL;
447                 }
448               else
449                 {
450                   if (*decided == not &&
451                       strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
452                               HERE_T_FMT_AMPM))
453                     *decided = loc;
454                   break;
455                 }
456               *decided = raw;
457             }
458 #endif
459           if (!recursive (HERE_T_FMT_AMPM))
460             return NULL;
461           break;
462         case 'R':
463           if (!recursive ("%H:%M"))
464             return NULL;
465           break;
466         case 's':
467           {
468             /* The number of seconds may be very high so we cannot use
469                the `get_number' macro.  Instead read the number
470                character for character and construct the result while
471                doing this.  */
472             time_t secs;
473             if (*rp < '0' || *rp > '9')
474               /* We need at least one digit.  */
475               return NULL;
476
477             do
478               {
479                 secs *= 10;
480                 secs += *rp++ - '0';
481               }
482             while (*rp >= '0' && *rp <= '9');
483
484             if (localtime_r (&secs, tm) == NULL)
485               /* Error in function.  */
486               return NULL;
487           }
488           break;
489         case 'S':
490           get_number (0, 61);
491           tm->tm_sec = val;
492           break;
493         case 'X':
494 #ifdef _NL_CURRENT
495           if (*decided != raw)
496             {
497               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
498                 {
499                   if (*decided == loc)
500                     return NULL;
501                 }
502               else
503                 {
504                   if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
505                     *decided = loc;
506                   break;
507                 }
508               *decided = raw;
509             }
510 #endif
511           /* Fall through.  */
512         case 'T':
513           if (!recursive (HERE_T_FMT))
514             return NULL;
515           break;
516         case 'u':
517           get_number (1, 7);
518           tm->tm_wday = val % 7;
519           break;
520         case 'g':
521           get_number (0, 99);
522           /* XXX This cannot determine any field in TM.  */
523           break;
524         case 'G':
525           if (*rp < '0' || *rp > '9')
526             return NULL;
527           /* XXX Ignore the number since we would need some more
528              information to compute a real date.  */
529           do
530             ++rp;
531           while (*rp >= '0' && *rp <= '9');
532           break;
533         case 'U':
534         case 'V':
535         case 'W':
536           get_number (0, 53);
537           /* XXX This cannot determine any field in TM without some
538              information.  */
539           break;
540         case 'w':
541           /* Match number of weekday.  */
542           get_number (0, 6);
543           tm->tm_wday = val;
544           break;
545         case 'y':
546           /* Match year within century.  */
547           get_number (0, 99);
548           /* The "Year 2000 :The Millennium Rollover" paper suggests that
549              values in the range 69-99 refer to the twentieth century.  */
550           tm->tm_year = val >= 69 ? val : val + 100;
551           break;
552         case 'Y':
553           /* Match year including century number.  */
554           get_number (0, 9999);
555           tm->tm_year = val - 1900;
556           break;
557         case 'Z':
558           /* XXX How to handle this?  */
559           break;
560         case 'E':
561 #ifdef _NL_CURRENT
562           switch (*fmt++)
563             {
564             case 'c':
565               /* Match locale's alternate date and time format.  */
566               if (*decided != raw)
567                 {
568                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
569
570                   if (*fmt == '\0')
571                     fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
572
573                   if (!recursive (fmt))
574                     {
575                       if (*decided == loc)
576                         return NULL;
577                     }
578                   else
579                     {
580                       if (strcmp (fmt, HERE_D_T_FMT))
581                         *decided = loc;
582                       break;
583                     }
584                   *decided = raw;
585                 }
586               /* The C locale has no era information, so use the
587                  normal representation.  */
588               if (!recursive (HERE_D_T_FMT))
589                 return NULL;
590               break;
591             case 'C':
592             case 'y':
593             case 'Y':
594               /* Match name of base year in locale's alternate
595                  representation.  */
596               /* XXX This is currently not implemented.  It should
597                  use the value _NL_CURRENT (LC_TIME, ERA).  */
598               break;
599             case 'x':
600               if (*decided != raw)
601                 {
602                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
603
604                   if (*fmt == '\0')
605                     fmt = _NL_CURRENT (LC_TIME, D_FMT);
606
607                   if (!recursive (fmt))
608                     {
609                       if (*decided == loc)
610                         return NULL;
611                     }
612                   else
613                     {
614                       if (strcmp (fmt, HERE_D_FMT))
615                         *decided = loc;
616                       break;
617                     }
618                   *decided = raw;
619                 }
620               if (!recursive (HERE_D_FMT))
621                 return NULL;
622               break;
623             case 'X':
624               if (*decided != raw)
625                 {
626                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
627
628                   if (*fmt == '\0')
629                     fmt = _NL_CURRENT (LC_TIME, T_FMT);
630
631                   if (!recursive (fmt))
632                     {
633                       if (*decided == loc)
634                         return NULL;
635                     }
636                   else
637                     {
638                       if (strcmp (fmt, HERE_T_FMT))
639                         *decided = loc;
640                       break;
641                     }
642                   *decided = raw;
643                 }
644               if (!recursive (HERE_T_FMT))
645                 return NULL;
646               break;
647             default:
648               return NULL;
649             }
650           break;
651 #else
652           /* We have no information about the era format.  Just use
653              the normal format.  */
654           if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
655               && *fmt != 'x' && *fmt != 'X')
656             /* This is an illegal format.  */
657             return NULL;
658
659           goto start_over;
660 #endif
661         case 'O':
662           switch (*fmt++)
663             {
664             case 'd':
665             case 'e':
666               /* Match day of month using alternate numeric symbols.  */
667               get_alt_number (1, 31);
668               tm->tm_mday = val;
669               break;
670             case 'H':
671               /* Match hour in 24-hour clock using alternate numeric
672                  symbols.  */
673               get_alt_number (0, 23);
674               tm->tm_hour = val;
675               have_I = 0;
676               break;
677             case 'I':
678               /* Match hour in 12-hour clock using alternate numeric
679                  symbols.  */
680               get_alt_number (1, 12);
681               tm->tm_hour = val - 1;
682               have_I = 1;
683               break;
684             case 'm':
685               /* Match month using alternate numeric symbols.  */
686               get_alt_number (1, 12);
687               tm->tm_mon = val - 1;
688               break;
689             case 'M':
690               /* Match minutes using alternate numeric symbols.  */
691               get_alt_number (0, 59);
692               tm->tm_min = val;
693               break;
694             case 'S':
695               /* Match seconds using alternate numeric symbols.  */
696               get_alt_number (0, 61);
697               tm->tm_sec = val;
698               break;
699             case 'U':
700             case 'V':
701             case 'W':
702               get_alt_number (0, 53);
703               /* XXX This cannot determine any field in TM without
704                  further information.  */
705               break;
706             case 'w':
707               /* Match number of weekday using alternate numeric symbols.  */
708               get_alt_number (0, 6);
709               tm->tm_wday = val;
710               break;
711             case 'y':
712               /* Match year within century using alternate numeric symbols.  */
713               get_alt_number (0, 99);
714               tm->tm_year = val >= 69 ? val : val + 100;
715               break;
716             default:
717               return NULL;
718             }
719           break;
720         default:
721           return NULL;
722         }
723     }
724
725   if (have_I && is_pm)
726     tm->tm_hour += 12;
727
728   return (char *) rp;
729 }
730
731
732 char *
733 strptime (buf, format, tm)
734      const char *buf;
735      const char *format;
736      struct tm *tm;
737 {
738   enum locale_status decided;
739 #ifdef _NL_CURRENT
740   decided = not;
741 #else
742   decided = raw;
743 #endif
744   return strptime_internal (buf, format, tm, &decided);
745 }