1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
4 * Chenthill Palanisamy <pchenthill@novell.com>
6 * Copyright 2007, Novell, Inc.
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of version 2 of the GNU Lesser General Public
10 * License as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
17 * * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
32 #if defined(sun) && defined(__SVR4)
33 #include <sys/types.h>
34 #include <sys/byteorder.h>
36 # ifdef HAVE_BYTESWAP_H
37 # include <byteswap.h>
42 # ifdef HAVE_SYS_ENDIAN_H
43 # include <sys/endian.h>
45 # define bswap_32 bswap32
47 # define bswap_32 swap32
54 #if !defined(HAVE_BYTESWAP_H) && !defined(HAVE_SYS_ENDIAN_H) && !defined(HAVE_ENDIAN_H)
55 #define bswap_16(x) (((x) << 8) & 0xff00) | (((x) >> 8 ) & 0xff)
56 #define bswap_32(x) (((x) << 24) & 0xff000000) \
57 | (((x) << 8) & 0xff0000) \
58 | (((x) >> 8) & 0xff00) \
59 | (((x) >> 24) & 0xff )
60 #define bswap_64(x) ((((x) & 0xff00000000000000ull) >> 56) \
61 | (((x) & 0x00ff000000000000ull) >> 40) \
62 | (((x) & 0x0000ff0000000000ull) >> 24) \
63 | (((x) & 0x000000ff00000000ull) >> 8) \
64 | (((x) & 0x00000000ff000000ull) << 8) \
65 | (((x) & 0x0000000000ff0000ull) << 24) \
66 | (((x) & 0x000000000000ff00ull) << 40) \
67 | (((x) & 0x00000000000000ffull) << 56))
72 #if defined(__APPLE__)
73 #define bswap_16(x) (((x) << 8) & 0xff00) | (((x) >> 8 ) & 0xff)
74 #define bswap_32 __builtin_bswap32
75 #define bswap_64 __builtin_bswap64
96 #include <libical/icalerror.h>
97 #include <icaltz-util.h>
109 static int r_pos [] = {1, 2, 3, -2, -1};
111 static char *search_paths [] = {"/usr/share/zoneinfo","/usr/lib/zoneinfo","/etc/zoneinfo","/usr/share/lib/zoneinfo"};
112 static char *zdir = NULL;
114 #define NUM_SEARCH_PATHS (sizeof (search_paths)/ sizeof (search_paths [0]))
115 #define EFREAD(buf,size,num,fs) \
116 if (fread (buf, size, num, fs) == 0 && ferror (fs)) {\
117 icalerror_set_errno (ICAL_FILE_ERROR); \
138 extern const char *ical_tzid_prefix;
141 decode (const void *ptr)
143 #if defined(sun) && defined(__SVR4)
144 if (sizeof (int) == 4)
146 return *(const int *) ptr;
148 return BSWAP_32 (*(const int *) ptr);
151 if ((BYTE_ORDER == BIG_ENDIAN) && sizeof (int) == 4)
152 return *(const int *) ptr;
153 else if (BYTE_ORDER == LITTLE_ENDIAN && sizeof (int) == 4)
154 return bswap_32 (*(const int *) ptr);
158 const unsigned char *p = ptr;
159 int result = *p & (1 << (CHAR_BIT - 1)) ? ~0 : 0;
161 result = (result << 8) | *p++;
162 result = (result << 8) | *p++;
163 result = (result << 8) | *p++;
164 result = (result << 8) | *p++;
171 zname_from_stridx (char *str, long int idx)
179 while (str [i] != '\0')
184 ret = (char *) malloc (size + 1);
185 ret = strncpy (ret, str, size);
192 find_transidx (time_t *transitions, ttinfo *types, int *trans_idx, long int num_trans, int *stdidx, int *dstidx)
194 time_t now, year_start;
196 struct icaltimetype itime;
199 itime = icaltime_from_timet (now, 0);
200 itime.month = itime.day = 1;
201 itime.hour = itime.minute = itime.second = 0;
202 year_start = icaltime_as_timet(itime);
204 /* Set this by default */
205 *stdidx = (num_trans - 1);
207 for (i = (num_trans - 1); i >= 0; --i)
208 if (year_start < transitions [i]) {
212 (types [idx].isdst) ? (*dstidx = i) : (*stdidx = i);
215 /* If the transition found is the last among the list, prepare to use the last two transtions.
216 * Using this will most likely throw the DTSTART of the resulting component off by 1 or 2 days
217 * but it would set right by the adjustment made.
218 * NOTE: We need to use the last two transitions only because there is no data for the future
221 if (found && (*dstidx == -1)) {
222 *dstidx = ((*stdidx) - 1);
231 char file_path[PATH_MAX];
232 const char *fname = ZONES_TAB_SYSTEM_FILENAME;
235 for (i = 0;i < NUM_SEARCH_PATHS; i++) {
236 sprintf (file_path, "%s/%s", search_paths [i], fname);
237 if (!access (file_path, F_OK|R_OK)) {
238 zdir = search_paths [i];
246 icaltzutil_get_zone_directory (void)
254 /* Calculate the relative position of the week in a month from a date */
256 calculate_pos (icaltimetype icaltime)
260 pos = (icaltime.day -1) / 7;
262 /* Check if pos 3 is the last occurence of the week day in the month */
263 if (pos == 3 && ((icaltime.day + 7) > icaltime_days_in_month (icaltime.month, icaltime.year)))
270 adjust_dtstart_day_to_rrule (icalcomponent *comp, struct icalrecurrencetype rule)
272 time_t now, year_start;
273 struct icaltimetype start, comp_start, iter_start, itime;
274 icalrecur_iterator *iter;
277 itime = icaltime_from_timet (now, 0);
278 itime.month = itime.day = 1;
279 itime.hour = itime.minute = itime.second = 0;
280 year_start = icaltime_as_timet(itime);
282 comp_start = icalcomponent_get_dtstart (comp);
283 start = icaltime_from_timet (year_start, 0);
285 iter = icalrecur_iterator_new (rule, start);
286 iter_start = icalrecur_iterator_next (iter);
287 icalrecur_iterator_free (iter);
289 if (iter_start.day != comp_start.day) {
290 comp_start.day = iter_start.day;
291 icalcomponent_set_dtstart (comp, comp_start);
296 icaltzutil_fetch_timezone (const char *location)
301 unsigned int i, num_trans, num_types, num_chars, num_leaps, num_isstd, num_isgmt;
302 time_t *transitions = NULL;
304 int *trans_idx = NULL, dstidx = -1, stdidx = -1, pos, sign, zidx, zp_idx;
305 ttinfo *types = NULL;
306 char *znames = NULL, *full_path, *tzid, *r_trans, *temp;
308 icalcomponent *tz_comp = NULL, *dst_comp = NULL, *std_comp = NULL;
309 icalproperty *icalprop;
310 icaltimetype dtstart, icaltime;
311 struct icalrecurrencetype ical_recur;
314 basedir = icaltzutil_get_zone_directory();
316 icalerror_set_errno (ICAL_FILE_ERROR);
320 full_path = (char *) malloc (strlen (basedir) + strlen (location) + 2);
321 sprintf (full_path,"%s/%s",basedir, location);
323 if ((f = fopen (full_path, "rb")) == 0) {
324 icalerror_set_errno (ICAL_FILE_ERROR);
329 if ((ret = fseek (f, 20, SEEK_SET)) != 0) {
330 icalerror_set_errno (ICAL_FILE_ERROR);
334 EFREAD(&type_cnts, 24, 1, f);
336 num_isgmt = decode (type_cnts.ttisgmtcnt);
337 num_leaps = decode (type_cnts.leapcnt);
338 num_chars = decode (type_cnts.charcnt);
339 num_trans = decode (type_cnts.timecnt);
340 num_isstd = decode (type_cnts.ttisstdcnt);
341 num_types = decode (type_cnts.typecnt);
343 transitions = calloc (num_trans, sizeof (time_t));
344 r_trans = calloc (num_trans, 4);
345 EFREAD(r_trans, 4, num_trans, f);
349 trans_idx = calloc (num_trans, sizeof (int));
350 for (i = 0; i < num_trans; i++) {
351 trans_idx [i] = fgetc (f);
352 transitions [i] = decode (r_trans);
359 types = calloc (num_types, sizeof (ttinfo));
360 for (i = 0; i < num_types; i++) {
367 if((c = fgetc (f)) < 0) {
372 types [i].gmtoff = decode (a);
375 znames = (char *) malloc (num_chars);
376 EFREAD(znames, num_chars, 1, f);
378 /* We got all the information which we need */
380 leaps = calloc (num_leaps, sizeof (leap));
381 for (i = 0; i < num_leaps; i++) {
385 leaps [i].transition = decode (c);
388 leaps [i].change = decode (c);
391 for (i = 0; i < num_isstd; ++i) {
393 types [i].isstd = c != 0;
396 while (i < num_types)
397 types [i++].isstd = 0;
399 for (i = 0; i < num_isgmt; ++i) {
401 types [i].isgmt = c != 0;
404 while (i < num_types)
405 types [i++].isgmt = 0;
407 /* Read all the contents now */
409 for (i = 0; i < num_types; i++)
410 types [i].zname = zname_from_stridx (znames, types [i].abbr);
413 find_transidx (transitions, types, trans_idx, num_trans, &stdidx, &dstidx);
417 tz_comp = icalcomponent_new (ICAL_VTIMEZONE_COMPONENT);
419 /* Add tzid property */
420 tzid = (char *) malloc (strlen (ical_tzid_prefix) + strlen (location) + 8);
421 sprintf (tzid, "%sTzfile/%s", ical_tzid_prefix, location);
422 icalprop = icalproperty_new_tzid (tzid);
423 icalcomponent_add_property (tz_comp, icalprop);
426 icalprop = icalproperty_new_x (location);
427 icalproperty_set_x_name (icalprop, "X-LIC-LOCATION");
428 icalcomponent_add_property (tz_comp, icalprop);
432 zidx = trans_idx [stdidx];
436 std_comp = icalcomponent_new (ICAL_XSTANDARD_COMPONENT);
437 icalprop = icalproperty_new_tzname (types [zidx].zname);
438 icalcomponent_add_property (std_comp, icalprop);
441 zp_idx = trans_idx [stdidx-1];
444 /* DTSTART localtime uses TZOFFSETFROM UTC offset */
446 trans = transitions [stdidx] + types [zp_idx].gmtoff;
448 trans = types [zp_idx].gmtoff;
449 icaltime = icaltime_from_timet (trans, 0);
452 dtstart.minute = dtstart.second = 0;
453 icalprop = icalproperty_new_dtstart (dtstart);
454 icalcomponent_add_property (std_comp, icalprop);
456 /* If DST changes are present use RRULE */
458 icalrecurrencetype_clear (&ical_recur);
459 ical_recur.freq = ICAL_YEARLY_RECURRENCE;
460 ical_recur.by_month [0] = icaltime.month;
461 pos = calculate_pos (icaltime);
462 pos < 0 ? (sign = -1): (sign = 1);
463 ical_recur.by_day [0] = sign * ((abs (pos) * 8) + icaltime_day_of_week (icaltime));
464 icalprop = icalproperty_new_rrule (ical_recur);
465 icalcomponent_add_property (std_comp, icalprop);
467 adjust_dtstart_day_to_rrule (std_comp, ical_recur);
469 icalprop = icalproperty_new_tzoffsetfrom (types [zp_idx].gmtoff);
470 icalcomponent_add_property (std_comp, icalprop);
472 icalprop = icalproperty_new_tzoffsetto (types [zidx].gmtoff);
473 icalcomponent_add_property (std_comp, icalprop);
475 icalcomponent_add_component (tz_comp, std_comp);
477 icalerror_set_errno (ICAL_MALFORMEDDATA_ERROR);
480 zidx = trans_idx [dstidx];
481 zp_idx = trans_idx [dstidx-1];
482 dst_comp = icalcomponent_new (ICAL_XDAYLIGHT_COMPONENT);
483 icalprop = icalproperty_new_tzname (types [zidx].zname);
484 icalcomponent_add_property (dst_comp, icalprop);
486 /* DTSTART localtime uses TZOFFSETFROM UTC offset */
488 trans = transitions [dstidx] + types [zp_idx].gmtoff;
490 trans = types [zp_idx].gmtoff;
491 icaltime = icaltime_from_timet (trans, 0);
494 dtstart.minute = dtstart.second = 0;
495 icalprop = icalproperty_new_dtstart (dtstart);
496 icalcomponent_add_property (dst_comp, icalprop);
498 icalrecurrencetype_clear (&ical_recur);
499 ical_recur.freq = ICAL_YEARLY_RECURRENCE;
500 ical_recur.by_month [0] = icaltime.month;
501 pos = calculate_pos (icaltime);
502 pos < 0 ? (sign = -1): (sign = 1);
503 ical_recur.by_day [0] = sign * ((abs (pos) * 8) + icaltime_day_of_week (icaltime));
504 icalprop = icalproperty_new_rrule (ical_recur);
505 icalcomponent_add_property (dst_comp, icalprop);
507 adjust_dtstart_day_to_rrule (dst_comp, ical_recur);
509 icalprop = icalproperty_new_tzoffsetfrom (types [zp_idx].gmtoff);
510 icalcomponent_add_property (dst_comp, icalprop);
512 icalprop = icalproperty_new_tzoffsetto (types [zidx].gmtoff);
513 icalcomponent_add_property (dst_comp, icalprop);
515 icalcomponent_add_component (tz_comp, dst_comp);
527 for (i = 0; i < num_types; i++)
529 free (types [i].zname);
543 main (int argc, char *argv [])
545 tzutil_fetch_timezone (argv [1]);