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