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