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