ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/vendor/libpeak-0.1.2/peak/tz.c
Revision: 3251
Committed: Wed Apr 2 16:58:30 2014 UTC (11 years, 4 months ago) by michael
Content type: text/x-csrc
File size: 12025 byte(s)
Log Message:
- Imported libpeak-0.1.2

File Contents

# User Rev Content
1 michael 3251 /* PEAK Library
2     *
3     * Copyright (c) 2003,2004
4     * Stephane Thiell <mbuna@bugged.org>. 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     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18     * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19     * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27     * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28     *
29     */
30     #define RCSID "$Id: tz.c,v 1.5 2005/01/27 16:31:50 mbuna Exp $"
31    
32     #ifdef HAVE_CONFIG_H
33     #include "config.h"
34     #endif
35    
36     #include <peak/tz.h>
37     #include <peak/alloc.h>
38     #include <peak/dict.h>
39     #include "fileutils.h"
40     #include "internal.h"
41    
42     #ifdef HAVE_ALLOCA_H
43     #include <alloca.h>
44     #endif
45     #include <assert.h>
46     #include <limits.h>
47     #include <stdlib.h>
48     #include <stdio.h>
49     #include <sys/types.h>
50     #include <sys/param.h>
51     #include <sys/time.h>
52     #include <string.h>
53     #include <unistd.h>
54     #ifdef HAVE_TZFILE_H
55     #include <tzfile.h>
56     #endif
57    
58     #ifndef HAVE_STRUCT_TZHEAD
59     struct tzhead
60     {
61     char tzh_reserved[24]; /* reserved for future use */
62     char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
63     char tzh_leapcnt[4]; /* coded number of leap seconds */
64     char tzh_timecnt[4]; /* coded number of transition times */
65     char tzh_typecnt[4]; /* coded number of local time types */
66     char tzh_charcnt[4]; /* coded number of abbr. chars */
67     };
68     #endif
69    
70     #ifndef TZDIR
71     #define TZDIR "/usr/share/zoneinfo"
72     #endif
73     #ifndef TZDEFAULT
74     #define TZDEFAULT "/etc/localtime"
75     #endif
76    
77     static const peak_dict_init_entry abbrev2name[] =
78     { { "ADT", "America/Halifax" },
79     { "AFT", "Asia/Kabul" },
80     { "AKDT", "America/Juneau" },
81     { "AKST", "America/Juneau" },
82     { "AST", "America/Halifax" },
83     { "CDT", "America/Chicago" },
84     { "CEST", "Europe/Rome" },
85     { "CET", "Europe/Rome" },
86     { "CST", "America/Chicago" },
87     { "EDT", "America/New_York" },
88     { "EEST", "Europe/Warsaw" },
89     { "EET", "Europe/Warsaw" },
90     { "EST", "America/New_York" },
91     { "GMT", "GMT" },
92     { "HKST", "Asia/Hong_Kong" },
93     { "HST", "Pacific/Honolulu" },
94     { "JST", "Asia/Tokyo" },
95     { "MDT", "America/Denver" },
96     { "MSD", "Europe/Moscow" },
97     { "MSK", "Europe/Moscow" },
98     { "MST", "America/Denver" },
99     { "NZDT", "Pacific/Auckland" },
100     { "NZST", "Pacific/Auckland" },
101     { "PDT", "America/Los_Angeles" },
102     { "PST", "America/Los_Angeles" },
103     { "UTC", "UTC" },
104     { "WEST", "Europe/Paris" },
105     { "WET", "Europe/Paris" },
106     { "YDT", "America/Yakutat" },
107     { "YST", "America/Yakutat" }
108     };
109    
110     #define NUM_ABBREV (sizeof(abbrev2name)/sizeof(peak_dict_init_entry))
111    
112     struct __peak_tz_period
113     {
114     time_t start;
115     const char *abbrev;
116     int32_t info; /* bit 30 stolen for DST - the rest is the offset */
117     };
118    
119     struct __peak_tz
120     {
121     PEAK_STRUCT_RT_HEADER;
122     const char *_name;
123     struct __peak_tz_period *_periods;
124     int _period_cnt;
125     };
126    
127    
128     static void __peak_tz_init(peak_tz tz, va_list vp, void *ctcx);
129     static void __peak_tz_finalize(peak_tz tz);
130     static struct __peak_tz_period * __peak_tz_bsearch_period(peak_tz tz,
131     time_t at);
132    
133     PEAK_CLASS_BASE_DECLARE(tz);
134    
135    
136     /* Helpers */
137    
138     static int32_t
139     __peak_tz_detzcode(const char * const codep)
140     {
141     int32_t result;
142     int i;
143    
144     result = (int32_t)((codep[0] & 0x80) ? ~0L : 0L);
145     for (i = 0; i < 4; ++i)
146     result = (result << 8) | (codep[i] & 0xff);
147     return result;
148     }
149    
150     void
151     __peak_tz_period_init(struct __peak_tz_period *period, int32_t start,
152     const char *abbrev, int32_t offset, int dst)
153     {
154     period->start = start;
155     period->abbrev = peak_strdup(abbrev);
156     period->info = abs(offset);
157    
158     if (offset < 0)
159     period->info |= (1 << 31);
160     if (dst)
161     period->info |= (1 << 30);
162     else
163     period->info &= ~(1 << 30);
164     }
165    
166     int
167     __peak_tz_period_compare(const void *v1, const void *v2)
168     {
169     struct __peak_tz_period *tzp1 = (struct __peak_tz_period *)v1;
170     struct __peak_tz_period *tzp2 = (struct __peak_tz_period *)v2;
171    
172     if (tzp1->start < tzp2->start)
173     return -1;
174     else
175     return (tzp1->start == tzp2->start) ? 0 : 1;
176     }
177    
178     peak_tz
179     peak_tz_create(const char *tz_name)
180     {
181     char path_buf[MAXPATHLEN+1];
182     void *data_buf;
183     long data_len;
184     const char *name = NULL;
185     peak_dict abbrevs;
186    
187     if (!tz_name)
188     return NULL;
189    
190     if (*tz_name != '/')
191     {
192     abbrevs = peak_dict_create(&peak_dict_string_key_ops,
193     &peak_dict_string_value_ops,
194     abbrev2name,
195     NUM_ABBREV);
196    
197     if (abbrevs)
198     {
199     name = (const char *)peak_dict_get_value(abbrevs, tz_name);
200     peak_release(abbrevs);
201     }
202     if (!name)
203     name = tz_name;
204     snprintf(path_buf, sizeof(path_buf), "%s/%s", TZDIR, name);
205     }
206     else
207     {
208     char *p;
209    
210     strncpy(path_buf, tz_name, sizeof(path_buf) - 1);
211     path_buf[sizeof(path_buf) - 1] = '\0';
212     name = ((p = strrchr(path_buf, '/'))) ? p + 1 : path_buf;
213     }
214    
215     if (!peak_read_file(path_buf, &data_buf, &data_len))
216     return NULL;
217    
218     return PEAK_CLASS_CONSTRUCT3(tz, name, data_buf, data_len);
219     }
220    
221     static void
222     __peak_tz_init(peak_tz tz, va_list vp, void *ctcx)
223     {
224     long len;
225     int i;
226     int32_t timecnt, typecnt, charcnt, cnt, start, offset;
227     const char *p, *timep, *typep, *ttypp, *charp;
228     char **abbrs;
229     uint8_t type, dst, idx;
230     struct __peak_tz_period *tzp;
231     int result = 1;
232    
233     tz->_name = peak_strdup(va_arg(vp, const char *));
234     p = va_arg(vp, void*);
235     len = va_arg(vp, long);
236    
237     if (len < (int)sizeof(struct tzhead))
238     PEAK_CT_RAISE("failed to read zone info: truncated tzhead", 0);
239    
240     p += 24 + 4 + 4; /* reserved, ttisstdcnt, leapcnt */
241    
242     timecnt = __peak_tz_detzcode(p);
243     p += 4;
244     typecnt = __peak_tz_detzcode(p);
245     p += 4;
246     charcnt = __peak_tz_detzcode(p);
247     p += 4;
248     if ((typecnt <= 0 || timecnt < 0 || charcnt < 0)
249     || (len - sizeof(struct tzhead) < (4 + 1) * timecnt
250     + (4 + 1 + 1) * typecnt
251     + charcnt))
252     PEAK_CT_RAISE("failed to read zone info: bad tzhead values", 0);
253    
254     assert(typecnt > 0);
255    
256     timep = p; /* coded transition times a la time(2) */
257     typep = p + 4 * timecnt; /* types of local time starting at above */
258     ttypp = typep + timecnt; /* local time types */
259     charp = ttypp + 6 * typecnt; /* 0-terminated zone abbreviations */
260     cnt = (0 < timecnt) ? timecnt : 1;
261     tzp = peak_allocate(cnt * sizeof(struct __peak_tz_period));
262     memset(tzp, 0, sizeof(cnt * sizeof(struct __peak_tz_period)));
263     #ifdef HAVE_ALLOCA
264     abbrs = (char **)alloca((charcnt + 1) * sizeof(char *));
265     #else
266     abbrs = (char **)peak_allocate((charcnt + 1) * sizeof(char *));
267     #endif
268     for (i = charcnt; i--; )
269     abbrs[i] = NULL;
270    
271     for (i = 0; i < cnt; i++)
272     {
273     if (0 == timecnt)
274     start = INT_MIN;
275     else
276     {
277     start = __peak_tz_detzcode(timep);
278     timep += 4;
279     }
280     type = (0 < timecnt) ? (uint8_t)*typep++ : 0;
281     if (typecnt <= type)
282     {
283     result = 0;
284     break;
285     }
286     offset = __peak_tz_detzcode(ttypp + 6 * type);
287     dst = (uint8_t)*(ttypp + 6 * type + 4);
288     if (dst != 0 && dst != 1)
289     {
290     result = 0;
291     break;
292     }
293     idx = (uint8_t)*(ttypp + 6 * type + 5);
294     if (charcnt < idx)
295     {
296     result = 0;
297     break;
298     }
299     if (!abbrs[idx])
300     abbrs[idx] = (char *)(charp + idx);
301    
302     __peak_tz_period_init(tzp + i, start, abbrs[idx], offset, dst);
303     }
304     #ifndef HAVE_ALLOCA
305     peak_deallocate(abbrs);
306     #endif
307     if (!result)
308     {
309     peak_deallocate(tzp);
310     PEAK_CT_RAISE("failed to read zone info: can't parse data", 0);
311     }
312    
313     /* cleanup */
314     for (i = 0; i < cnt; i++)
315     {
316     if ((tzp[i].start == INT_MIN)
317     && (i + 1 < cnt)
318     && (tzp[i + 1].start == INT_MIN))
319     {
320     cnt--;
321     memmove(&tzp[i], &tzp[i + 1],
322     sizeof(struct __peak_tz_period) * (cnt - i));
323     i--;
324     }
325     }
326     for (i = 0; i < cnt; i++)
327     {
328     if ((tzp[i].start == INT_MAX)
329     && (0 < i)
330     && (tzp[i - 1].start == INT_MAX))
331     {
332     cnt--;
333     memmove(&tzp[i], &tzp[i + 1],
334     sizeof(struct __peak_tz_period) * (cnt - i));
335     i--;
336     }
337     }
338     qsort(tzp, cnt, sizeof(struct __peak_tz_period), __peak_tz_period_compare);
339    
340     tz->_periods = tzp;
341     tz->_period_cnt = cnt;
342     }
343    
344     static void
345     __peak_tz_finalize(peak_tz tz)
346     {
347     /* XXX */
348     if (tz->_periods)
349     peak_deallocate(tz->_periods);
350    
351     peak_deallocate((void*)tz->_name);
352     }
353    
354     peak_tz
355     peak_tz_create_system()
356     {
357     char *tzenv;
358     peak_tz tz;
359     int res;
360     char *name;
361     char linkbuf[MAXPATHLEN+1];
362    
363     tzenv = getenv("TZFILE");
364     if (tzenv != NULL)
365     {
366     tz = peak_tz_create(tzenv);
367     if (tz)
368     return tz;
369     }
370    
371     tzenv = getenv("TZ");
372     if (tzenv != NULL)
373     {
374     tz = peak_tz_create(tzenv);
375     if (tz)
376     return tz;
377     }
378    
379     if ((res = readlink(TZDEFAULT, linkbuf, sizeof(linkbuf) - 1)) > 0)
380     {
381     linkbuf[res] = '\0';
382     if (strncmp(linkbuf, TZDIR, sizeof(TZDIR) - 1) == 0)
383     {
384     name = linkbuf + sizeof(TZDIR);
385     while (*name == '/')
386     name++;
387     }
388     else
389     name = linkbuf;
390    
391     if ((tz = peak_tz_create(name)))
392     return tz;
393     }
394     return peak_tz_create("GMT");
395     }
396    
397     const char *
398     peak_tz_get_name(peak_tz tz)
399     {
400     return tz->_name;
401     }
402    
403     static struct __peak_tz_period *
404     __peak_tz_bsearch_period(peak_tz tz, time_t t)
405     {
406     struct __peak_tz_period *base = tz->_periods;
407     struct __peak_tz_period *p;
408     struct __peak_tz_period *result = NULL;
409     size_t lim;
410    
411     for (lim = tz->_period_cnt; lim; lim >>= 1)
412     {
413     p = base + (lim >> 1);
414     if (t >= p->start) /* key >= p: move right */
415     {
416     result = p;
417     base = p + 1;
418     lim--;
419     } /* else move left */
420     }
421    
422     if (result == NULL)
423     {
424     int i;
425    
426     #if 1
427     fprintf(stderr, "libpeak: __peak_tz_bsearch_period failed\n");
428     #endif
429     for (i = 0; i < tz->_period_cnt && (tz->_periods[i].info & (1 << 31));
430     i++)
431     ;
432     if (i < tz->_period_cnt)
433     result = &tz->_periods[i];
434     else
435     result = &tz->_periods[0];
436     }
437     else
438     {
439     if (result > tz->_periods)
440     {
441     assert(result->start > (result - 1)->start);
442     }
443     if (result < (tz->_periods + tz->_period_cnt - 1))
444     {
445     assert(result->start < (result + 1)->start);
446     }
447     }
448     return result;
449     }
450    
451     const char *
452     peak_tz_get_abbreviation(peak_tz tz, time_t t)
453     {
454     struct __peak_tz_period *period;
455    
456     period = __peak_tz_bsearch_period(tz, t);
457     assert(period != NULL);
458    
459     return period->abbrev;
460     }
461    
462     time_t
463     peak_tz_get_gmt_offset(peak_tz tz, time_t t)
464     {
465     struct __peak_tz_period *period;
466     time_t result;
467    
468     period = __peak_tz_bsearch_period(tz, t);
469     assert(period != NULL);
470    
471     result = period->info & ~(3 << 30);
472     if (period->info & (1 << 31))
473     result = -result;
474     return result;
475     }
476    
477    
478     int
479     peak_tz_is_dst(peak_tz tz, time_t t)
480     {
481     struct __peak_tz_period *period;
482    
483     period = __peak_tz_bsearch_period(tz, t);
484     assert(period != NULL);
485    
486     return (period->info & (1 << 30));
487     }
488    
489