parse_date.cxx

Go to the documentation of this file.
00001 /*-
00002  * Changes to this file are copyright Lowell Boggs Jr, 2003.  My changes
00003  * only envolve fixing a bug in the parsing of dates without time specifications
00004  * and minor modifications needed to make the file compile in my environment.
00005  * I have done my best not to make unnecessary changes that would obscure any
00006  * diffing process.  However, in order to understand what the code was doing
00007  * so I could fix the bug envolving missing time specs on dates, I did have to
00008  * comment some code.  
00009  *
00010  * This file was obtained originally from the following
00011  * web site:
00012  *
00013  *  http://www.gnu-darwin.org/sources/src/usr.sbin/pw/psdate.c
00014  * 
00015  * Many thanks to the generosity of Mr. Nugent.
00016  *
00017  * Copyright (C) 1996
00018  *      David L. Nugent.  All rights reserved.
00019  *
00020  * Redistribution and use in source and binary forms, with or without
00021  * modification, are permitted provided that the following conditions
00022  * are met:
00023  * 1. Redistributions of source code must retain the above copyright
00024  *    notice, this list of conditions and the following disclaimer.
00025  * 2. Redistributions in binary form must reproduce the above copyright
00026  *    notice, this list of conditions and the following disclaimer in the
00027  *    documentation and/or other materials provided with the distribution.
00028  *
00029  * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
00030  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00031  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00032  * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
00033  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00034  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00035  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00036  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00037  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00038  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00039  * SUCH DAMAGE.
00040  */
00041 
00042 #ifndef lint
00043 static const char rcsid[] =
00044   "$FreeBSD: src/usr.sbin/pw/psdate.c,v 1.6.2.1 2000/06/28 19:19:04 ache Exp $";
00045 #endif /* not lint */
00046 
00047 #include <portable_io.h>
00048 #include <stdlib.h>
00049 #include <string.h>
00050 #include <ctype.h>
00051 #include <parse_date.h>  
00052   // the above include file name was changed to make the file compile
00053   // in Lowell Boggs' environment.
00054 
00055 
00056 static int
00057 a2i(char const ** str)
00058   //
00059   //  Parse a integer out of a string and move the pointer into the string
00060   //  past the digits of the integer.
00061   //
00062 {
00063         int             i = 0;
00064         char const     *s = *str;
00065 
00066         if (isdigit((unsigned char)*s)) {
00067                 i = atoi(s);
00068                 while (isdigit((unsigned char)*s))
00069                         ++s;
00070                 *str = s;
00071         }
00072         return i;
00073 }
00074 
00075 static int
00076 numerics(char const * str)  
00077   //
00078   // returns true if all characters in the string are decimal digits or the
00079   // 'x' character
00080   //
00081 {
00082         int             rc = isdigit((unsigned char)*str);
00083 
00084         if (rc)
00085                 while (isdigit((unsigned char)*str) || *str == 'x')
00086                         ++str;
00087         return rc && !*str;
00088 }
00089 
00090 static int
00091 aindex(char const * arr[], char const ** str, int len)
00092   //
00093   //  Search an array of strings for a specified string and return the
00094   //  array index to that string.  Return a negative number to mean 'not
00095   //  found.  If the string is found in the array, assume that the str
00096   //  is the beginning of a longer sequence and move the pointer into that
00097   //  string past the matched parts and any trailing ',' and whitespace
00098   //  found after the matching parts.
00099   //
00100 {
00101         int             l, i;
00102         char            mystr[32];
00103 
00104         mystr[len] = '\0';
00105         l = strlen(strncpy(mystr, *str, len));
00106         for (i = 0; i < l; i++)
00107                 mystr[i] = (char) tolower((unsigned char)mystr[i]);
00108         for (i = 0; arr[i] && strcmp(mystr, arr[i]) != 0; i++);
00109         if (arr[i] == NULL)
00110                 i = -1;
00111         else {                  /* Skip past it */
00112                 while (**str && isalpha((unsigned char)**str))
00113                         ++(*str);
00114                 /* And any following whitespace */
00115                 while (**str && (**str == ',' || isspace((unsigned char)**str)))
00116                         ++(*str);
00117         }                       /* Return index */
00118         return i;
00119 }
00120 
00121 static int
00122 weekday(char const ** str)
00123   //
00124   //  Determine if the specified string pointer refers to a week day's name
00125   //  and if so, move the string pointer past that name and any trailing ,
00126   //  and white space.
00127   //
00128 {
00129         static char const *days[] =
00130         {"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL};
00131 
00132         return aindex(days, str, 3);
00133 }
00134 
00135 static int
00136 month(char const ** str)
00137   //
00138   //  Determine if the specified string pointer refers to a month name
00139   //  and if so, move the string pointer past that name and any trailing ,
00140   //  and white space.
00141   //
00142 {
00143         static char const *months[] =
00144         {"jan", "feb", "mar", "apr", "may", "jun", "jul",
00145         "aug", "sep", "oct", "nov", "dec", NULL};
00146 
00147         return aindex(months, str, 3);
00148 }
00149 
00150 static void
00151 parse_time(char const * str, int *hour, int *min, int *sec)
00152   //
00153   //  Parse the hour, minute, and second out of a time string.  Do not move
00154   //  the string pointer past the string.
00155   //
00156 {
00157         *hour = a2i(&str);
00158         if ((str = strchr(str, ':')) == NULL)
00159                 *min = *sec = 0;
00160         else {
00161                 ++str;
00162                 *min = a2i(&str);
00163                 *sec = ((str = strchr(str, ':')) == NULL) ? 0 : atoi(++str);
00164         }
00165 }
00166 
00167 
00168 static void
00169 parse_datesub(char const * str, int *day, int *mon, int *year)
00170   //
00171   //  Parse the year, month, and day out of a string but don't move the
00172   //  the string pointer past the date.
00173   //
00174 {
00175         int             i;
00176 
00177         static char const nchrs[] = "0123456789 \t,/-.";
00178 
00179         if ((i = month(&str)) != -1) {
00180                 *mon = i;
00181                 if ((i = a2i(&str)) != 0)
00182                         *day = i;
00183         } else if ((i = a2i(&str)) != 0) {
00184                 *day = i;
00185                 while (*str && strchr(nchrs + 10, *str) != NULL)
00186                         ++str;
00187                 if ((i = month(&str)) != -1)
00188                         *mon = i;
00189                 else if ((i = a2i(&str)) != 0)
00190                         *mon = i - 1;
00191         } else
00192                 return;
00193 
00194         while (*str && strchr(nchrs + 10, *str) != NULL)
00195                 ++str;
00196         if (isdigit((unsigned char)*str)) {
00197                 *year = atoi(str);
00198                 if (*year > 1900)
00199                         *year -= 1900;
00200                 else if (*year < 32)
00201                         *year += 100;
00202         }
00203 }
00204 
00205 
00206 /*-
00207  * Parse time must be flexible, it handles the following formats:
00208  * nnnnnnnnnnn          UNIX timestamp (all numeric), 0 = now
00209  * 0xnnnnnnnn           UNIX timestamp in hexadecimal
00210  * 0nnnnnnnnn           UNIX timestamp in octal
00211  * 0                    Given time
00212  * +nnnn[smhdwoy]       Given time + nnnn hours, mins, days, weeks, months or years
00213  * -nnnn[smhdwoy]       Given time - nnnn hours, mins, days, weeks, months or years
00214  * dd[ ./-]mmm[ ./-]yy  Date }
00215  * hh:mm:ss             Time } May be combined
00216  */
00217 
00218 time_t
00219 parse_date(char const * str, time_t dt)
00220 {
00221         char           *p;
00222         int             i;
00223         long            val;
00224         struct tm      *T;
00225 
00226         //
00227         // setup the default date/time (dt).  If 0 is passed in,
00228         // use the current time as the default.
00229         //
00230 
00231         if (dt == 0)
00232                 dt = time(NULL);
00233 
00234         //
00235         //  remove leading spaces from the string
00236         //
00237 
00238         while (*str && isspace((unsigned char)*str))
00239                 ++str;
00240 
00241         if (numerics(str)) {
00242           //
00243           // If the remainder of the string, after initial blank removal
00244           // is all decimal digits, then assume that is the unix time value.
00245           // (Seconds from 1970).
00246           //
00247                 dt = strtol(str, &p, 0);
00248 
00249         } else if (*str == '+' || *str == '-') {
00250 
00251           //
00252           // Otherwise,  if the string begins with '+' or '-', assume
00253           // that we are dealing with a string defining an offset from
00254           // the 'default' date/time passed in (or time now if 0 is passed
00255           // in).
00256           //
00257 
00258                 val = strtol(str, &p, 0);
00259                 switch (*p) {
00260                 case 'h':
00261                 case 'H':       /* hours */
00262                         dt += (val * 3600L);
00263                         break;
00264                 case '\0':
00265                 case 'm':
00266                 case 'M':       /* minutes */
00267                         dt += (val * 60L);
00268                         break;
00269                 case 's':
00270                 case 'S':       /* seconds */
00271                         dt += val;
00272                         break;
00273                 case 'd':
00274                 case 'D':       /* days */
00275                         dt += (val * 86400L);
00276                         break;
00277                 case 'w':
00278                 case 'W':       /* weeks */
00279                         dt += (val * 604800L);
00280                         break;
00281                 case 'o':
00282                 case 'O':       /* months */
00283                         T = localtime(&dt);
00284                         T->tm_mon += (int) val;
00285                         i = T->tm_mday;
00286                         goto fixday;
00287                 case 'y':
00288                 case 'Y':       /* years */
00289                         T = localtime(&dt);
00290                         T->tm_year += (int) val;
00291                         i = T->tm_mday;
00292         fixday:
00293                         dt = mktime(T);
00294                         T = localtime(&dt);
00295                         if (T->tm_mday != i) {
00296                                 T->tm_mday = 1;
00297                                 dt = mktime(T);
00298                                 dt -= (time_t) 86400L;
00299                         }
00300                 default:        /* unknown */
00301                         break;  /* leave untouched */
00302                 }
00303         } else {
00304 
00305                 //
00306                 // At this point, we have determine that we have more than
00307                 // merely a year specification, and are dealing with more
00308                 // than just a time offset from the specified default time,
00309                 // dt.
00310                 //
00311 
00312                 char           *q, tmp[64];
00313 
00314                 /*
00315                  * Ignore any user specified week day.
00316                  */
00317                 weekday(&str);
00318                 str = strncpy(tmp, str, sizeof tmp - 1);
00319                 tmp[sizeof tmp - 1] = '\0';
00320                 T = localtime(&dt);
00321                 T->tm_isdst = -1;  // -1 means info not avail, LB.
00322 
00323                 /*
00324                  * See if we can break off any timezone
00325                  */
00326                 while ((q = strrchr(tmp, ' ')) != NULL) {
00327 
00328                         //
00329                         // time strings ending in a time zone specification,
00330                         // such as -05:00, are removed
00331                         // from the string.  This represent time zone
00332                         // values which are not needed given our use of
00333                         // localtime.
00334                         //
00335 
00336                         if (strchr("(+-", q[1]) != NULL)
00337                                 *q = '\0';
00338                         else {
00339                                 int             j = 1;
00340 
00341                                 while (q[j] && isupper((unsigned char)q[j]))
00342                                         ++j;
00343                                 if (q[j] == '\0')
00344                                         *q = '\0';
00345                                 else
00346                                         break;
00347                         }
00348                 }
00349 
00350                 /*
00351                  * See if there is a time hh:mm[:ss]
00352                  */
00353                 if ((p = strchr(tmp, ':')) == NULL) {
00354 
00355                         /*
00356                          * No time string involved
00357                          */
00358                         T->tm_hour = T->tm_min = T->tm_sec = 0;
00359                         parse_datesub(tmp, &T->tm_mday, &T->tm_mon, &T->tm_year);
00360 
00361                 } else {
00362                         char            datestr[64], timestr[64];
00363 
00364                         /*
00365                          * Let's chip off the time string
00366                          */
00367                         if ((q = strpbrk(p, " \t")) != NULL) {  /* Time first? */
00368                                 int             l = q - str;
00369 
00370                                 strncpy(timestr, str, l);
00371                                 timestr[l] = '\0';
00372                                 strncpy(datestr, q + 1, sizeof datestr);
00373                                 datestr[sizeof datestr - 1] = '\0';
00374                                 parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec);
00375                                 parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year);
00376                         } else if ((q = strrchr(tmp, ' ')) != NULL) {   /* Time last */
00377                                 int             l = q - tmp;
00378 
00379                                 strncpy(timestr, q + 1, sizeof timestr);
00380                                 timestr[sizeof timestr - 1] = '\0';
00381                                 strncpy(datestr, tmp, l);
00382                                 datestr[l] = '\0';
00383                         } else  /* Bail out */
00384                                 return dt;
00385                         parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec);
00386                         parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year);
00387                 }
00388                 dt = mktime(T);
00389         }
00390         return dt;
00391 }
00392 
Generated on Wed Feb 29 22:50:05 2012 for CXXUtilities by  doxygen 1.6.3