1 : /*
2 : * Copyright (C) 2004, 2005, 2006 James Antill
3 : *
4 : * This library is free software; you can redistribute it and/or
5 : * modify it under the terms of the GNU Lesser General Public
6 : * License as published by the Free Software Foundation; either
7 : * version 2 of the License, or (at your option) any later version.
8 : *
9 : * This library is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : * Lesser General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU Lesser General Public
15 : * License along with this library; if not, write to the Free Software
16 : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 : *
18 : * email: james@and.org
19 : */
20 : /* date text generating functions ... stop glibc some spending all the time
21 : * doing stat() on /etc/localtime */
22 :
23 : #define EX_UTILS_NO_FUNCS 1
24 : #include "ex_utils.h"
25 :
26 : #include "date.h"
27 :
28 : #include "mk.h"
29 :
30 : #include <limits.h> /* CHAR_BIT */
31 :
32 : #ifndef FALSE
33 : # define FALSE 0
34 : #endif
35 :
36 : #ifndef TRUE
37 : # define TRUE 1
38 : #endif
39 :
40 : void date_free(Date_store *data)
41 596 : {
42 596 : F(data);
43 596 : }
44 :
45 : Date_store *date_make(void)
46 656 : {
47 656 : Date_store *data = MK(sizeof(Date_store));
48 656 : unsigned int num = 0;
49 :
50 656 : if (!data)
51 0 : return (NULL);
52 :
53 : if (COMPILE_DEBUG)
54 328 : data->tmt = DATE__TYPE_TM_UNKNOWN;
55 :
56 656 : data->saved_count = DATE__CACHE_NUM - 1;
57 :
58 3936 : while (num < DATE__CACHE_NUM)
59 2624 : data->saved_val[num++] = -1;
60 :
61 656 : return (data);
62 : }
63 :
64 : static const struct tm *date__srch(Date_store *data, time_t val)
65 212367 : {
66 212367 : unsigned int num = 0;
67 :
68 106234 : ASSERT(malloc_check_sz_mem(data, sizeof(Date_store)) || TRUE);
69 :
70 106234 : ASSERT((data->tmt == DATE__TYPE_TM_GMT) ||
71 : (data->tmt == DATE__TYPE_TM_LOC));
72 :
73 643486 : while (num < DATE__CACHE_NUM)
74 : {
75 530781 : if (data->saved_val[num] == val)
76 205795 : return (data->saved_tm + num);
77 :
78 324986 : ++num;
79 : }
80 :
81 6572 : return (NULL);
82 : }
83 :
84 : #define DATE__GET(x) do { \
85 : num = (data->saved_count + 1) % DATE__CACHE_NUM; \
86 : \
87 : data->saved_val[num] = -1; \
88 : if (! x (&val, data->saved_tm + num)) \
89 : return (NULL); \
90 : data->saved_val[num] = val; \
91 : data->saved_count = num; \
92 : } while (FALSE)
93 :
94 : const struct tm *date_gmtime(Date_store *data, time_t val)
95 144454 : {
96 144454 : unsigned int num = 0;
97 144454 : const struct tm *ret = NULL;
98 :
99 72263 : if (COMPILE_DEBUG && (data->tmt == DATE__TYPE_TM_UNKNOWN))
100 22 : data->tmt = DATE__TYPE_TM_GMT;
101 :
102 72263 : ASSERT(data->tmt == DATE__TYPE_TM_GMT);
103 :
104 144454 : if ((ret = date__srch(data, val)))
105 138612 : return (ret);
106 :
107 5842 : DATE__GET(gmtime_r);
108 :
109 5842 : return (data->saved_tm + num);
110 : }
111 :
112 : const struct tm *date_localtime(Date_store *data, time_t val)
113 67913 : {
114 67913 : unsigned int num = 0;
115 67913 : const struct tm *ret = NULL;
116 :
117 33971 : if (COMPILE_DEBUG && (data->tmt == DATE__TYPE_TM_UNKNOWN))
118 22 : data->tmt = DATE__TYPE_TM_LOC;
119 :
120 33971 : ASSERT(data->tmt == DATE__TYPE_TM_LOC);
121 :
122 67913 : if ((ret = date__srch(data, val)))
123 67183 : return (ret);
124 :
125 730 : DATE__GET(localtime_r);
126 :
127 730 : return (data->saved_tm + num);
128 : }
129 : #undef DATE__GET
130 :
131 :
132 : static const char date__days_shrt[7][4] =
133 : {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
134 : static const char date__days_full[7][10] =
135 : {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
136 : "Saturday"};
137 : static const char date__months[12][4] =
138 : {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
139 : "Sep", "Oct", "Nov", "Dec"};
140 :
141 : /* stupid roll your own string API, but what ya gonna do... */
142 : #define CP_BEG() do { ptr = data->ret_buf; \
143 : ASSERT(memset(ptr, 0xbe, DATE__RET_SZ)); \
144 : } while (FALSE)
145 : #define CP(x, n) do { \
146 : memcpy(ptr, x, n); ptr += (n); \
147 : ASSERT((ptr >= data->ret_buf) && \
148 : (ptr <= (data->ret_buf + DATE__RET_SZ))); } while (FALSE)
149 : #define CP_LEN(x) CP(x, strlen(x))
150 : #define CP_NUM(x) do { \
151 : unsigned int num = (x); \
152 : char buf[sizeof(unsigned int) * CHAR_BIT]; \
153 : char *tmp = buf + sizeof(buf); \
154 : \
155 : while (num) \
156 : { \
157 : *--tmp = '0' + (num % 10); \
158 : num /= 10; \
159 : } \
160 : if ((tmp - buf) != sizeof(buf)) \
161 : CP(tmp, sizeof(buf) - (tmp - buf)); \
162 : else \
163 : CP_LEN("0"); \
164 : } while (FALSE)
165 : #define CP_02NUM(x) do { \
166 : if ((x) < 10) \
167 : CP_LEN("0"); \
168 : CP_NUM(x); \
169 : } while (FALSE)
170 : #define CP__2NUM(x) do { \
171 : if ((x) < 10) \
172 : CP_LEN(" "); \
173 : CP_NUM(x); \
174 : } while (FALSE)
175 :
176 : #define CP_END() CP("", 1)
177 :
178 : /* we do all of this "locally" due to POSIX requiring strftime to call tzset()
179 : Glibc then compounds that by stat()'ing */
180 : const char *date_rfc1123(Date_store *data, time_t val)
181 100678 : {
182 100678 : const struct tm *tm = NULL;
183 100678 : char *ptr = NULL;
184 :
185 100678 : if (!(tm = date_gmtime(data, val)))
186 0 : err(EXIT_FAILURE, "gmtime_r(%s)", "%a, %d %h %Y %T GMT");
187 :
188 100678 : CP_BEG();
189 100678 : CP(date__days_shrt[tm->tm_wday], 3); /* %a */
190 100678 : CP_LEN(", ");
191 100678 : CP_02NUM(tm->tm_mday); /* %d */
192 100678 : CP_LEN(" ");
193 100678 : CP(date__months[tm->tm_mon], 3); /* %h */
194 100678 : CP_LEN(" ");
195 100678 : CP_NUM(tm->tm_year + 1900); /* %Y */
196 100678 : CP_LEN(" ");
197 100678 : CP_02NUM(tm->tm_hour); /* %T */
198 100678 : CP_LEN(":");
199 100678 : CP_02NUM(tm->tm_min);
200 100678 : CP_LEN(":");
201 100678 : CP_02NUM(tm->tm_sec);
202 100678 : CP_LEN(" GMT");
203 100678 : CP_END();
204 :
205 100678 : return (data->ret_buf);
206 : }
207 : const char *date_rfc850(Date_store *data, time_t val)
208 21944 : {
209 21944 : const struct tm *tm = NULL;
210 21944 : char *ptr = NULL;
211 :
212 21944 : if (!(tm = date_gmtime(data, val)))
213 0 : err(EXIT_FAILURE, "gmtime_r(%s)", "%A, %d-%h-%y %T GMT");
214 :
215 21944 : CP_BEG();
216 21944 : CP_LEN(date__days_full[tm->tm_wday]); /* %A */
217 21944 : CP_LEN(", ");
218 21944 : CP_02NUM(tm->tm_mday); /* %d */
219 21944 : CP_LEN("-");
220 21944 : CP(date__months[tm->tm_mon], 3); /* %h */
221 21944 : CP_LEN("-");
222 21944 : CP_02NUM(tm->tm_year % 100); /* %y */
223 21944 : CP_LEN(" ");
224 21944 : CP_02NUM(tm->tm_hour); /* %T */
225 21944 : CP_LEN(":");
226 21944 : CP_02NUM(tm->tm_min);
227 21944 : CP_LEN(":");
228 21944 : CP_02NUM(tm->tm_sec);
229 21944 : CP_LEN(" GMT");
230 21944 : CP_END();
231 :
232 21944 : return (data->ret_buf);
233 : }
234 : const char *date_asctime(Date_store *data, time_t val)
235 21800 : {
236 21800 : const struct tm *tm = NULL;
237 21800 : char *ptr = NULL;
238 :
239 21800 : if (!(tm = date_gmtime(data, val)))
240 0 : err(EXIT_FAILURE, "gmtime_r(%s)", "%a %h %e %T %Y");
241 :
242 21800 : CP_BEG();
243 21800 : CP(date__days_shrt[tm->tm_wday], 3); /* %a */
244 21800 : CP_LEN(" ");
245 21800 : CP(date__months[tm->tm_mon], 3); /* %h */
246 21800 : CP_LEN(" ");
247 21800 : CP__2NUM(tm->tm_mday); /* %e */
248 21800 : CP_LEN(" ");
249 21800 : CP_02NUM(tm->tm_hour); /* %T */
250 21800 : CP_LEN(":");
251 21800 : CP_02NUM(tm->tm_min);
252 21800 : CP_LEN(":");
253 21800 : CP_02NUM(tm->tm_sec);
254 21800 : CP_LEN(" ");
255 21800 : CP_NUM(tm->tm_year + 1900); /* %Y */
256 21800 : CP_END();
257 :
258 21800 : return (data->ret_buf);
259 : }
260 : const char *date_syslog_trad(Date_store *data, time_t val)
261 0 : {
262 0 : const struct tm *tm = NULL;
263 0 : char *ptr = NULL;
264 :
265 0 : if (!(tm = date_localtime(data, val)))
266 0 : err(EXIT_FAILURE, "localtime_r(%s)", "%h %e %T");
267 :
268 0 : CP_BEG();
269 0 : CP(date__months[tm->tm_mon], 3); /* %h */
270 0 : CP_LEN(" ");
271 0 : CP__2NUM(tm->tm_mday); /* %e */
272 0 : CP_LEN(" ");
273 0 : CP_02NUM(tm->tm_hour); /* %T */
274 0 : CP_LEN(":");
275 0 : CP_02NUM(tm->tm_min);
276 0 : CP_LEN(":");
277 0 : CP_02NUM(tm->tm_sec);
278 0 : CP_END();
279 :
280 0 : return (data->ret_buf);
281 : }
282 :
283 : const char *date_syslog_yr(Date_store *data, time_t val)
284 67913 : {
285 67913 : const struct tm *tm = NULL;
286 67913 : char *ptr = NULL;
287 :
288 67913 : if (!(tm = date_localtime(data, val)))
289 0 : err(EXIT_FAILURE, "localtime_r(%s)", "%h %e %T");
290 :
291 67913 : CP_BEG();
292 67913 : CP_NUM(tm->tm_year + 1900); /* %Y */
293 67913 : CP_LEN(" ");
294 67913 : CP(date__months[tm->tm_mon], 3); /* %h */
295 67913 : CP_LEN(" ");
296 67913 : CP__2NUM(tm->tm_mday); /* %e */
297 67913 : CP_LEN(" ");
298 67913 : CP_02NUM(tm->tm_hour); /* %T */
299 67913 : CP_LEN(":");
300 67913 : CP_02NUM(tm->tm_min);
301 67913 : CP_LEN(":");
302 67913 : CP_02NUM(tm->tm_sec);
303 67913 : CP_END();
304 :
305 67913 : return (data->ret_buf);
306 : }
307 :
308 : #if 0
309 : #define VPREFIX(vstr, p, l, cstr) \
310 : (((l) >= CLEN(cstr)) && \
311 : vstr_cmp_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
312 :
313 : /* see rfc2616 3.3.1 -- full date parser */
314 : static time_t date__parse_http(Date_storage *date,
315 : Vstr_base *s1, size_t pos, size_t len,
316 : time_t val)
317 : {
318 : const struct tm *tm = date_gmtime(date, &val);
319 : unsigned int scan = 0;
320 :
321 : if (!tm) return (-1);
322 :
323 : switch (len)
324 : {
325 : case 4 + 1 + 9 + 1 + 8 + 1 + 3: /* rfc1123 format - should be most common */
326 : {
327 : scan = 0;
328 : while (scan < 7)
329 : {
330 : if (VPREFIX(s1, pos, len, http__date_days_shrt[scan]))
331 : break;
332 : ++scan;
333 : }
334 : len -= 3; pos += 3;
335 :
336 : if (!VPREFIX(s1, pos, len, ", "))
337 : return (-1);
338 : len -= CLEN(", "); pos += CLEN(", ");
339 :
340 : tm->tm_mday = http__date_parse_2d(s1, pos, len, 1, 31);
341 :
342 : if (!VPREFIX(s1, pos, len, " "))
343 : return (-1);
344 : len -= CLEN(" "); pos += CLEN(" ");
345 :
346 : scan = 0;
347 : while (scan < 12)
348 : {
349 : if (VPREFIX(s1, pos, len, http__date_months[scan]))
350 : break;
351 : ++scan;
352 : }
353 : len -= 3; pos += 3;
354 :
355 : tm->tm_mon = scan;
356 :
357 : if (!VPREFIX(s1, pos, len, " "))
358 : return (-1);
359 : len -= CLEN(" "); pos += CLEN(" ");
360 :
361 : tm->tm_year = http__date_parse_4d(s1, pos, len);
362 :
363 : if (!VPREFIX(s1, pos, len, " "))
364 : return (-1);
365 : len -= CLEN(" "); pos += CLEN(" ");
366 :
367 : tm->tm_hour = http__date_parse2d(s1, pos, len, 0, 23);
368 : if (!VPREFIX(s1, pos, len, ":"))
369 : return (-1);
370 : len -= CLEN(":"); pos += CLEN(":");
371 : tm->tm_min = http__date_parse2d(s1, pos, len, 0, 59);
372 : if (!VPREFIX(s1, pos, len, ":"))
373 : return (-1);
374 : len -= CLEN(":"); pos += CLEN(":");
375 : tm->tm_sec = http__date_parse2d(s1, pos, len, 0, 61);
376 :
377 : if (!VPREFIX(s1, pos, len, " GMT"))
378 : return (-1);
379 : }
380 : return (mktime(tm));
381 :
382 : case 7 + 1 + 7 + 1 + 8 + 1 + 3:
383 : case 8 + 1 + 7 + 1 + 8 + 1 + 3:
384 : case 9 + 1 + 7 + 1 + 8 + 1 + 3:
385 : case 10 + 1 + 7 + 1 + 8 + 1 + 3: /* rfc850 format */
386 : {
387 : size_t match_len = 0;
388 :
389 : scan = 0;
390 : while (scan < 7)
391 : {
392 : match_len = CLEN(http__date_days_full[scan]);
393 : if (VPREFIX(s1, pos, len, http__date_days_full[scan]))
394 : break;
395 : ++scan;
396 : }
397 : len -= match_len; pos += match_len;
398 :
399 : return (-1);
400 : }
401 : return (mktime(tm));
402 :
403 : case 3 + 1 + 6 + 1 + 8 + 1 + 4: /* asctime format */
404 : {
405 : scan = 0;
406 : while (scan < 7)
407 : {
408 : if (VPREFIX(s1, pos, len, http__date_days_shrt[scan]))
409 : break;
410 : ++scan;
411 : }
412 : len -= 3; pos += 3;
413 :
414 : return (-1);
415 : }
416 : return (mktime(tm));
417 : }
418 :
419 : return (-1);
420 : }
421 : #endif
|