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