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