src/lib/w32/strptime.c

Go to the documentation of this file.
00001 #include "misc.h"
00002 
00003 /*
00004  * Ported from NetBSD to Windows by Ron Koenderink, 2007
00005  */
00006 
00007 /*      $NetBSD: strptime.c,v 1.25 2005/11/29 03:12:00 christos Exp $   */
00008 
00009 /*-
00010  * Copyright (c) 1997, 1998, 2005 The NetBSD Foundation, Inc.
00011  * All rights reserved.
00012  *
00013  * This code was contributed to The NetBSD Foundation by Klaus Klein.
00014  * Heavily optimised by David Laight
00015  *
00016  * Redistribution and use in source and binary forms, with or without
00017  * modification, are permitted provided that the following conditions
00018  * are met:
00019  * 1. Redistributions of source code must retain the above copyright
00020  *    notice, this list of conditions and the following disclaimer.
00021  * 2. Redistributions in binary form must reproduce the above copyright
00022  *    notice, this list of conditions and the following disclaimer in the
00023  *    documentation and/or other materials provided with the distribution.
00024  * 3. All advertising materials mentioning features or use of this software
00025  *    must display the following acknowledgement:
00026  *        This product includes software developed by the NetBSD
00027  *        Foundation, Inc. and its contributors.
00028  * 4. Neither the name of The NetBSD Foundation nor the names of its
00029  *    contributors may be used to endorse or promote products derived
00030  *    from this software without specific prior written permission.
00031  *
00032  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
00033  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
00034  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00035  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
00036  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00037  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00038  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00039  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00040  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00041  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00042  * POSSIBILITY OF SUCH DAMAGE.
00043  */
00044 
00045 #if !defined(_WIN32)
00046 #include <sys/cdefs.h>
00047 #endif
00048 
00049 #if defined(LIBC_SCCS) && !defined(lint)
00050 __RCSID("$NetBSD: strptime.c,v 1.25 2005/11/29 03:12:00 christos Exp $");
00051 #endif
00052 
00053 #if !defined(_WIN32)
00054 #include "namespace.h"
00055 #include <sys/localedef.h>
00056 #else
00057 typedef unsigned char u_char;
00058 typedef unsigned int uint;
00059 #endif
00060 #include <ctype.h>
00061 #include <locale.h>
00062 #include <string.h>
00063 #include <time.h>
00064 #if !defined(_WIN32)
00065 #include <tzfile.h>
00066 #endif
00067 
00068 #ifdef __weak_alias
00069 __weak_alias(strptime,_strptime)
00070 #endif
00071 
00072 #if !defined(_WIN32)
00073 #define _ctloc(x)               (_CurrentTimeLocale->x)
00074 #else
00075 #define _ctloc(x)   (x)
00076 const char *abday[] = {
00077         "Sun", "Mon", "Tue", "Wed",
00078         "Thu", "Fri", "Sat"
00079 };
00080 const char *day[] = {
00081         "Sunday", "Monday", "Tuesday", "Wednesday",
00082         "Thursday", "Friday", "Saturday"
00083 };
00084 const char *abmon[] =   {
00085         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
00086         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
00087 };
00088 const char *mon[] = {
00089         "January", "February", "March", "April", "May", "June",
00090         "July", "August", "September", "October", "November", "December"
00091 };
00092 const char *am_pm[] = {
00093         "AM", "PM"
00094 };
00095 char *d_t_fmt = "%a %Ef %T %Y";
00096 char *t_fmt_ampm = "%I:%M:%S %p";
00097 char *t_fmt = "%H:%M:%S";
00098 char *d_fmt = "%m/%d/%y";
00099 #define TM_YEAR_BASE 1900
00100 #define __UNCONST(x) ((void *)(((const char *)(x) - (const char *)0) + (char *)0))
00101 
00102 #endif
00103 /*
00104  * We do not implement alternate representations. However, we always
00105  * check whether a given modifier is allowed for a certain conversion.
00106  */
00107 #define ALT_E                   0x01
00108 #define ALT_O                   0x02
00109 #define LEGAL_ALT(x)            { if (alt_format & ~(x)) return NULL; }
00110 
00111 
00112 static const u_char *conv_num(const unsigned char *, int *, uint, uint);
00113 static const u_char *find_string(const u_char *, int *, const char * const *,
00114         const char * const *, int);
00115 
00116 
00117 char *
00118 strptime(const char *buf, const char *fmt, struct tm *tm)
00119 {
00120         unsigned char c;
00121         const unsigned char *bp;
00122         int alt_format, i, split_year = 0;
00123         const char *new_fmt;
00124 
00125         bp = (const u_char *)buf;
00126 
00127         while (bp != NULL && (c = *fmt++) != '\0') {
00128                 /* Clear `alternate' modifier prior to new conversion. */
00129                 alt_format = 0;
00130                 i = 0;
00131 
00132                 /* Eat up white-space. */
00133                 if (isspace(c)) {
00134                         while (isspace(*bp))
00135                                 bp++;
00136                         continue;
00137                 }
00138 
00139                 if (c != '%')
00140                         goto literal;
00141 
00142 
00143 again:          switch (c = *fmt++) {
00144                 case '%':       /* "%%" is converted to "%". */
00145 literal:
00146                         if (c != *bp++)
00147                                 return NULL;
00148                         LEGAL_ALT(0);
00149                         continue;
00150 
00151                 /*
00152                  * "Alternative" modifiers. Just set the appropriate flag
00153                  * and start over again.
00154                  */
00155                 case 'E':       /* "%E?" alternative conversion modifier. */
00156                         LEGAL_ALT(0);
00157                         alt_format |= ALT_E;
00158                         goto again;
00159 
00160                 case 'O':       /* "%O?" alternative conversion modifier. */
00161                         LEGAL_ALT(0);
00162                         alt_format |= ALT_O;
00163                         goto again;
00164 
00165                 /*
00166                  * "Complex" conversion rules, implemented through recursion.
00167                  */
00168                 case 'c':       /* Date and time, using the locale's format. */
00169                         new_fmt = _ctloc(d_t_fmt);
00170                         goto recurse;
00171 
00172                 case 'D':       /* The date as "%m/%d/%y". */
00173                         new_fmt = "%m/%d/%y";
00174                         LEGAL_ALT(0);
00175                         goto recurse;
00176 
00177                 case 'R':       /* The time as "%H:%M". */
00178                         new_fmt = "%H:%M";
00179                         LEGAL_ALT(0);
00180                         goto recurse;
00181 
00182                 case 'r':       /* The time in 12-hour clock representation. */
00183                         new_fmt =_ctloc(t_fmt_ampm);
00184                         LEGAL_ALT(0);
00185                         goto recurse;
00186 
00187                 case 'T':       /* The time as "%H:%M:%S". */
00188                         new_fmt = "%H:%M:%S";
00189                         LEGAL_ALT(0);
00190                         goto recurse;
00191 
00192                 case 'X':       /* The time, using the locale's format. */
00193                         new_fmt =_ctloc(t_fmt);
00194                         goto recurse;
00195 
00196                 case 'x':       /* The date, using the locale's format. */
00197                         new_fmt =_ctloc(d_fmt);
00198                     recurse:
00199                         bp = (const u_char *)strptime((const char *)bp,
00200                                                             new_fmt, tm);
00201                         LEGAL_ALT(ALT_E);
00202                         continue;
00203 
00204                 /*
00205                  * "Elementary" conversion rules.
00206                  */
00207                 case 'A':       /* The day of week, using the locale's form. */
00208                 case 'a':
00209                         bp = find_string(bp, &tm->tm_wday, _ctloc(day),
00210                                         _ctloc(abday), 7);
00211                         LEGAL_ALT(0);
00212                         continue;
00213 
00214                 case 'B':       /* The month, using the locale's form. */
00215                 case 'b':
00216                 case 'h':
00217                         bp = find_string(bp, &tm->tm_mon, _ctloc(mon),
00218                                         _ctloc(abmon), 12);
00219                         LEGAL_ALT(0);
00220                         continue;
00221 
00222                 case 'C':       /* The century number. */
00223                         i = 20;
00224                         bp = conv_num(bp, &i, 0, 99);
00225 
00226                         i = i * 100 - TM_YEAR_BASE;
00227                         if (split_year)
00228                                 i += tm->tm_year % 100;
00229                         split_year = 1;
00230                         tm->tm_year = i;
00231                         LEGAL_ALT(ALT_E);
00232                         continue;
00233 
00234                 case 'd':       /* The day of month. */
00235                 case 'e':
00236                         bp = conv_num(bp, &tm->tm_mday, 1, 31);
00237                         LEGAL_ALT(ALT_O);
00238                         continue;
00239 
00240                 case 'k':       /* The hour (24-hour clock representation). */
00241                         LEGAL_ALT(0);
00242                         /* FALLTHROUGH */
00243                 case 'H':
00244                         bp = conv_num(bp, &tm->tm_hour, 0, 23);
00245                         LEGAL_ALT(ALT_O);
00246                         continue;
00247 
00248                 case 'l':       /* The hour (12-hour clock representation). */
00249                         LEGAL_ALT(0);
00250                         /* FALLTHROUGH */
00251                 case 'I':
00252                         bp = conv_num(bp, &tm->tm_hour, 1, 12);
00253                         if (tm->tm_hour == 12)
00254                                 tm->tm_hour = 0;
00255                         LEGAL_ALT(ALT_O);
00256                         continue;
00257 
00258                 case 'j':       /* The day of year. */
00259                         i = 1;
00260                         bp = conv_num(bp, &i, 1, 366);
00261                         tm->tm_yday = i - 1;
00262                         LEGAL_ALT(0);
00263                         continue;
00264 
00265                 case 'M':       /* The minute. */
00266                         bp = conv_num(bp, &tm->tm_min, 0, 59);
00267                         LEGAL_ALT(ALT_O);
00268                         continue;
00269 
00270                 case 'm':       /* The month. */
00271                         i = 1;
00272                         bp = conv_num(bp, &i, 1, 12);
00273                         tm->tm_mon = i - 1;
00274                         LEGAL_ALT(ALT_O);
00275                         continue;
00276 
00277                 case 'p':       /* The locale's equivalent of AM/PM. */
00278                         bp = find_string(bp, &i, _ctloc(am_pm), NULL, 2);
00279                         if (tm->tm_hour > 11)
00280                                 return NULL;
00281                         tm->tm_hour += i * 12;
00282                         LEGAL_ALT(0);
00283                         continue;
00284 
00285                 case 'S':       /* The seconds. */
00286                         bp = conv_num(bp, &tm->tm_sec, 0, 61);
00287                         LEGAL_ALT(ALT_O);
00288                         continue;
00289 
00290                 case 'U':       /* The week of year, beginning on sunday. */
00291                 case 'W':       /* The week of year, beginning on monday. */
00292                         /*
00293                          * XXX This is bogus, as we can not assume any valid
00294                          * information present in the tm structure at this
00295                          * point to calculate a real value, so just check the
00296                          * range for now.
00297                          */
00298                          bp = conv_num(bp, &i, 0, 53);
00299                          LEGAL_ALT(ALT_O);
00300                          continue;
00301 
00302                 case 'w':       /* The day of week, beginning on sunday. */
00303                         bp = conv_num(bp, &tm->tm_wday, 0, 6);
00304                         LEGAL_ALT(ALT_O);
00305                         continue;
00306 
00307                 case 'Y':       /* The year. */
00308                         i = TM_YEAR_BASE;       /* just for data sanity... */
00309                         bp = conv_num(bp, &i, 0, 9999);
00310                         tm->tm_year = i - TM_YEAR_BASE;
00311                         LEGAL_ALT(ALT_E);
00312                         continue;
00313 
00314                 case 'y':       /* The year within 100 years of the epoch. */
00315                         /* LEGAL_ALT(ALT_E | ALT_O); */
00316                         bp = conv_num(bp, &i, 0, 99);
00317 
00318                         if (split_year)
00319                                 /* preserve century */
00320                                 i += (tm->tm_year / 100) * 100;
00321                         else {
00322                                 split_year = 1;
00323                                 if (i <= 68)
00324                                         i = i + 2000 - TM_YEAR_BASE;
00325                                 else
00326                                         i = i + 1900 - TM_YEAR_BASE;
00327                         }
00328                         tm->tm_year = i;
00329                         continue;
00330 
00331                 /*
00332                  * Miscellaneous conversions.
00333                  */
00334                 case 'n':       /* Any kind of white-space. */
00335                 case 't':
00336                         while (isspace(*bp))
00337                                 bp++;
00338                         LEGAL_ALT(0);
00339                         continue;
00340 
00341 
00342                 default:        /* Unknown/unsupported conversion. */
00343                         return NULL;
00344                 }
00345         }
00346 
00347         return __UNCONST(bp);
00348 }
00349 
00350 
00351 static const u_char *
00352 conv_num(const unsigned char *buf, int *dest, uint llim, uint ulim)
00353 {
00354         uint result = 0;
00355         unsigned char ch;
00356 
00357         /* The limit also determines the number of valid digits. */
00358         uint rulim = ulim;
00359 
00360         ch = *buf;
00361         if (ch < '0' || ch > '9')
00362                 return NULL;
00363 
00364         do {
00365                 result *= 10;
00366                 result += ch - '0';
00367                 rulim /= 10;
00368                 ch = *++buf;
00369         } while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9');
00370 
00371         if (result < llim || result > ulim)
00372                 return NULL;
00373 
00374         *dest = result;
00375         return buf;
00376 }
00377 
00378 static const u_char *
00379 find_string(const u_char *bp, int *tgt, const char * const *n1,
00380                 const char * const *n2, int c)
00381 {
00382         int i;
00383         unsigned int len;
00384 
00385         /* check full name - then abbreviated ones */
00386         for (; n1 != NULL; n1 = n2, n2 = NULL) {
00387                 for (i = 0; i < c; i++, n1++) {
00388                         len = strlen(*n1);
00389                         if (strncasecmp(*n1, (const char *)bp, len) == 0) {
00390                                 *tgt = i;
00391                                 return bp + len;
00392                         }
00393                 }
00394         }
00395 
00396         /* Nothing matched */
00397         return NULL;
00398 }

Generated on Fri Mar 28 11:01:16 2008 for empserver by  doxygen 1.5.2