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

# Content
1 /* 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