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 #if defined(_WIN32) && !defined(__CYGWIN__)
34 
35 #include <ctype.h>
36 #include <stdlib.h>
37 #include <stddef.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <time.h>
41 #include "util.h"
42 
43 static const char *abb_weekdays[] = {
44   "Sun",
45   "Mon",
46   "Tue",
47   "Wed",
48   "Thu",
49   "Fri",
50   "Sat",
51   NULL
52 };
53 
54 static const char *full_weekdays[] = {
55   "Sunday",
56   "Monday",
57   "Tuesday",
58   "Wednesday",
59   "Thursday",
60   "Friday",
61   "Saturday",
62   NULL
63 };
64 
65 static const char *abb_month[] = {
66   "Jan",
67   "Feb",
68   "Mar",
69   "Apr",
70   "May",
71   "Jun",
72   "Jul",
73   "Aug",
74   "Sep",
75   "Oct",
76   "Nov",
77   "Dec",
78   NULL
79 };
80 
81 static const char *full_month[] = {
82   "January",
83   "February",
84   "Mars",
85   "April",
86   "May",
87   "June",
88   "July",
89   "August",
90   "September",
91   "October",
92   "November",
93   "December",
94   NULL,
95 };
96 
97 static const char *ampm[] = {
98   "am",
99   "pm",
100   NULL
101 };
102 
103 /*
104  * tm_year is relative this year
105  */
106 const int tm_year_base = 1900;
107 
108 /*
109  * Return TRUE iff `year' was a leap year.
110  * Needed for strptime.
111  */
112 static int
is_leap_year(int year)113 is_leap_year (int year)
114 {
115   return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0);
116 }
117 
118 /* Needed for strptime. */
119 static int
match_string(const char ** buf,const char ** strs)120 match_string (const char **buf, const char **strs)
121 {
122   int i = 0;
123 
124   for (i = 0; strs[i] != NULL; ++i) {
125     int len = strlen (strs[i]);
126 
127     if (strncasecmp (*buf, strs[i], len) == 0) {
128       *buf += len;
129       return i;
130     }
131   }
132   return -1;
133 }
134 
135 /* Needed for strptime. */
136 static int
first_day(int year)137 first_day (int year)
138 {
139   int ret = 4;
140 
141   for (; year > 1970; --year)
142     ret = (ret + 365 + is_leap_year (year) ? 1 : 0) % 7;
143   return ret;
144 }
145 
146 /*
147  * Set `timeptr' given `wnum' (week number [0, 53])
148  * Needed for strptime
149  */
150 
151 static void
set_week_number_sun(struct tm * timeptr,int wnum)152 set_week_number_sun (struct tm *timeptr, int wnum)
153 {
154   int fday = first_day (timeptr->tm_year + tm_year_base);
155 
156   timeptr->tm_yday = wnum * 7 + timeptr->tm_wday - fday;
157   if (timeptr->tm_yday < 0) {
158     timeptr->tm_wday = fday;
159     timeptr->tm_yday = 0;
160   }
161 }
162 
163 /*
164  * Set `timeptr' given `wnum' (week number [0, 53])
165  * Needed for strptime
166  */
167 
168 static void
set_week_number_mon(struct tm * timeptr,int wnum)169 set_week_number_mon (struct tm *timeptr, int wnum)
170 {
171   int fday = (first_day (timeptr->tm_year + tm_year_base) + 6) % 7;
172 
173   timeptr->tm_yday = wnum * 7 + (timeptr->tm_wday + 6) % 7 - fday;
174   if (timeptr->tm_yday < 0) {
175     timeptr->tm_wday = (fday + 1) % 7;
176     timeptr->tm_yday = 0;
177   }
178 }
179 
180 /*
181  * Set `timeptr' given `wnum' (week number [0, 53])
182  * Needed for strptime
183  */
184 static void
set_week_number_mon4(struct tm * timeptr,int wnum)185 set_week_number_mon4 (struct tm *timeptr, int wnum)
186 {
187   int fday = (first_day (timeptr->tm_year + tm_year_base) + 6) % 7;
188   int offset = 0;
189 
190   if (fday < 4)
191     offset += 7;
192 
193   timeptr->tm_yday = offset + (wnum - 1) * 7 + timeptr->tm_wday - fday;
194   if (timeptr->tm_yday < 0) {
195     timeptr->tm_wday = fday;
196     timeptr->tm_yday = 0;
197   }
198 }
199 
200 /* strptime: roken */
201 /* extern "C" */
202 char *
203 /* strptime (const char *buf, const char *format, struct tm *timeptr) */
strptime(const char * buf,const char * format,struct tm * timeptr)204 strptime (const char *buf, const char *format, struct tm *timeptr)
205 {
206   char c;
207 
208   for (; (c = *format) != '\0'; ++format) {
209     char *s;
210     int ret;
211 
212     if (isspace (c)) {
213       while (isspace (*buf))
214         ++buf;
215     } else if (c == '%' && format[1] != '\0') {
216       c = *++format;
217       if (c == 'E' || c == 'O')
218         c = *++format;
219       switch (c) {
220         case 'A' :
221           ret = match_string (&buf, full_weekdays);
222           if (ret < 0)
223             return NULL;
224           timeptr->tm_wday = ret;
225           break;
226         case 'a' :
227           ret = match_string (&buf, abb_weekdays);
228           if (ret < 0)
229             return NULL;
230           timeptr->tm_wday = ret;
231           break;
232         case 'B' :
233           ret = match_string (&buf, full_month);
234           if (ret < 0)
235             return NULL;
236           timeptr->tm_mon = ret;
237           break;
238         case 'b' :
239         case 'h' :
240           ret = match_string (&buf, abb_month);
241           if (ret < 0)
242             return NULL;
243           timeptr->tm_mon = ret;
244           break;
245         case 'C' :
246           ret = strtol (buf, &s, 10);
247           if (s == buf)
248             return NULL;
249           timeptr->tm_year = (ret * 100) - tm_year_base;
250           buf = s;
251           break;
252         case 'c' :
253           abort ();
254         case 'D' :    /* %m/%d/%y */
255           s = strptime (buf, "%m/%d/%y", timeptr);
256           if (s == NULL)
257             return NULL;
258           buf = s;
259           break;
260         case 'd' :
261         case 'e' :
262           ret = strtol (buf, &s, 10);
263           if (s == buf)
264             return NULL;
265           timeptr->tm_mday = ret;
266           buf = s;
267           break;
268         case 'H' :
269         case 'k' :
270           ret = strtol (buf, &s, 10);
271           if (s == buf)
272             return NULL;
273           timeptr->tm_hour = ret;
274           buf = s;
275           break;
276         case 'I' :
277         case 'l' :
278           ret = strtol (buf, &s, 10);
279           if (s == buf)
280             return NULL;
281           if (ret == 12)
282             timeptr->tm_hour = 0;
283           else
284             timeptr->tm_hour = ret;
285           buf = s;
286           break;
287         case 'j' :
288           ret = strtol (buf, &s, 10);
289           if (s == buf)
290             return NULL;
291           timeptr->tm_yday = ret - 1;
292           buf = s;
293           break;
294         case 'm' :
295           ret = strtol (buf, &s, 10);
296           if (s == buf)
297             return NULL;
298           timeptr->tm_mon = ret - 1;
299           buf = s;
300           break;
301         case 'M' :
302           ret = strtol (buf, &s, 10);
303           if (s == buf)
304             return NULL;
305           timeptr->tm_min = ret;
306           buf = s;
307           break;
308         case 'n' :
309           if (*buf == '\n')
310             ++buf;
311           else
312             return NULL;
313           break;
314         case 'p' :
315           ret = match_string (&buf, ampm);
316           if (ret < 0)
317             return NULL;
318           if (timeptr->tm_hour == 0) {
319             if (ret == 1)
320               timeptr->tm_hour = 12;
321           } else
322             timeptr->tm_hour += 12;
323           break;
324         case 'r' :    /* %I:%M:%S %p */
325           s = strptime (buf, "%I:%M:%S %p", timeptr);
326           if (s == NULL)
327             return NULL;
328           buf = s;
329           break;
330         case 'R' :    /* %H:%M */
331           s = strptime (buf, "%H:%M", timeptr);
332           if (s == NULL)
333             return NULL;
334           buf = s;
335           break;
336         case 'S' :
337           ret = strtol (buf, &s, 10);
338           if (s == buf)
339             return NULL;
340           timeptr->tm_sec = ret;
341           buf = s;
342           break;
343         case 't' :
344           if (*buf == '\t')
345             ++buf;
346           else
347             return NULL;
348           break;
349         case 'T' :    /* %H:%M:%S */
350         case 'X' :
351           s = strptime (buf, "%H:%M:%S", timeptr);
352           if (s == NULL)
353             return NULL;
354           buf = s;
355           break;
356         case 'u' :
357           ret = strtol (buf, &s, 10);
358           if (s == buf)
359             return NULL;
360           timeptr->tm_wday = ret - 1;
361           buf = s;
362           break;
363         case 'w' :
364           ret = strtol (buf, &s, 10);
365           if (s == buf)
366             return NULL;
367           timeptr->tm_wday = ret;
368           buf = s;
369           break;
370         case 'U' :
371           ret = strtol (buf, &s, 10);
372           if (s == buf)
373             return NULL;
374           set_week_number_sun (timeptr, ret);
375           buf = s;
376           break;
377         case 'V' :
378           ret = strtol (buf, &s, 10);
379           if (s == buf)
380             return NULL;
381           set_week_number_mon4 (timeptr, ret);
382           buf = s;
383           break;
384         case 'W' :
385           ret = strtol (buf, &s, 10);
386           if (s == buf)
387             return NULL;
388           set_week_number_mon (timeptr, ret);
389           buf = s;
390           break;
391         case 'x' :
392           s = strptime (buf, "%Y:%m:%d", timeptr);
393           if (s == NULL)
394             return NULL;
395           buf = s;
396           break;
397         case 'y' :
398           ret = strtol (buf, &s, 10);
399           if (s == buf)
400             return NULL;
401           if (ret < 70)
402             timeptr->tm_year = 100 + ret;
403           else
404             timeptr->tm_year = ret;
405           buf = s;
406           break;
407         case 'Y' :
408           ret = strtol (buf, &s, 10);
409           if (s == buf)
410             return NULL;
411           timeptr->tm_year = ret - tm_year_base;
412           buf = s;
413           break;
414         case 'Z' :
415           abort ();
416         case '\0' :
417           --format;
418           /* FALLTHROUGH */
419         case '%' :
420           if (*buf == '%')
421             ++buf;
422           else
423             return NULL;
424           break;
425         default :
426           if (*buf == '%' || *++buf == c)
427             ++buf;
428           else
429             return NULL;
430           break;
431       }
432     } else {
433       if (*buf == c)
434         ++buf;
435       else
436         return NULL;
437     }
438   }
439   return (char *)buf;
440 }
441 
442 #endif
443