1 /*
2 * Copyright (C) 2004, 2005 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 /* FIXME: fix this to use constant data which is always in the C locale */
41
date_free(Date_store * data)42 void date_free(Date_store *data)
43 {
44 F(data);
45 }
46
date_make(void)47 Date_store *date_make(void)
48 {
49 Date_store *data = MK(sizeof(Date_store));
50 unsigned int num = 0;
51
52 if (!data)
53 return (NULL);
54
55 data->saved_count = DATE__CACHE_NUM - 1;
56
57 while (num < DATE__CACHE_NUM)
58 data->saved_val[num++] = -1;
59
60 return (data);
61 }
62
date_gmtime(Date_store * data,time_t val)63 const struct tm *date_gmtime(Date_store *data, time_t val)
64 {
65 unsigned int num = 0;
66
67 ASSERT(malloc_check_sz_mem(data, sizeof(Date_store)) || TRUE);
68
69 while (num < DATE__CACHE_NUM)
70 {
71 if (data->saved_val[num] == val)
72 return (data->saved_tm + num);
73
74 ++num;
75 }
76
77 num = (data->saved_count + 1) % DATE__CACHE_NUM;
78
79 data->saved_val[num] = -1;
80 if (!gmtime_r(&val, data->saved_tm + num))
81 return (NULL);
82 data->saved_val[num] = val;
83 data->saved_count = num;
84
85 return (data->saved_tm + num);
86 }
87
88
89 static const char date__days_shrt[7][4] =
90 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
91 static const char date__days_full[7][10] =
92 {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
93 "Saturday"};
94 static const char date__months[12][4] =
95 {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
96 "Sep", "Oct", "Nov", "Dec"};
97
98 /* stupid roll your own string API, but what ya gonna do... */
99 #define CP_BEG() do { ptr = data->ret_buf; \
100 ASSERT(memset(ptr, 0xbe, DATE__RET_SZ)); \
101 } while (FALSE)
102 #define CP(x, n) do { \
103 memcpy(ptr, x, n); ptr += (n); \
104 ASSERT((ptr >= data->ret_buf) && \
105 (ptr <= (data->ret_buf + DATE__RET_SZ))); } while (FALSE)
106 #define CP_LEN(x) CP(x, strlen(x))
107 #define CP_NUM(x) do { \
108 unsigned int num = (x); \
109 char buf[sizeof(unsigned int) * CHAR_BIT]; \
110 char *tmp = buf + sizeof(buf); \
111 \
112 while (num) \
113 { \
114 *--tmp = '0' + (num % 10); \
115 num /= 10; \
116 } \
117 if ((tmp - buf) != sizeof(buf)) \
118 CP(tmp, sizeof(buf) - (tmp - buf)); \
119 else \
120 CP_LEN("0"); \
121 } while (FALSE)
122 #define CP_02NUM(x) do { \
123 if ((x) < 10) \
124 CP_LEN("0"); \
125 CP_NUM(x); \
126 } while (FALSE)
127 #define CP__2NUM(x) do { \
128 if ((x) < 10) \
129 CP_LEN(" "); \
130 CP_NUM(x); \
131 } while (FALSE)
132
133 #define CP_END() CP("", 1)
134
135 /* we do all of this "locally" due to POSIX requiring strftime to call tzset()
136 Glibc then compounds that by stat()'ing */
date_rfc1123(Date_store * data,time_t val)137 const char *date_rfc1123(Date_store *data, time_t val)
138 {
139 const struct tm *tm = NULL;
140 char *ptr = NULL;
141
142 if (!(tm = date_gmtime(data, val)))
143 err(EXIT_FAILURE, "gmtime_r(%s)", "%a, %d %h %Y %T GMT");
144
145 CP_BEG();
146 CP(date__days_shrt[tm->tm_wday], 3); /* %a */
147 CP_LEN(", ");
148 CP_02NUM(tm->tm_mday); /* %d */
149 CP_LEN(" ");
150 CP(date__months[tm->tm_mon], 3); /* %h */
151 CP_LEN(" ");
152 CP_NUM(tm->tm_year + 1900); /* %Y */
153 CP_LEN(" ");
154 CP_02NUM(tm->tm_hour); /* %T */
155 CP_LEN(":");
156 CP_02NUM(tm->tm_min);
157 CP_LEN(":");
158 CP_02NUM(tm->tm_sec);
159 CP_LEN(" GMT");
160 CP_END();
161
162 return (data->ret_buf);
163 }
date_rfc850(Date_store * data,time_t val)164 const char *date_rfc850(Date_store *data, time_t val)
165 {
166 const struct tm *tm = NULL;
167 char *ptr = NULL;
168
169 if (!(tm = date_gmtime(data, val)))
170 err(EXIT_FAILURE, "gmtime_r(%s)", "%A, %d-%h-%y %T GMT");
171
172 CP_BEG();
173 CP_LEN(date__days_full[tm->tm_wday]); /* %A */
174 CP_LEN(", ");
175 CP_02NUM(tm->tm_mday); /* %d */
176 CP_LEN("-");
177 CP(date__months[tm->tm_mon], 3); /* %h */
178 CP_LEN("-");
179 CP_02NUM(tm->tm_year % 100); /* %y */
180 CP_LEN(" ");
181 CP_02NUM(tm->tm_hour); /* %T */
182 CP_LEN(":");
183 CP_02NUM(tm->tm_min);
184 CP_LEN(":");
185 CP_02NUM(tm->tm_sec);
186 CP_LEN(" GMT");
187 CP_END();
188
189 return (data->ret_buf);
190 }
date_asctime(Date_store * data,time_t val)191 const char *date_asctime(Date_store *data, time_t val)
192 {
193 const struct tm *tm = NULL;
194 char *ptr = NULL;
195
196 if (!(tm = date_gmtime(data, val)))
197 err(EXIT_FAILURE, "gmtime_r(%s)", "%a %h %e %T %Y");
198
199 CP_BEG();
200 CP(date__days_shrt[tm->tm_wday], 3); /* %a */
201 CP_LEN(" ");
202 CP(date__months[tm->tm_mon], 3); /* %h */
203 CP_LEN(" ");
204 CP__2NUM(tm->tm_mday); /* %e */
205 CP_LEN(" ");
206 CP_02NUM(tm->tm_hour); /* %T */
207 CP_LEN(":");
208 CP_02NUM(tm->tm_min);
209 CP_LEN(":");
210 CP_02NUM(tm->tm_sec);
211 CP_LEN(" ");
212 CP_NUM(tm->tm_year + 1900); /* %Y */
213 CP_END();
214
215 return (data->ret_buf);
216 }
217
218 #if 0
219 #define VPREFIX(vstr, p, l, cstr) \
220 (((l) >= CLEN(cstr)) && \
221 vstr_cmp_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
222
223 /* see rfc2616 3.3.1 -- full date parser */
224 static time_t date__parse_http(Date_storage *date,
225 Vstr_base *s1, size_t pos, size_t len,
226 time_t val)
227 {
228 const struct tm *tm = date_gmtime(date, &val);
229 unsigned int scan = 0;
230
231 if (!tm) return (-1);
232
233 switch (len)
234 {
235 case 4 + 1 + 9 + 1 + 8 + 1 + 3: /* rfc1123 format - should be most common */
236 {
237 scan = 0;
238 while (scan < 7)
239 {
240 if (VPREFIX(s1, pos, len, http__date_days_shrt[scan]))
241 break;
242 ++scan;
243 }
244 len -= 3; pos += 3;
245
246 if (!VPREFIX(s1, pos, len, ", "))
247 return (-1);
248 len -= CLEN(", "); pos += CLEN(", ");
249
250 tm->tm_mday = http__date_parse_2d(s1, pos, len, 1, 31);
251
252 if (!VPREFIX(s1, pos, len, " "))
253 return (-1);
254 len -= CLEN(" "); pos += CLEN(" ");
255
256 scan = 0;
257 while (scan < 12)
258 {
259 if (VPREFIX(s1, pos, len, http__date_months[scan]))
260 break;
261 ++scan;
262 }
263 len -= 3; pos += 3;
264
265 tm->tm_mon = scan;
266
267 if (!VPREFIX(s1, pos, len, " "))
268 return (-1);
269 len -= CLEN(" "); pos += CLEN(" ");
270
271 tm->tm_year = http__date_parse_4d(s1, pos, len);
272
273 if (!VPREFIX(s1, pos, len, " "))
274 return (-1);
275 len -= CLEN(" "); pos += CLEN(" ");
276
277 tm->tm_hour = http__date_parse2d(s1, pos, len, 0, 23);
278 if (!VPREFIX(s1, pos, len, ":"))
279 return (-1);
280 len -= CLEN(":"); pos += CLEN(":");
281 tm->tm_min = http__date_parse2d(s1, pos, len, 0, 59);
282 if (!VPREFIX(s1, pos, len, ":"))
283 return (-1);
284 len -= CLEN(":"); pos += CLEN(":");
285 tm->tm_sec = http__date_parse2d(s1, pos, len, 0, 61);
286
287 if (!VPREFIX(s1, pos, len, " GMT"))
288 return (-1);
289 }
290 return (mktime(tm));
291
292 case 7 + 1 + 7 + 1 + 8 + 1 + 3:
293 case 8 + 1 + 7 + 1 + 8 + 1 + 3:
294 case 9 + 1 + 7 + 1 + 8 + 1 + 3:
295 case 10 + 1 + 7 + 1 + 8 + 1 + 3: /* rfc850 format */
296 {
297 size_t match_len = 0;
298
299 scan = 0;
300 while (scan < 7)
301 {
302 match_len = CLEN(http__date_days_full[scan]);
303 if (VPREFIX(s1, pos, len, http__date_days_full[scan]))
304 break;
305 ++scan;
306 }
307 len -= match_len; pos += match_len;
308
309 return (-1);
310 }
311 return (mktime(tm));
312
313 case 3 + 1 + 6 + 1 + 8 + 1 + 4: /* asctime format */
314 {
315 scan = 0;
316 while (scan < 7)
317 {
318 if (VPREFIX(s1, pos, len, http__date_days_shrt[scan]))
319 break;
320 ++scan;
321 }
322 len -= 3; pos += 3;
323
324 return (-1);
325 }
326 return (mktime(tm));
327 }
328
329 return (-1);
330 }
331 #endif
332
333 /* syslog is "special" as it might be called from a signal handler etc. */
date_syslog(time_t val,char * buf,size_t len)334 const char *date_syslog(time_t val, char *buf, size_t len)
335 {
336 struct tm store_tm_val[1];
337 struct tm *tm_val = NULL;
338
339 if (!(tm_val = localtime_r(&val, store_tm_val)))
340 err(EXIT_FAILURE, "localtime_r(%s)", "syslog");
341
342 strftime(buf, len, "%h %e %T", tm_val);
343
344 return (buf);
345 }
346
347