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