1 /* Convert a string representation of time to a time value.
2    Copyright (C) 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Mark Kettenis <kettenis@phys.uva.nl>, 1997.
5 
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the 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    Lesser General Public License for more details.
15 
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20 
21 /*
22 FUNCTION
23 <<getdate>>,<<getdate_r>>---convert a string representation of time to a time value
24 
25 INDEX
26         getdate
27 INDEX
28         getdate_r
29 
30 ANSI_SYNOPSIS
31         #include <time.h>
32         struct tm *getdate(const char *<[string]>);
33         int getdate_r(const char *<[string]>, struct tm *<[res]>);
34 
35 TRAD_SYNOPSIS
36         #include <time.h>
37         struct tm *getdate(<[string]>);
38         const char *<[string]>;
39 
40         int getdate_r(<[string]>, <[res]>);
41         const char *<[string]>;
42         struct tm *<[res]>;
43 
44 DESCRIPTION
45 <<getdate>> reads a file which is specified by the environment variable:
46 DATEMSK.  This file contains a number of formats valid for input to the
47 <<strptime>> function.  The input <[string]> is used as input to the format
48 strings and the first valid match that occurs is used.  The resultant
49 time struct is returned.  If an error occurs, the value <<getdate_err>> is
50 set to one of the following values.
51 
52      1  the DATEMSK environment variable is null or undefined,
53      2  the template file cannot be opened for reading,
54      3  failed to get file status information,
55      4  the template file is not a regular file,
56      5  an error is encountered while reading the template file,
57      6  memory allication failed (not enough memory available),
58      7  there is no line in the template that matches the input,
59      8  invalid input specification
60 
61 The <<getdate_r>> routine is similar, except that it returns the error
62 code and has the <[res]> time struct pointer passed in.  <<getdate>> is
63 non-reentrant.  Applications that wish to be reentrant should use
64 <<getdate_r>> instead of <<getdate>>.
65 
66 RETURNS
67 <<getdate>> returns a pointer to the traditional time representation
68 (<<struct tm>>).  <<getdate_r>> returns 0 if successful, otherwise it
69 returns the error code.
70 
71 PORTABILITY
72 <<getdate>> is defined by the Single Unix specification.
73 <<getdate_r>> is a reentrant extension.
74 
75 <<getdate>> and <<getdate_r>> optionally require <<stat>> and <<access>>.
76 */
77 
78 
79 /* Modified for newlib by Jeff Johnston, June 19/2002 */
80 
81 #include <limits.h>
82 #include <stdio.h>
83 #include <stdlib.h>
84 #include <string.h>
85 #include <time.h>
86 #include <unistd.h>
87 #include <sys/stat.h>
88 
89 #if !defined(_ELIX_LEVEL) || _ELIX_LEVEL >= 2
90 # define STAT stat64
91 #else
92 # define STAT stat
93 #endif
94 
95 #define TM_YEAR_BASE 1900
96 
97 extern ssize_t __getline (char **, size_t *, FILE *);
98 
99 /* Prototypes for local functions.  */
100 static int first_wday (int year, int mon, int wday);
101 static int check_mday (int year, int mon, int mday);
102 
103 #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
104 
105 /* Error code is set to one of the following values to indicate an error.
106      1  the DATEMSK environment variable is null or undefined,
107      2  the template file cannot be opened for reading,
108      3  failed to get file status information,
109      4  the template file is not a regular file,
110      5  an error is encountered while reading the template file,
111      6  memory allication failed (not enough memory available),
112      7  there is no line in the template that matches the input,
113      8  invalid input specification Example: February 31 or a time is
114         specified that can not be represented in a time_t (representing
115 	the time in seconds since 00:00:00 UTC, January 1, 1970) */
116 
117 /* Returns the first weekday WDAY of month MON in the year YEAR.  */
118 static int
first_wday(int year,int mon,int wday)119 first_wday (int year, int mon, int wday)
120 {
121   struct tm tm;
122 
123   if (wday == INT_MIN)
124     return 1;
125 
126   memset (&tm, 0, sizeof (struct tm));
127   tm.tm_year = year;
128   tm.tm_mon = mon;
129   tm.tm_mday = 1;
130   mktime (&tm);
131 
132   return (1 + (wday - tm.tm_wday + 7) % 7);
133 }
134 
135 
136 /* Returns 1 if MDAY is a valid day of the month in month MON of year
137    YEAR, and 0 if it is not.  */
138 static int
check_mday(int year,int mon,int mday)139 check_mday (int year, int mon, int mday)
140 {
141   switch (mon)
142     {
143     case 0:
144     case 2:
145     case 4:
146     case 6:
147     case 7:
148     case 9:
149     case 11:
150       if (mday >= 1 && mday <= 31)
151 	return 1;
152       break;
153     case 3:
154     case 5:
155     case 8:
156     case 10:
157       if (mday >= 1 && mday <= 30)
158 	return 1;
159       break;
160     case 1:
161       if (mday >= 1 && mday <= (isleap (year) ? 29 : 28))
162 	return 1;
163       break;
164     }
165 
166   return 0;
167 }
168 
169 
170 int
getdate_r(const char * string,struct tm * tp)171 getdate_r (const char *string, struct tm *tp)
172 {
173   FILE *fp;
174   char *line;
175   size_t len;
176   char *datemsk;
177   char *result = NULL;
178   time_t timer;
179   struct tm tm;
180   struct STAT st;
181   int mday_ok = 0;
182 
183   datemsk = getenv ("DATEMSK");
184   if (datemsk == NULL || *datemsk == '\0')
185     return 1;
186 
187   if (STAT (datemsk, &st) < 0)
188     return 3;
189 
190   if (!S_ISREG (st.st_mode))
191     return 4;
192 
193   if (access (datemsk, R_OK) < 0)
194     return 2;
195 
196   /* Open the template file.  */
197   fp = fopen (datemsk, "r");
198   if (fp == NULL)
199     return 2;
200 
201   line = NULL;
202   len = 0;
203   do
204     {
205       ssize_t n;
206 
207       n = __getline (&line, &len, fp);
208       if (n < 0)
209 	break;
210       if (line[n - 1] == '\n')
211 	line[n - 1] = '\0';
212 
213       /* Do the conversion.  */
214       tp->tm_year = tp->tm_mon = tp->tm_mday = tp->tm_wday = INT_MIN;
215       tp->tm_hour = tp->tm_sec = tp->tm_min = INT_MIN;
216       tp->tm_isdst = -1;
217       result = strptime (string, line, tp);
218       if (result && *result == '\0')
219 	break;
220     }
221   while (!__sfeof (fp));
222 
223   /* Free the buffer.  */
224   free (line);
225 
226   /* Check for errors. */
227   if (__sferror (fp))
228     {
229       fclose (fp);
230       return 5;
231     }
232 
233   /* Close template file.  */
234   fclose (fp);
235 
236   if (result == NULL || *result != '\0')
237     return 7;
238 
239   /* Get current time.  */
240   time (&timer);
241   localtime_r (&timer, &tm);
242 
243   /* If only the weekday is given, today is assumed if the given day
244      is equal to the current day and next week if it is less.  */
245   if (tp->tm_wday >= 0 && tp->tm_wday <= 6 && tp->tm_year == INT_MIN
246       && tp->tm_mon == INT_MIN && tp->tm_mday == INT_MIN)
247     {
248       tp->tm_year = tm.tm_year;
249       tp->tm_mon = tm.tm_mon;
250       tp->tm_mday = tm.tm_mday + (tp->tm_wday - tm.tm_wday + 7) % 7;
251       mday_ok = 1;
252     }
253 
254   /* If only the month is given, the current month is assumed if the
255      given month is equal to the current month and next year if it is
256      less and no year is given (the first day of month is assumed if
257      no day is given.  */
258   if (tp->tm_mon >= 0 && tp->tm_mon <= 11 && tp->tm_mday == INT_MIN)
259     {
260       if (tp->tm_year == INT_MIN)
261 	tp->tm_year = tm.tm_year + (((tp->tm_mon - tm.tm_mon) < 0) ? 1 : 0);
262       tp->tm_mday = first_wday (tp->tm_year, tp->tm_mon, tp->tm_wday);
263       mday_ok = 1;
264     }
265 
266   /* If no hour, minute and second are given the current hour, minute
267      and second are assumed.  */
268   if (tp->tm_hour == INT_MIN && tp->tm_min == INT_MIN && tp->tm_sec == INT_MIN)
269     {
270       tp->tm_hour = tm.tm_hour;
271       tp->tm_min = tm.tm_min;
272       tp->tm_sec = tm.tm_sec;
273     }
274 
275   /* If no date is given, today is assumed if the given hour is
276      greater than the current hour and tomorrow is assumed if
277      it is less.  */
278   if (tp->tm_hour >= 0 && tp->tm_hour <= 23
279       && tp->tm_year == INT_MIN && tp->tm_mon == INT_MIN
280       && tp->tm_mday == INT_MIN && tp->tm_wday == INT_MIN)
281     {
282       tp->tm_year = tm.tm_year;
283       tp->tm_mon = tm.tm_mon;
284       tp->tm_mday = tm.tm_mday + ((tp->tm_hour - tm.tm_hour) < 0 ? 1 : 0);
285       mday_ok = 1;
286     }
287 
288   /* Fill in the gaps.  */
289   if (tp->tm_year == INT_MIN)
290     tp->tm_year = tm.tm_year;
291   if (tp->tm_hour == INT_MIN)
292     tp->tm_hour = 0;
293   if (tp->tm_min == INT_MIN)
294     tp->tm_min = 0;
295   if (tp->tm_sec == INT_MIN)
296     tp->tm_sec = 0;
297 
298   /* Check if the day of month is within range, and if the time can be
299      represented in a time_t.  We make use of the fact that the mktime
300      call normalizes the struct tm.  */
301   if ((!mday_ok && !check_mday (TM_YEAR_BASE + tp->tm_year, tp->tm_mon,
302 				tp->tm_mday))
303       || mktime (tp) == (time_t) -1)
304     return 8;
305 
306   return 0;
307 }
308 
309 #ifndef _REENT_ONLY
310 struct tm *
getdate(const char * string)311 getdate (const char *string)
312 {
313   /* Buffer returned by getdate.  */
314   static struct tm tmbuf;
315   int errval = getdate_r (string, &tmbuf);
316 
317   if (errval != 0)
318     {
319       getdate_err = errval;
320       return NULL;
321     }
322 
323   return &tmbuf;
324 }
325 #endif /* _REENT_ONLY */
326