test debug
[heimdal.git] / lib / roken / strptime.c
blob9e250dbdbd88fd14a853b3686f33a297f7cd36d0
1 /*
2 * Copyright (c) 1999, 2003, 2005 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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. */
33 #ifdef HAVE_CONFIG_H
34 #include <config.h>
35 #endif
36 #ifdef TEST_STRPFTIME
37 #include "strpftime-test.h"
38 #endif
39 #include <ctype.h>
40 #include "roken.h"
42 RCSID("$Id$");
44 static const char *abb_weekdays[] = {
45 "Sun",
46 "Mon",
47 "Tue",
48 "Wed",
49 "Thu",
50 "Fri",
51 "Sat",
52 NULL
55 static const char *full_weekdays[] = {
56 "Sunday",
57 "Monday",
58 "Tuesday",
59 "Wednesday",
60 "Thursday",
61 "Friday",
62 "Saturday",
63 NULL
66 static const char *abb_month[] = {
67 "Jan",
68 "Feb",
69 "Mar",
70 "Apr",
71 "May",
72 "Jun",
73 "Jul",
74 "Aug",
75 "Sep",
76 "Oct",
77 "Nov",
78 "Dec",
79 NULL
82 static const char *full_month[] = {
83 "January",
84 "February",
85 "March",
86 "April",
87 "May",
88 "June",
89 "July",
90 "August",
91 "September",
92 "October",
93 "November",
94 "December",
95 NULL,
98 static const char *ampm[] = {
99 "am",
100 "pm",
101 NULL
105 * Try to match `*buf' to one of the strings in `strs'. Return the
106 * index of the matching string (or -1 if none). Also advance buf.
109 static int
110 match_string (const char **buf, const char **strs)
112 int i = 0;
114 for (i = 0; strs[i] != NULL; ++i) {
115 int len = strlen (strs[i]);
117 if (strncasecmp (*buf, strs[i], len) == 0) {
118 *buf += len;
119 return i;
122 return -1;
126 * Try to match `*buf' to at the most `n' characters and return the
127 * resulting number in `num'. Returns 0 or an error. Also advance
128 * buf.
131 static int
132 parse_number (const char **buf, int n, int *num)
134 char *s, *str;
135 int i;
137 str = malloc(n + 1);
138 if (str == NULL)
139 return -1;
141 /* skip whitespace */
142 for (; **buf != '\0' && isspace((unsigned char)(**buf)); (*buf)++)
145 /* parse at least n characters */
146 for (i = 0; **buf != '\0' && i < n && isdigit((unsigned char)(**buf)); i++, (*buf)++)
147 str[i] = **buf;
148 str[i] = '\0';
150 *num = strtol (str, &s, 10);
151 free(str);
152 if (s == str)
153 return -1;
155 return 0;
159 * tm_year is relative this year
162 const int tm_year_base = 1900;
165 * Return TRUE iff `year' was a leap year.
168 static int
169 is_leap_year (int year)
171 return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0);
175 * Return the weekday [0,6] (0 = Sunday) of the first day of `year'
178 static int
179 first_day (int year)
181 int ret = 4;
183 for (; year > 1970; --year)
184 ret = (ret + 365 + is_leap_year (year) ? 1 : 0) % 7;
185 return ret;
189 * Set `timeptr' given `wnum' (week number [0, 53])
192 static void
193 set_week_number_sun (struct tm *timeptr, int wnum)
195 int fday = first_day (timeptr->tm_year + tm_year_base);
197 timeptr->tm_yday = wnum * 7 + timeptr->tm_wday - fday;
198 if (timeptr->tm_yday < 0) {
199 timeptr->tm_wday = fday;
200 timeptr->tm_yday = 0;
205 * Set `timeptr' given `wnum' (week number [0, 53])
208 static void
209 set_week_number_mon (struct tm *timeptr, int wnum)
211 int fday = (first_day (timeptr->tm_year + tm_year_base) + 6) % 7;
213 timeptr->tm_yday = wnum * 7 + (timeptr->tm_wday + 6) % 7 - fday;
214 if (timeptr->tm_yday < 0) {
215 timeptr->tm_wday = (fday + 1) % 7;
216 timeptr->tm_yday = 0;
221 * Set `timeptr' given `wnum' (week number [0, 53])
224 static void
225 set_week_number_mon4 (struct tm *timeptr, int wnum)
227 int fday = (first_day (timeptr->tm_year + tm_year_base) + 6) % 7;
228 int offset = 0;
230 if (fday < 4)
231 offset += 7;
233 timeptr->tm_yday = offset + (wnum - 1) * 7 + timeptr->tm_wday - fday;
234 if (timeptr->tm_yday < 0) {
235 timeptr->tm_wday = fday;
236 timeptr->tm_yday = 0;
244 char * ROKEN_LIB_FUNCTION
245 strptime (const char *buf, const char *format, struct tm *timeptr)
247 char c;
249 for (; (c = *format) != '\0'; ++format) {
250 char *s;
251 int ret;
253 if (isspace ((unsigned char)c)) {
254 while (isspace ((unsigned char)*buf))
255 ++buf;
256 } else if (c == '%' && format[1] != '\0') {
257 c = *++format;
258 if (c == 'E' || c == 'O')
259 c = *++format;
260 switch (c) {
261 case 'A' :
262 ret = match_string (&buf, full_weekdays);
263 if (ret < 0)
264 return NULL;
265 timeptr->tm_wday = ret;
266 break;
267 case 'a' :
268 ret = match_string (&buf, abb_weekdays);
269 if (ret < 0)
270 return NULL;
271 timeptr->tm_wday = ret;
272 break;
273 case 'B' :
274 ret = match_string (&buf, full_month);
275 if (ret < 0)
276 return NULL;
277 timeptr->tm_mon = ret;
278 break;
279 case 'b' :
280 case 'h' :
281 ret = match_string (&buf, abb_month);
282 if (ret < 0)
283 return NULL;
284 timeptr->tm_mon = ret;
285 break;
286 case 'C' :
287 if (parse_number(&buf, 2, &ret))
288 return NULL;
289 timeptr->tm_year = (ret * 100) - tm_year_base;
290 break;
291 case 'c' :
292 abort ();
293 case 'D' : /* %m/%d/%y */
294 s = strptime (buf, "%m/%d/%y", timeptr);
295 if (s == NULL)
296 return NULL;
297 buf = s;
298 break;
299 case 'd' :
300 case 'e' :
301 if (parse_number(&buf, 2, &ret))
302 return NULL;
303 timeptr->tm_mday = ret;
304 break;
305 case 'H' :
306 case 'k' :
307 if (parse_number(&buf, 2, &ret))
308 return NULL;
309 timeptr->tm_hour = ret;
310 break;
311 case 'I' :
312 case 'l' :
313 if (parse_number(&buf, 2, &ret))
314 return NULL;
315 if (ret == 12)
316 timeptr->tm_hour = 0;
317 else
318 timeptr->tm_hour = ret;
319 break;
320 case 'j' :
321 if (parse_number(&buf, 3, &ret))
322 return NULL;
323 if (ret == 0)
324 return NULL;
325 timeptr->tm_yday = ret - 1;
326 break;
327 case 'm' :
328 if (parse_number(&buf, 2, &ret))
329 return NULL;
330 if (ret == 0)
331 return NULL;
332 timeptr->tm_mon = ret - 1;
333 break;
334 case 'M' :
335 if (parse_number(&buf, 2, &ret))
336 return NULL;
337 timeptr->tm_min = ret;
338 break;
339 case 'n' :
340 while (isspace ((unsigned char)*buf))
341 buf++;
342 break;
343 case 'p' :
344 ret = match_string (&buf, ampm);
345 if (ret < 0)
346 return NULL;
347 if (timeptr->tm_hour == 0) {
348 if (ret == 1)
349 timeptr->tm_hour = 12;
350 } else
351 timeptr->tm_hour += 12;
352 break;
353 case 'r' : /* %I:%M:%S %p */
354 s = strptime (buf, "%I:%M:%S %p", timeptr);
355 if (s == NULL)
356 return NULL;
357 buf = s;
358 break;
359 case 'R' : /* %H:%M */
360 s = strptime (buf, "%H:%M", timeptr);
361 if (s == NULL)
362 return NULL;
363 buf = s;
364 break;
365 case 'S' :
366 if (parse_number(&buf, 2, &ret))
367 return NULL;
368 timeptr->tm_sec = ret;
369 break;
370 case 't' :
371 while (isspace ((unsigned char)*buf))
372 buf++;
373 break;
374 case 'T' : /* %H:%M:%S */
375 case 'X' :
376 s = strptime (buf, "%H:%M:%S", timeptr);
377 if (s == NULL)
378 return NULL;
379 buf = s;
380 break;
381 case 'u' :
382 if (parse_number(&buf, 1, &ret))
383 return NULL;
384 if (ret <= 0)
385 return NULL;
386 timeptr->tm_wday = ret - 1;
387 break;
388 case 'w' :
389 if (parse_number(&buf, 1, &ret))
390 return NULL;
391 timeptr->tm_wday = ret;
392 break;
393 case 'U' :
394 if (parse_number(&buf, 2, &ret))
395 return NULL;
396 set_week_number_sun (timeptr, ret);
397 break;
398 case 'V' :
399 if (parse_number(&buf, 2, &ret))
400 return NULL;
401 set_week_number_mon4 (timeptr, ret);
402 break;
403 case 'W' :
404 if (parse_number(&buf, 2, &ret))
405 return NULL;
406 set_week_number_mon (timeptr, ret);
407 break;
408 case 'x' :
409 s = strptime (buf, "%Y:%m:%d", timeptr);
410 if (s == NULL)
411 return NULL;
412 buf = s;
413 break;
414 case 'y' :
415 if (parse_number(&buf, 2, &ret))
416 return NULL;
417 if (ret < 70)
418 timeptr->tm_year = 100 + ret;
419 else
420 timeptr->tm_year = ret;
421 break;
422 case 'Y' :
423 if (parse_number(&buf, 4, &ret))
424 return NULL;
425 timeptr->tm_year = ret - tm_year_base;
426 break;
427 case 'Z' :
428 abort ();
429 case '\0' :
430 --format;
431 /* FALLTHROUGH */
432 case '%' :
433 if (*buf == '%')
434 ++buf;
435 else
436 return NULL;
437 break;
438 default :
439 if (*buf == '%' || *++buf == c)
440 ++buf;
441 else
442 return NULL;
443 break;
445 } else {
446 if (*buf == c)
447 ++buf;
448 else
449 return NULL;
452 return rk_UNCONST(buf);