Home | History | Annotate | Download | only in tzcode
      1 /*  $OpenBSD: strptime.c,v 1.11 2005/08/08 08:05:38 espie Exp $ */
      2 /*  $NetBSD: strptime.c,v 1.12 1998/01/20 21:39:40 mycroft Exp $    */
      3 
      4 /*-
      5  * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
      6  * All rights reserved.
      7  *
      8  * This code was contributed to The NetBSD Foundation by Klaus Klein.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *        This product includes software developed by the NetBSD
     21  *        Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 //#include <sys/localedef.h>
     40 #include <ctype.h>
     41 #include <locale.h>
     42 #include <string.h>
     43 #include <time.h>
     44 #include "tzfile.h"
     45 
     46 static const struct {
     47     const char *abday[7];
     48     const char *day[7];
     49     const char *abmon[12];
     50     const char *mon[12];
     51     const char *am_pm[2];
     52     const char *d_t_fmt;
     53     const char *d_fmt;
     54     const char *t_fmt;
     55     const char *t_fmt_ampm;
     56 } _DefaultTimeLocale = {
     57     {
     58         "Sun","Mon","Tue","Wed","Thu","Fri","Sat",
     59     },
     60     {
     61         "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
     62         "Friday", "Saturday"
     63     },
     64     {
     65         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
     66         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
     67     },
     68     {
     69         "January", "February", "March", "April", "May", "June", "July",
     70         "August", "September", "October", "November", "December"
     71     },
     72     {
     73         "AM", "PM"
     74     },
     75     "%a %b %d %H:%M:%S %Y",
     76     "%m/%d/%y",
     77     "%H:%M:%S",
     78     "%I:%M:%S %p"
     79 };
     80 
     81 #define _ctloc(x) (_DefaultTimeLocale.x)
     82 
     83 /*
     84  * We do not implement alternate representations. However, we always
     85  * check whether a given modifier is allowed for a certain conversion.
     86  */
     87 #define _ALT_E          0x01
     88 #define _ALT_O          0x02
     89 #define _LEGAL_ALT(x)       { if (alt_format & ~(x)) return (0); }
     90 
     91 
     92 static  int _conv_num(const unsigned char **, int *, int, int);
     93 static  unsigned char *_strptime(const unsigned char *, const char *, struct tm *, int);
     94 
     95 
     96 char *
     97 strptime(const char *buf, const char *fmt, struct tm *tm)
     98 {
     99     return (char*)(_strptime((const unsigned char*)buf, fmt, tm, 1));
    100 }
    101 
    102 static unsigned char *
    103 _strptime(const unsigned char *buf, const char *fmt, struct tm *tm, int initialize)
    104 {
    105     unsigned char c;
    106     const unsigned char *bp;
    107     size_t len = 0;
    108     int alt_format, i;
    109     static int century, relyear;
    110 
    111     if (initialize) {
    112         century = TM_YEAR_BASE;
    113         relyear = -1;
    114     }
    115 
    116     bp = (unsigned char *)buf;
    117     while ((c = *fmt) != '\0') {
    118         /* Clear `alternate' modifier prior to new conversion. */
    119         alt_format = 0;
    120 
    121         /* Eat up white-space. */
    122         if (isspace(c)) {
    123             while (isspace(*bp))
    124                 bp++;
    125 
    126             fmt++;
    127             continue;
    128         }
    129 
    130         if ((c = *fmt++) != '%')
    131             goto literal;
    132 
    133 
    134 again:      switch (c = *fmt++) {
    135         case '%':   /* "%%" is converted to "%". */
    136 literal:
    137         if (c != *bp++)
    138             return (NULL);
    139 
    140         break;
    141 
    142         /*
    143          * "Alternative" modifiers. Just set the appropriate flag
    144          * and start over again.
    145          */
    146         case 'E':   /* "%E?" alternative conversion modifier. */
    147             _LEGAL_ALT(0);
    148             alt_format |= _ALT_E;
    149             goto again;
    150 
    151         case 'O':   /* "%O?" alternative conversion modifier. */
    152             _LEGAL_ALT(0);
    153             alt_format |= _ALT_O;
    154             goto again;
    155 
    156         /*
    157          * "Complex" conversion rules, implemented through recursion.
    158          */
    159         case 'c':   /* Date and time, using the locale's format. */
    160             _LEGAL_ALT(_ALT_E);
    161             if (!(bp = _strptime(bp, _ctloc(d_t_fmt), tm, 0)))
    162                 return (NULL);
    163             break;
    164 
    165         case 'D':   /* The date as "%m/%d/%y". */
    166             _LEGAL_ALT(0);
    167             if (!(bp = _strptime(bp, "%m/%d/%y", tm, 0)))
    168                 return (NULL);
    169             break;
    170 
    171         case 'R':   /* The time as "%H:%M". */
    172             _LEGAL_ALT(0);
    173             if (!(bp = _strptime(bp, "%H:%M", tm, 0)))
    174                 return (NULL);
    175             break;
    176 
    177         case 'r':   /* The time as "%I:%M:%S %p". */
    178             _LEGAL_ALT(0);
    179             if (!(bp = _strptime(bp, "%I:%M:%S %p", tm, 0)))
    180                 return (NULL);
    181             break;
    182 
    183         case 'T':   /* The time as "%H:%M:%S". */
    184             _LEGAL_ALT(0);
    185             if (!(bp = _strptime(bp, "%H:%M:%S", tm, 0)))
    186                 return (NULL);
    187             break;
    188 
    189         case 'X':   /* The time, using the locale's format. */
    190             _LEGAL_ALT(_ALT_E);
    191             if (!(bp = _strptime(bp, _ctloc(t_fmt), tm, 0)))
    192                 return (NULL);
    193             break;
    194 
    195         case 'x':   /* The date, using the locale's format. */
    196             _LEGAL_ALT(_ALT_E);
    197             if (!(bp = _strptime(bp, _ctloc(d_fmt), tm, 0)))
    198                 return (NULL);
    199             break;
    200 
    201         /*
    202          * "Elementary" conversion rules.
    203          */
    204         case 'A':   /* The day of week, using the locale's form. */
    205         case 'a':
    206             _LEGAL_ALT(0);
    207             for (i = 0; i < 7; i++) {
    208                 /* Full name. */
    209                 len = strlen(_ctloc(day[i]));
    210                 if (strncasecmp(_ctloc(day[i]), (const char*)bp, len) == 0)
    211                     break;
    212 
    213                 /* Abbreviated name. */
    214                 len = strlen(_ctloc(abday[i]));
    215                 if (strncasecmp(_ctloc(abday[i]), (const char*)bp, len) == 0)
    216                     break;
    217             }
    218 
    219             /* Nothing matched. */
    220             if (i == 7)
    221                 return (NULL);
    222 
    223             tm->tm_wday = i;
    224             bp += len;
    225             break;
    226 
    227         case 'B':   /* The month, using the locale's form. */
    228         case 'b':
    229         case 'h':
    230             _LEGAL_ALT(0);
    231             for (i = 0; i < 12; i++) {
    232                 /* Full name. */
    233                 len = strlen(_ctloc(mon[i]));
    234                 if (strncasecmp(_ctloc(mon[i]), (const char*)bp, len) == 0)
    235                     break;
    236 
    237                 /* Abbreviated name. */
    238                 len = strlen(_ctloc(abmon[i]));
    239                 if (strncasecmp(_ctloc(abmon[i]), (const char*)bp, len) == 0)
    240                     break;
    241             }
    242 
    243             /* Nothing matched. */
    244             if (i == 12)
    245                 return (NULL);
    246 
    247             tm->tm_mon = i;
    248             bp += len;
    249             break;
    250 
    251         case 'C':   /* The century number. */
    252             _LEGAL_ALT(_ALT_E);
    253             if (!(_conv_num(&bp, &i, 0, 99)))
    254                 return (NULL);
    255 
    256             century = i * 100;
    257             break;
    258 
    259         case 'd':   /* The day of month. */
    260         case 'e':
    261             _LEGAL_ALT(_ALT_O);
    262             if (!(_conv_num(&bp, &tm->tm_mday, 1, 31)))
    263                 return (NULL);
    264             break;
    265 
    266         case 'k':   /* The hour (24-hour clock representation). */
    267             _LEGAL_ALT(0);
    268             /* FALLTHROUGH */
    269         case 'H':
    270             _LEGAL_ALT(_ALT_O);
    271             if (!(_conv_num(&bp, &tm->tm_hour, 0, 23)))
    272                 return (NULL);
    273             break;
    274 
    275         case 'l':   /* The hour (12-hour clock representation). */
    276             _LEGAL_ALT(0);
    277             /* FALLTHROUGH */
    278         case 'I':
    279             _LEGAL_ALT(_ALT_O);
    280             if (!(_conv_num(&bp, &tm->tm_hour, 1, 12)))
    281                 return (NULL);
    282             break;
    283 
    284         case 'j':   /* The day of year. */
    285             _LEGAL_ALT(0);
    286             if (!(_conv_num(&bp, &tm->tm_yday, 1, 366)))
    287                 return (NULL);
    288             tm->tm_yday--;
    289             break;
    290 
    291         case 'M':   /* The minute. */
    292             _LEGAL_ALT(_ALT_O);
    293             if (!(_conv_num(&bp, &tm->tm_min, 0, 59)))
    294                 return (NULL);
    295             break;
    296 
    297         case 'm':   /* The month. */
    298             _LEGAL_ALT(_ALT_O);
    299             if (!(_conv_num(&bp, &tm->tm_mon, 1, 12)))
    300                 return (NULL);
    301             tm->tm_mon--;
    302             break;
    303 
    304         case 'p':   /* The locale's equivalent of AM/PM. */
    305             _LEGAL_ALT(0);
    306             /* AM? */
    307             len = strlen(_ctloc(am_pm[0]));
    308             if (strncasecmp(_ctloc(am_pm[0]), (const char*)bp, len) == 0) {
    309                 if (tm->tm_hour > 12)   /* i.e., 13:00 AM ?! */
    310                     return (NULL);
    311                 else if (tm->tm_hour == 12)
    312                     tm->tm_hour = 0;
    313 
    314                 bp += len;
    315                 break;
    316             }
    317             /* PM? */
    318             len = strlen(_ctloc(am_pm[1]));
    319             if (strncasecmp(_ctloc(am_pm[1]), (const char*)bp, len) == 0) {
    320                 if (tm->tm_hour > 12)   /* i.e., 13:00 PM ?! */
    321                     return (NULL);
    322                 else if (tm->tm_hour < 12)
    323                     tm->tm_hour += 12;
    324 
    325                 bp += len;
    326                 break;
    327             }
    328 
    329             /* Nothing matched. */
    330             return (NULL);
    331 
    332         case 'S':   /* The seconds. */
    333             _LEGAL_ALT(_ALT_O);
    334             if (!(_conv_num(&bp, &tm->tm_sec, 0, 61)))
    335                 return (NULL);
    336             break;
    337 
    338         case 'U':   /* The week of year, beginning on sunday. */
    339         case 'W':   /* The week of year, beginning on monday. */
    340             _LEGAL_ALT(_ALT_O);
    341             /*
    342              * XXX This is bogus, as we can not assume any valid
    343              * information present in the tm structure at this
    344              * point to calculate a real value, so just check the
    345              * range for now.
    346              */
    347              if (!(_conv_num(&bp, &i, 0, 53)))
    348                 return (NULL);
    349              break;
    350 
    351         case 'w':   /* The day of week, beginning on sunday. */
    352             _LEGAL_ALT(_ALT_O);
    353             if (!(_conv_num(&bp, &tm->tm_wday, 0, 6)))
    354                 return (NULL);
    355             break;
    356 
    357         case 'Y':   /* The year. */
    358             _LEGAL_ALT(_ALT_E);
    359             if (!(_conv_num(&bp, &i, 0, 9999)))
    360                 return (NULL);
    361 
    362             relyear = -1;
    363             tm->tm_year = i - TM_YEAR_BASE;
    364             break;
    365 
    366         case 'y':   /* The year within the century (2 digits). */
    367             _LEGAL_ALT(_ALT_E | _ALT_O);
    368             if (!(_conv_num(&bp, &relyear, 0, 99)))
    369                 return (NULL);
    370             break;
    371 
    372         /*
    373          * Miscellaneous conversions.
    374          */
    375         case 'n':   /* Any kind of white-space. */
    376         case 't':
    377             _LEGAL_ALT(0);
    378             while (isspace(*bp))
    379                 bp++;
    380             break;
    381 
    382 
    383         default:    /* Unknown/unsupported conversion. */
    384             return (NULL);
    385         }
    386 
    387 
    388     }
    389 
    390     /*
    391      * We need to evaluate the two digit year spec (%y)
    392      * last as we can get a century spec (%C) at any time.
    393      */
    394     if (relyear != -1) {
    395         if (century == TM_YEAR_BASE) {
    396             if (relyear <= 68)
    397                 tm->tm_year = relyear + 2000 - TM_YEAR_BASE;
    398             else
    399                 tm->tm_year = relyear + 1900 - TM_YEAR_BASE;
    400         } else {
    401             tm->tm_year = relyear + century - TM_YEAR_BASE;
    402         }
    403     }
    404 
    405     return (unsigned char*)bp;
    406 }
    407 
    408 
    409 static int
    410 _conv_num(const unsigned char **buf, int *dest, int llim, int ulim)
    411 {
    412     int result = 0;
    413     int rulim = ulim;
    414 
    415     if (**buf < '0' || **buf > '9')
    416         return (0);
    417 
    418     /* we use rulim to break out of the loop when we run out of digits */
    419     do {
    420         result *= 10;
    421         result += *(*buf)++ - '0';
    422         rulim /= 10;
    423     } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
    424 
    425     if (result < llim || result > ulim)
    426         return (0);
    427 
    428     *dest = result;
    429     return (1);
    430 }
    431