Back to index

glibc  2.9
getdate.c
Go to the documentation of this file.
00001 /* Convert a string representation of time to a time value.
00002    Copyright (C) 1997,1998,1999,2000,2001,2003 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Contributed by Mark Kettenis <kettenis@phys.uva.nl>, 1997.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 #include <limits.h>
00022 #include <stdio.h>
00023 #include <stdio_ext.h>
00024 #include <stdlib.h>
00025 #include <string.h>
00026 #include <time.h>
00027 #include <unistd.h>
00028 #include <sys/stat.h>
00029 
00030 #define TM_YEAR_BASE 1900
00031 
00032 
00033 /* Prototypes for local functions.  */
00034 static int first_wday (int year, int mon, int wday);
00035 static int check_mday (int year, int mon, int mday);
00036 
00037 
00038 /* Set to one of the following values to indicate an error.
00039      1  the DATEMSK environment variable is null or undefined,
00040      2  the template file cannot be opened for reading,
00041      3  failed to get file status information,
00042      4  the template file is not a regular file,
00043      5  an error is encountered while reading the template file,
00044      6  memory allication failed (not enough memory available),
00045      7  there is no line in the template that matches the input,
00046      8  invalid input specification Example: February 31 or a time is
00047         specified that can not be represented in a time_t (representing
00048        the time in seconds since 00:00:00 UTC, January 1, 1970) */
00049 int getdate_err;
00050 
00051 
00052 /* Returns the first weekday WDAY of month MON in the year YEAR.  */
00053 static int
00054 first_wday (int year, int mon, int wday)
00055 {
00056   struct tm tm;
00057 
00058   if (wday == INT_MIN)
00059     return 1;
00060 
00061   memset (&tm, 0, sizeof (struct tm));
00062   tm.tm_year = year;
00063   tm.tm_mon = mon;
00064   tm.tm_mday = 1;
00065   mktime (&tm);
00066 
00067   return (1 + (wday - tm.tm_wday + 7) % 7);
00068 }
00069 
00070 
00071 /* Returns 1 if MDAY is a valid day of the month in month MON of year
00072    YEAR, and 0 if it is not.  */
00073 static int
00074 check_mday (int year, int mon, int mday)
00075 {
00076   switch (mon)
00077     {
00078     case 0:
00079     case 2:
00080     case 4:
00081     case 6:
00082     case 7:
00083     case 9:
00084     case 11:
00085       if (mday >= 1 && mday <= 31)
00086        return 1;
00087       break;
00088     case 3:
00089     case 5:
00090     case 8:
00091     case 10:
00092       if (mday >= 1 && mday <= 30)
00093        return 1;
00094       break;
00095     case 1:
00096       if (mday >= 1 && mday <= (__isleap (year) ? 29 : 28))
00097        return 1;
00098       break;
00099     }
00100 
00101   return 0;
00102 }
00103 
00104 
00105 int
00106 __getdate_r (const char *string, struct tm *tp)
00107 {
00108   FILE *fp;
00109   char *line;
00110   size_t len;
00111   char *datemsk;
00112   char *result = NULL;
00113   time_t timer;
00114   struct tm tm;
00115   struct stat64 st;
00116   int mday_ok = 0;
00117 
00118   datemsk = getenv ("DATEMSK");
00119   if (datemsk == NULL || *datemsk == '\0')
00120     return 1;
00121 
00122   if (stat64 (datemsk, &st) < 0)
00123     return 3;
00124 
00125   if (!S_ISREG (st.st_mode))
00126     return 4;
00127 
00128   if (__access (datemsk, R_OK) < 0)
00129     return 2;
00130 
00131   /* Open the template file.  */
00132   fp = fopen (datemsk, "rc");
00133   if (fp == NULL)
00134     return 2;
00135 
00136   /* No threads reading this stream.  */
00137   __fsetlocking (fp, FSETLOCKING_BYCALLER);
00138 
00139   line = NULL;
00140   len = 0;
00141   do
00142     {
00143       ssize_t n;
00144 
00145       n = __getline (&line, &len, fp);
00146       if (n < 0)
00147        break;
00148       if (line[n - 1] == '\n')
00149        line[n - 1] = '\0';
00150 
00151       /* Do the conversion.  */
00152       tp->tm_year = tp->tm_mon = tp->tm_mday = tp->tm_wday = INT_MIN;
00153       tp->tm_hour = tp->tm_sec = tp->tm_min = INT_MIN;
00154       tp->tm_isdst = -1;
00155       tp->tm_gmtoff = 0;
00156       tp->tm_zone = NULL;
00157       result = strptime (string, line, tp);
00158       if (result && *result == '\0')
00159        break;
00160     }
00161   while (!feof_unlocked (fp));
00162 
00163   /* Free the buffer.  */
00164   free (line);
00165 
00166   /* Check for errors. */
00167   if (ferror_unlocked (fp))
00168     {
00169       fclose (fp);
00170       return 5;
00171     }
00172 
00173   /* Close template file.  */
00174   fclose (fp);
00175 
00176   if (result == NULL || *result != '\0')
00177     return 7;
00178 
00179   /* Get current time.  */
00180   time (&timer);
00181   __localtime_r (&timer, &tm);
00182 
00183   /* If only the weekday is given, today is assumed if the given day
00184      is equal to the current day and next week if it is less.  */
00185   if (tp->tm_wday >= 0 && tp->tm_wday <= 6 && tp->tm_year == INT_MIN
00186       && tp->tm_mon == INT_MIN && tp->tm_mday == INT_MIN)
00187     {
00188       tp->tm_year = tm.tm_year;
00189       tp->tm_mon = tm.tm_mon;
00190       tp->tm_mday = tm.tm_mday + (tp->tm_wday - tm.tm_wday + 7) % 7;
00191       mday_ok = 1;
00192     }
00193 
00194   /* If only the month is given, the current month is assumed if the
00195      given month is equal to the current month and next year if it is
00196      less and no year is given (the first day of month is assumed if
00197      no day is given.  */
00198   if (tp->tm_mon >= 0 && tp->tm_mon <= 11 && tp->tm_mday == INT_MIN)
00199     {
00200       if (tp->tm_year == INT_MIN)
00201        tp->tm_year = tm.tm_year + (((tp->tm_mon - tm.tm_mon) < 0) ? 1 : 0);
00202       tp->tm_mday = first_wday (tp->tm_year, tp->tm_mon, tp->tm_wday);
00203       mday_ok = 1;
00204     }
00205 
00206   /* If no hour, minute and second are given the current hour, minute
00207      and second are assumed.  */
00208   if (tp->tm_hour == INT_MIN && tp->tm_min == INT_MIN && tp->tm_sec == INT_MIN)
00209     {
00210       tp->tm_hour = tm.tm_hour;
00211       tp->tm_min = tm.tm_min;
00212       tp->tm_sec = tm.tm_sec;
00213     }
00214 
00215   /* Fill in the gaps.  */
00216   if (tp->tm_hour == INT_MIN)
00217     tp->tm_hour = 0;
00218   if (tp->tm_min == INT_MIN)
00219     tp->tm_min = 0;
00220   if (tp->tm_sec == INT_MIN)
00221     tp->tm_sec = 0;
00222 
00223   /* If no date is given, today is assumed if the given hour is
00224      greater than the current hour and tomorrow is assumed if
00225      it is less.  */
00226   if (tp->tm_hour >= 0 && tp->tm_hour <= 23
00227       && tp->tm_mon == INT_MIN
00228       && tp->tm_mday == INT_MIN && tp->tm_wday == INT_MIN)
00229     {
00230       tp->tm_mon = tm.tm_mon;
00231       tp->tm_mday = tm.tm_mday + ((tp->tm_hour - tm.tm_hour) < 0 ? 1 : 0);
00232       mday_ok = 1;
00233     }
00234 
00235   /* More fillers.  */
00236   if (tp->tm_year == INT_MIN)
00237     tp->tm_year = tm.tm_year;
00238   if (tp->tm_mon == INT_MIN)
00239     tp->tm_mon = tm.tm_mon;
00240 
00241   /* Check if the day of month is within range, and if the time can be
00242      represented in a time_t.  We make use of the fact that the mktime
00243      call normalizes the struct tm.  */
00244   if ((!mday_ok && !check_mday (TM_YEAR_BASE + tp->tm_year, tp->tm_mon,
00245                             tp->tm_mday))
00246       || mktime (tp) == (time_t) -1)
00247     return 8;
00248 
00249   return 0;
00250 }
00251 #ifdef weak_alias
00252 weak_alias (__getdate_r, getdate_r)
00253 #endif
00254 
00255 
00256 struct tm *
00257 getdate (const char *string)
00258 {
00259   /* Buffer returned by getdate.  */
00260   static struct tm tmbuf;
00261   int errval = __getdate_r (string, &tmbuf);
00262 
00263   if (errval != 0)
00264     {
00265       getdate_err = errval;
00266       return NULL;
00267     }
00268 
00269   return &tmbuf;
00270 }