1 /*
2  * Copyright (c) 1999 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of KTH nor the names of its contributors may be
18  *    used to endorse or promote products derived from this software without
19  *    specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
22  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
32 
33 #include <stddef.h>
34 #include <stdio.h>
35 #include <time.h>
36 
37 static const char *abb_weekdays[] = {
38     "Sun",
39     "Mon",
40     "Tue",
41     "Wed",
42     "Thu",
43     "Fri",
44     "Sat",
45     NULL
46 };
47 
48 static const char *full_weekdays[] = {
49     "Sunday",
50     "Monday",
51     "Tuesday",
52     "Wednesday",
53     "Thursday",
54     "Friday",
55     "Saturday",
56     NULL
57 };
58 
59 static const char *abb_month[] = {
60     "Jan",
61     "Feb",
62     "Mar",
63     "Apr",
64     "May",
65     "Jun",
66     "Jul",
67     "Aug",
68     "Sep",
69     "Oct",
70     "Nov",
71     "Dec",
72     NULL
73 };
74 
75 static const char *full_month[] = {
76     "January",
77     "February",
78     "Mars",
79     "April",
80     "May",
81     "June",
82     "July",
83     "August",
84     "September",
85     "October",
86     "November",
87     "December",
88     NULL,
89 };
90 
91 static const char *ampm[] = {
92     "am",
93     "pm",
94     NULL
95 };
96 
97 /*
98  * tm_year is relative this year
99  */
100 const int tm_year_base = 1900;
101 
102 /*
103  * Return TRUE iff `year' was a leap year.
104  * Needed for strptime.
105  */
106 static int
is_leap_year(int year)107 is_leap_year (int year)
108 {
109     return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0);
110 }
111 
112 /* Needed for strptime. */
113 static int
match_string(const char ** buf,const char ** strs)114 match_string (const char **buf, const char **strs)
115 {
116     int i = 0;
117 
118     for (i = 0; strs[i] != NULL; ++i) {
119 	int len = strlen (strs[i]);
120 
121 	if (strncasecmp (*buf, strs[i], len) == 0) {
122 	    *buf += len;
123 	    return i;
124 	}
125     }
126     return -1;
127 }
128 
129 /* Needed for strptime. */
130 static int
first_day(int year)131 first_day (int year)
132 {
133     int ret = 4;
134 
135     for (; year > 1970; --year)
136 	ret = (ret + 365 + is_leap_year (year) ? 1 : 0) % 7;
137     return ret;
138 }
139 
140 /*
141  * Set `timeptr' given `wnum' (week number [0, 53])
142  * Needed for strptime
143  */
144 
145 static void
set_week_number_sun(struct tm * timeptr,int wnum)146 set_week_number_sun (struct tm *timeptr, int wnum)
147 {
148     int fday = first_day (timeptr->tm_year + tm_year_base);
149 
150     timeptr->tm_yday = wnum * 7 + timeptr->tm_wday - fday;
151     if (timeptr->tm_yday < 0) {
152 	timeptr->tm_wday = fday;
153 	timeptr->tm_yday = 0;
154     }
155 }
156 
157 /*
158  * Set `timeptr' given `wnum' (week number [0, 53])
159  * Needed for strptime
160  */
161 
162 static void
set_week_number_mon(struct tm * timeptr,int wnum)163 set_week_number_mon (struct tm *timeptr, int wnum)
164 {
165     int fday = (first_day (timeptr->tm_year + tm_year_base) + 6) % 7;
166 
167     timeptr->tm_yday = wnum * 7 + (timeptr->tm_wday + 6) % 7 - fday;
168     if (timeptr->tm_yday < 0) {
169 	timeptr->tm_wday = (fday + 1) % 7;
170 	timeptr->tm_yday = 0;
171     }
172 }
173 
174 /*
175  * Set `timeptr' given `wnum' (week number [0, 53])
176  * Needed for strptime
177  */
178 static void
set_week_number_mon4(struct tm * timeptr,int wnum)179 set_week_number_mon4 (struct tm *timeptr, int wnum)
180 {
181     int fday = (first_day (timeptr->tm_year + tm_year_base) + 6) % 7;
182     int offset = 0;
183 
184     if (fday < 4)
185 	offset += 7;
186 
187     timeptr->tm_yday = offset + (wnum - 1) * 7 + timeptr->tm_wday - fday;
188     if (timeptr->tm_yday < 0) {
189 	timeptr->tm_wday = fday;
190 	timeptr->tm_yday = 0;
191     }
192 }
193 
194 /* strptime: roken */
195 //extern "C"
196 char *
197 //strptime (const char *buf, const char *format, struct tm *timeptr)
198 _DEFUN (strptime, (buf, format, timeptr),
199 	_CONST char *buf _AND
200 	_CONST char *format _AND
201 	struct tm *timeptr)
202 {
203     char c;
204 
205     for (; (c = *format) != '\0'; ++format) {
206 	char *s;
207 	int ret;
208 
209 	if (isspace (c)) {
210 	    while (isspace (*buf))
211 		++buf;
212 	} else if (c == '%' && format[1] != '\0') {
213 	    c = *++format;
214 	    if (c == 'E' || c == 'O')
215 		c = *++format;
216 	    switch (c) {
217 	    case 'A' :
218 		ret = match_string (&buf, full_weekdays);
219 		if (ret < 0)
220 		    return NULL;
221 		timeptr->tm_wday = ret;
222 		break;
223 	    case 'a' :
224 		ret = match_string (&buf, abb_weekdays);
225 		if (ret < 0)
226 		    return NULL;
227 		timeptr->tm_wday = ret;
228 		break;
229 	    case 'B' :
230 		ret = match_string (&buf, full_month);
231 		if (ret < 0)
232 		    return NULL;
233 		timeptr->tm_mon = ret;
234 		break;
235 	    case 'b' :
236 	    case 'h' :
237 		ret = match_string (&buf, abb_month);
238 		if (ret < 0)
239 		    return NULL;
240 		timeptr->tm_mon = ret;
241 		break;
242 	    case 'C' :
243 		ret = strtol (buf, &s, 10);
244 		if (s == buf)
245 		    return NULL;
246 		timeptr->tm_year = (ret * 100) - tm_year_base;
247 		buf = s;
248 		break;
249 	    case 'c' :
250 		abort ();
251 	    case 'D' :		/* %m/%d/%y */
252 		s = strptime (buf, "%m/%d/%y", timeptr);
253 		if (s == NULL)
254 		    return NULL;
255 		buf = s;
256 		break;
257 	    case 'd' :
258 	    case 'e' :
259 		ret = strtol (buf, &s, 10);
260 		if (s == buf)
261 		    return NULL;
262 		timeptr->tm_mday = ret;
263 		buf = s;
264 		break;
265 	    case 'H' :
266 	    case 'k' :
267 		ret = strtol (buf, &s, 10);
268 		if (s == buf)
269 		    return NULL;
270 		timeptr->tm_hour = ret;
271 		buf = s;
272 		break;
273 	    case 'I' :
274 	    case 'l' :
275 		ret = strtol (buf, &s, 10);
276 		if (s == buf)
277 		    return NULL;
278 		if (ret == 12)
279 		    timeptr->tm_hour = 0;
280 		else
281 		    timeptr->tm_hour = ret;
282 		buf = s;
283 		break;
284 	    case 'j' :
285 		ret = strtol (buf, &s, 10);
286 		if (s == buf)
287 		    return NULL;
288 		timeptr->tm_yday = ret - 1;
289 		buf = s;
290 		break;
291 	    case 'm' :
292 		ret = strtol (buf, &s, 10);
293 		if (s == buf)
294 		    return NULL;
295 		timeptr->tm_mon = ret - 1;
296 		buf = s;
297 		break;
298 	    case 'M' :
299 		ret = strtol (buf, &s, 10);
300 		if (s == buf)
301 		    return NULL;
302 		timeptr->tm_min = ret;
303 		buf = s;
304 		break;
305 	    case 'n' :
306 		if (*buf == '\n')
307 		    ++buf;
308 		else
309 		    return NULL;
310 		break;
311 	    case 'p' :
312 		ret = match_string (&buf, ampm);
313 		if (ret < 0)
314 		    return NULL;
315 		if (timeptr->tm_hour == 0) {
316 		    if (ret == 1)
317 			timeptr->tm_hour = 12;
318 		} else
319 		    timeptr->tm_hour += 12;
320 		break;
321 	    case 'r' :		/* %I:%M:%S %p */
322 		s = strptime (buf, "%I:%M:%S %p", timeptr);
323 		if (s == NULL)
324 		    return NULL;
325 		buf = s;
326 		break;
327 	    case 'R' :		/* %H:%M */
328 		s = strptime (buf, "%H:%M", timeptr);
329 		if (s == NULL)
330 		    return NULL;
331 		buf = s;
332 		break;
333 	    case 'S' :
334 		ret = strtol (buf, &s, 10);
335 		if (s == buf)
336 		    return NULL;
337 		timeptr->tm_sec = ret;
338 		buf = s;
339 		break;
340 	    case 't' :
341 		if (*buf == '\t')
342 		    ++buf;
343 		else
344 		    return NULL;
345 		break;
346 	    case 'T' :		/* %H:%M:%S */
347 	    case 'X' :
348 		s = strptime (buf, "%H:%M:%S", timeptr);
349 		if (s == NULL)
350 		    return NULL;
351 		buf = s;
352 		break;
353 	    case 'u' :
354 		ret = strtol (buf, &s, 10);
355 		if (s == buf)
356 		    return NULL;
357 		timeptr->tm_wday = ret - 1;
358 		buf = s;
359 		break;
360 	    case 'w' :
361 		ret = strtol (buf, &s, 10);
362 		if (s == buf)
363 		    return NULL;
364 		timeptr->tm_wday = ret;
365 		buf = s;
366 		break;
367 	    case 'U' :
368 		ret = strtol (buf, &s, 10);
369 		if (s == buf)
370 		    return NULL;
371 		set_week_number_sun (timeptr, ret);
372 		buf = s;
373 		break;
374 	    case 'V' :
375 		ret = strtol (buf, &s, 10);
376 		if (s == buf)
377 		    return NULL;
378 		set_week_number_mon4 (timeptr, ret);
379 		buf = s;
380 		break;
381 	    case 'W' :
382 		ret = strtol (buf, &s, 10);
383 		if (s == buf)
384 		    return NULL;
385 		set_week_number_mon (timeptr, ret);
386 		buf = s;
387 		break;
388 	    case 'x' :
389 		s = strptime (buf, "%Y:%m:%d", timeptr);
390 		if (s == NULL)
391 		    return NULL;
392 		buf = s;
393 		break;
394 	    case 'y' :
395 		ret = strtol (buf, &s, 10);
396 		if (s == buf)
397 		    return NULL;
398 		if (ret < 70)
399 		    timeptr->tm_year = 100 + ret;
400 		else
401 		    timeptr->tm_year = ret;
402 		buf = s;
403 		break;
404 	    case 'Y' :
405 		ret = strtol (buf, &s, 10);
406 		if (s == buf)
407 		    return NULL;
408 		timeptr->tm_year = ret - tm_year_base;
409 		buf = s;
410 		break;
411 	    case 'Z' :
412 		abort ();
413 	    case '\0' :
414 		--format;
415 		/* FALLTHROUGH */
416 	    case '%' :
417 		if (*buf == '%')
418 		    ++buf;
419 		else
420 		    return NULL;
421 		break;
422 	    default :
423 		if (*buf == '%' || *++buf == c)
424 		    ++buf;
425 		else
426 		    return NULL;
427 		break;
428 	    }
429 	} else {
430 	    if (*buf == c)
431 		++buf;
432 	    else
433 		return NULL;
434 	}
435     }
436     return (char *)buf;
437 }
438 
439