xref: /haiku/src/apps/poorman/libhttpd/tdate_parse.c (revision 268f99dd7dc4bd7474a8bd2742d3f1ec1de6752a)
1 /* tdate_parse - parse string dates into internal form, stripped-down version
2 **
3 ** Copyright © 1995 by Jef Poskanzer <jef@mail.acme.com>.
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 ** 1. Redistributions of source code must retain the above copyright
10 **    notice, this list of conditions and the following disclaimer.
11 ** 2. Redistributions in binary form must reproduce the above copyright
12 **    notice, this list of conditions and the following disclaimer in the
13 **    documentation and/or other materials provided with the distribution.
14 **
15 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 ** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 ** SUCH DAMAGE.
26 */
27 
28 /* This is a stripped-down version of date_parse.c, available at
29 ** http://www.acme.com/software/date_parse/
30 */
31 
32 #include <sys/types.h>
33 
34 #include <ctype.h>
35 #ifdef HAVE_MEMORY_H
36 #include <memory.h>
37 #endif
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <time.h>
42 
43 #include "tdate_parse.h"
44 
45 
46 struct strlong {
47     char* s;
48     long l;
49     };
50 
51 
52 static void
pound_case(char * str)53 pound_case( char* str )
54     {
55     for ( ; *str != '\0'; ++str )
56 	{
57 	if ( isupper( (int) *str ) )
58 	    *str = tolower( (int) *str );
59 	}
60     }
61 
62 
63 //static int
64 //strlong_compare( const void* v1, const void* v2 )
65 //    {
66 //    const struct strlong* s1 = (const struct strlong*) v1;
67 //    const struct strlong* s2 = (const struct strlong*) v2;
68 //    return strcmp( s1->s, s2->s );
69 //    }
70 
71 
72 static int
strlong_search(char * str,struct strlong * tab,int n,long * lP)73 strlong_search( char* str, struct strlong* tab, int n, long* lP )
74     {
75     int i, h, l, r;
76 
77     l = 0;
78     h = n - 1;
79     for (;;)
80 	{
81 	i = ( h + l ) / 2;
82 	r = strcmp( str, tab[i].s );
83 	if ( r < 0 )
84 	    h = i - 1;
85 	else if ( r > 0 )
86 	    l = i + 1;
87 	else
88 	    {
89 	    *lP = tab[i].l;
90 	    return 1;
91 	    }
92 	if ( h < l )
93 	    return 0;
94 	}
95     }
96 
97 
98 static int
scan_wday(char * str_wday,long * tm_wdayP)99 scan_wday( char* str_wday, long* tm_wdayP )
100     {
101 //    static struct strlong wday_tab[] = {
102 //	{ "sun", 0 }, { "sunday", 0 },
103 //	{ "mon", 1 }, { "monday", 1 },
104 //	{ "tue", 2 }, { "tuesday", 2 },
105 //	{ "wed", 3 }, { "wednesday", 3 },
106 //	{ "thu", 4 }, { "thursday", 4 },
107 //	{ "fri", 5 }, { "friday", 5 },
108 //	{ "sat", 6 }, { "saturday", 6 },
109 //	};
110 //    static int sorted = 0;
111 //
112 //    if ( ! sorted )
113 //	{
114 //	(void) qsort(
115 //	    wday_tab, sizeof(wday_tab)/sizeof(struct strlong),
116 //	    sizeof(struct strlong), strlong_compare );
117 //	sorted = 1;
118 //	}
119 
120 	/*manually sorted wday_tab to avoid concurrent accessing problem*/
121 	static const struct strlong wday_tab[] = {
122 		{ "fri", 5 }, { "friday", 5 },
123 		{ "mon", 1 }, { "monday", 1 },
124 		{ "sat", 6 }, { "saturday", 6 },
125 		{ "sun", 0 }, { "sunday", 0 },
126 		{ "thu", 4 }, { "thursday", 4 },
127 		{ "tue", 2 }, { "tuesday", 2 },
128 		{ "wed", 3 }, { "wednesday", 3 }
129 	};
130 
131     pound_case( str_wday );
132     return strlong_search(
133 	str_wday, wday_tab, sizeof(wday_tab)/sizeof(struct strlong), tm_wdayP );
134     }
135 
136 
137 static int
scan_mon(char * str_mon,long * tm_monP)138 scan_mon( char* str_mon, long* tm_monP )
139     {
140 //    static struct strlong mon_tab[] = {
141 //	{ "jan", 0 }, { "january", 0 },
142 //	{ "feb", 1 }, { "february", 1 },
143 //	{ "mar", 2 }, { "march", 2 },
144 //	{ "apr", 3 }, { "april", 3 },
145 //	{ "may", 4 },
146 //	{ "jun", 5 }, { "june", 5 },
147 //	{ "jul", 6 }, { "july", 6 },
148 //	{ "aug", 7 }, { "august", 7 },
149 //	{ "sep", 8 }, { "september", 8 },
150 //	{ "oct", 9 }, { "october", 9 },
151 //	{ "nov", 10 }, { "november", 10 },
152 //	{ "dec", 11 }, { "december", 11 },
153 //	};
154 //    static int sorted = 0;
155 //
156 //    if ( ! sorted )
157 //	{
158 //	(void) qsort(
159 //	    mon_tab, sizeof(mon_tab)/sizeof(struct strlong),
160 //	    sizeof(struct strlong), strlong_compare );
161 //	sorted = 1;
162 //	}
163 
164 	/*manually sorted mon_tab to avoid concurrent accessing problem*/
165 	static const struct strlong mon_tab[] = {
166 		{ "apr", 3 }, { "april", 3 },
167 		{ "aug", 7 }, { "august", 7 },
168 		{ "dec", 11}, { "december", 11 },
169 		{ "feb", 1 }, { "february", 1 },
170 		{ "jan", 0 }, { "january", 0 },
171 		{ "jul", 6 }, { "july", 6 },
172 		{ "jun", 5 }, { "june", 5 },
173 		{ "mar", 2 }, { "march", 2 },
174 		{ "may", 4 },
175 		{ "nov", 10}, { "november", 10 },
176 		{ "oct", 9 }, { "october", 9 },
177 		{ "sep", 8 }, { "september", 8 }
178 	};
179 
180     pound_case( str_mon );
181     return strlong_search(
182 	str_mon, mon_tab, sizeof(mon_tab)/sizeof(struct strlong), tm_monP );
183     }
184 
185 
186 static int
is_leap(int year)187 is_leap( int year )
188     {
189     return year % 400? ( year % 100 ? ( year % 4 ? 0 : 1 ) : 0 ) : 1;
190     }
191 
192 
193 /* Basically the same as mktime(). */
194 static time_t
tm_to_time(struct tm * tmP)195 tm_to_time( struct tm* tmP )
196     {
197     time_t t;
198     static int monthtab[12] = {
199 	0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
200 
201     /* Years since epoch, converted to days. */
202     t = ( tmP->tm_year - 70 ) * 365;
203     /* Leap days for previous years - this will break in 2100! */
204     t += ( tmP->tm_year - 69 ) / 4;
205     /* Days for the beginning of this month. */
206     t += monthtab[tmP->tm_mon];
207     /* Leap day for this year. */
208     if ( tmP->tm_mon >= 2 && is_leap( tmP->tm_year + 1900 ) )
209 	++t;
210     /* Days since the beginning of this month. */
211     t += tmP->tm_mday - 1;	/* 1-based field */
212     /* Hours, minutes, and seconds. */
213     t = t * 24 + tmP->tm_hour;
214     t = t * 60 + tmP->tm_min;
215     t = t * 60 + tmP->tm_sec;
216 
217     return t;
218     }
219 
220 
221 time_t
tdate_parse(char * str)222 tdate_parse( char* str )
223     {
224     struct tm tm;
225     char* cp;
226     char str_mon[500], str_wday[500];
227     int tm_sec, tm_min, tm_hour, tm_mday, tm_year;
228     long tm_mon, tm_wday;
229     time_t t;
230 
231     /* Initialize. */
232     (void) memset( (char*) &tm, 0, sizeof(struct tm) );
233 
234     /* Skip initial whitespace ourselves - sscanf is clumsy at this. */
235     for ( cp = str; *cp == ' ' || *cp == '\t'; ++cp )
236 	continue;
237 
238     /* And do the sscanfs.  WARNING: you can add more formats here,
239     ** but be careful!  You can easily screw up the parsing of existing
240     ** formats when you add new ones.  The order is important.
241     */
242 
243     /* DD-mth-YY HH:MM:SS GMT */
244     if ( sscanf( cp, "%d-%400[a-zA-Z]-%d %d:%d:%d GMT",
245 		&tm_mday, str_mon, &tm_year, &tm_hour, &tm_min,
246 		&tm_sec ) == 6 &&
247 	    scan_mon( str_mon, &tm_mon ) )
248 	{
249 	tm.tm_mday = tm_mday;
250 	tm.tm_mon = tm_mon;
251 	tm.tm_year = tm_year;
252 	tm.tm_hour = tm_hour;
253 	tm.tm_min = tm_min;
254 	tm.tm_sec = tm_sec;
255 	}
256 
257     /* DD mth YY HH:MM:SS GMT */
258     else if ( sscanf( cp, "%d %400[a-zA-Z] %d %d:%d:%d GMT",
259 		&tm_mday, str_mon, &tm_year, &tm_hour, &tm_min,
260 		&tm_sec) == 6 &&
261 	    scan_mon( str_mon, &tm_mon ) )
262 	{
263 	tm.tm_mday = tm_mday;
264 	tm.tm_mon = tm_mon;
265 	tm.tm_year = tm_year;
266 	tm.tm_hour = tm_hour;
267 	tm.tm_min = tm_min;
268 	tm.tm_sec = tm_sec;
269 	}
270 
271     /* HH:MM:SS GMT DD-mth-YY */
272     else if ( sscanf( cp, "%d:%d:%d GMT %d-%400[a-zA-Z]-%d",
273 		&tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon,
274 		&tm_year ) == 6 &&
275 	    scan_mon( str_mon, &tm_mon ) )
276 	{
277 	tm.tm_hour = tm_hour;
278 	tm.tm_min = tm_min;
279 	tm.tm_sec = tm_sec;
280 	tm.tm_mday = tm_mday;
281 	tm.tm_mon = tm_mon;
282 	tm.tm_year = tm_year;
283 	}
284 
285     /* HH:MM:SS GMT DD mth YY */
286     else if ( sscanf( cp, "%d:%d:%d GMT %d %400[a-zA-Z] %d",
287 		&tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon,
288 		&tm_year ) == 6 &&
289 	    scan_mon( str_mon, &tm_mon ) )
290 	{
291 	tm.tm_hour = tm_hour;
292 	tm.tm_min = tm_min;
293 	tm.tm_sec = tm_sec;
294 	tm.tm_mday = tm_mday;
295 	tm.tm_mon = tm_mon;
296 	tm.tm_year = tm_year;
297 	}
298 
299     /* wdy, DD-mth-YY HH:MM:SS GMT */
300     else if ( sscanf( cp, "%400[a-zA-Z], %d-%400[a-zA-Z]-%d %d:%d:%d GMT",
301 		str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min,
302 		&tm_sec ) == 7 &&
303 	    scan_wday( str_wday, &tm_wday ) &&
304 	    scan_mon( str_mon, &tm_mon ) )
305 	{
306 	tm.tm_wday = tm_wday;
307 	tm.tm_mday = tm_mday;
308 	tm.tm_mon = tm_mon;
309 	tm.tm_year = tm_year;
310 	tm.tm_hour = tm_hour;
311 	tm.tm_min = tm_min;
312 	tm.tm_sec = tm_sec;
313 	}
314 
315     /* wdy, DD mth YY HH:MM:SS GMT */
316     else if ( sscanf( cp, "%400[a-zA-Z], %d %400[a-zA-Z] %d %d:%d:%d GMT",
317 		str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min,
318 		&tm_sec ) == 7 &&
319 	    scan_wday( str_wday, &tm_wday ) &&
320 	    scan_mon( str_mon, &tm_mon ) )
321 	{
322 	tm.tm_wday = tm_wday;
323 	tm.tm_mday = tm_mday;
324 	tm.tm_mon = tm_mon;
325 	tm.tm_year = tm_year;
326 	tm.tm_hour = tm_hour;
327 	tm.tm_min = tm_min;
328 	tm.tm_sec = tm_sec;
329 	}
330 
331     /* wdy mth DD HH:MM:SS GMT YY */
332     else if ( sscanf( cp, "%400[a-zA-Z] %400[a-zA-Z] %d %d:%d:%d GMT %d",
333 		str_wday, str_mon, &tm_mday, &tm_hour, &tm_min, &tm_sec,
334 		&tm_year ) == 7 &&
335 	    scan_wday( str_wday, &tm_wday ) &&
336 	    scan_mon( str_mon, &tm_mon ) )
337 	{
338 	tm.tm_wday = tm_wday;
339 	tm.tm_mon = tm_mon;
340 	tm.tm_mday = tm_mday;
341 	tm.tm_hour = tm_hour;
342 	tm.tm_min = tm_min;
343 	tm.tm_sec = tm_sec;
344 	tm.tm_year = tm_year;
345 	}
346     else
347 	return (time_t) -1;
348 
349     if ( tm.tm_year > 1900 )
350 	tm.tm_year -= 1900;
351     else if ( tm.tm_year < 70 )
352 	tm.tm_year += 100;
353 
354     t = tm_to_time( &tm );
355 
356     return t;
357     }
358