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.
25 * gcc -c -Wall -DDISABLE_ICALTZUTIL_GET_ZONE_DIRECTORY -DICALTZ_UTIL_MAIN -DHAVE_UNISTD_H -DHAVE_ENDIAN_H -DHAVE_BYTESWAP_H $(pkg-config --cflags --libs libical) -o icaltz-util icaltz-util.c
26 * to get an utility which will print the VTIMEZONE definition of
27 * a certain location, for example "Europe/Berlin".
29 * With -DDISABLE_ICALTZUTIL_GET_ZONE_DIRECTORY the
30 * icaltzutil_get_zone_directory() from libical will be used.
38 #ifdef ICALTZ_UTIL_MAIN
42 #ifdef EVOLUTION_ICAL_COMPATIBILITY
43 # include "eds_abi_wrapper.h"
45 # include <libical/icaltimezone.h>
56 #if defined(sun) && defined(__SVR4)
57 #include <sys/types.h>
58 #include <sys/byteorder.h>
60 # ifdef HAVE_BYTESWAP_H
61 # include <byteswap.h>
66 # ifdef HAVE_SYS_ENDIAN_H
67 # include <sys/endian.h>
69 # define bswap_32 bswap32
71 # define bswap_32 swap32
78 #if !defined(HAVE_BYTESWAP_H) && !defined(HAVE_SYS_ENDIAN_H) && !defined(HAVE_ENDIAN_H)
79 #define bswap_16(x) (((x) << 8) & 0xff00) | (((x) >> 8 ) & 0xff)
80 #define bswap_32(x) (((x) << 24) & 0xff000000) \
81 | (((x) << 8) & 0xff0000) \
82 | (((x) >> 8) & 0xff00) \
83 | (((x) >> 24) & 0xff )
84 #define bswap_64(x) ((((x) & 0xff00000000000000ull) >> 56) \
85 | (((x) & 0x00ff000000000000ull) >> 40) \
86 | (((x) & 0x0000ff0000000000ull) >> 24) \
87 | (((x) & 0x000000ff00000000ull) >> 8) \
88 | (((x) & 0x00000000ff000000ull) << 8) \
89 | (((x) & 0x0000000000ff0000ull) << 24) \
90 | (((x) & 0x000000000000ff00ull) << 40) \
91 | (((x) & 0x00000000000000ffull) << 56))
96 #if defined(__APPLE__)
97 #define bswap_16(x) (((x) << 8) & 0xff00) | (((x) >> 8 ) & 0xff)
98 #define bswap_32 __builtin_bswap32
99 #define bswap_64 __builtin_bswap64
120 #include <libical/icalerror.h>
121 #include "icaltz-util.h"
133 static int r_pos [] = {1, 2, 3, -2, -1};
135 #ifndef DISABLE_ICALTZUTIL_GET_ZONE_DIRECTORY
136 static char *search_paths [] = {"/usr/share/zoneinfo","/usr/lib/zoneinfo","/etc/zoneinfo","/usr/share/lib/zoneinfo"};
137 static char *zdir = NULL;
140 #define NUM_SEARCH_PATHS (sizeof (search_paths)/ sizeof (search_paths [0]))
141 #define EFREAD(buf,size,num,fs) \
142 if (fread (buf, size, num, fs) == 0 && ferror (fs)) {\
143 icalerror_set_errno (ICAL_FILE_ERROR); \
164 #ifndef EVOLUTION_ICAL_COMPATIBILITY
165 extern const char *ical_tzid_prefix;
169 decode (const void *ptr)
171 #if defined(sun) && defined(__SVR4)
172 if (sizeof (int) == 4)
174 return *(const int *) ptr;
176 return BSWAP_32 (*(const int *) ptr);
179 if ((BYTE_ORDER == BIG_ENDIAN) && sizeof (int) == 4)
180 return *(const int *) ptr;
181 else if (BYTE_ORDER == LITTLE_ENDIAN && sizeof (int) == 4)
182 return bswap_32 (*(const int *) ptr);
186 const unsigned char *p = ptr;
187 int result = *p & (1 << (CHAR_BIT - 1)) ? ~0 : 0;
189 result = (result << 8) | *p++;
190 result = (result << 8) | *p++;
191 result = (result << 8) | *p++;
192 result = (result << 8) | *p++;
199 zname_from_stridx (char *str, long int idx)
207 while (str [i] != '\0')
212 ret = (char *) malloc (size + 1);
213 ret = strncpy (ret, str, size);
220 find_transidx (time_t *transitions, ttinfo *types, int *trans_idx, long int num_trans, int *stdidx, int *dstidx)
222 time_t now, year_start;
224 struct icaltimetype itime;
227 itime = icaltime_from_timet (now, 0);
228 itime.month = itime.day = 1;
229 itime.hour = itime.minute = itime.second = 0;
230 year_start = icaltime_as_timet(itime);
232 /* Set this by default */
233 *stdidx = (num_trans - 1);
235 for (i = (num_trans - 1); i >= 0; --i)
236 if (year_start < transitions [i]) {
240 (types [idx].isdst) ? (*dstidx = i) : (*stdidx = i);
243 /* If the transition found is the last among the list, prepare to use the last two transtions.
244 * Using this will most likely throw the DTSTART of the resulting component off by 1 or 2 days
245 * but it would set right by the adjustment made.
246 * NOTE: We need to use the last two transitions only because there is no data for the future
249 if (found && (*dstidx == -1)) {
250 *dstidx = ((*stdidx) - 1);
256 #ifndef DISABLE_ICALTZUTIL_GET_ZONE_DIRECTORY
260 char file_path[PATH_MAX];
261 const char *fname = ZONES_TAB_SYSTEM_FILENAME;
264 for (i = 0;i < NUM_SEARCH_PATHS; i++) {
265 sprintf (file_path, "%s/%s", search_paths [i], fname);
266 if (!access (file_path, F_OK|R_OK)) {
267 zdir = search_paths [i];
275 icaltzutil_get_zone_directory (void)
284 /* Calculate the relative position of the week in a month from a date */
286 calculate_pos (icaltimetype icaltime)
290 pos = (icaltime.day -1) / 7;
292 /* Check if pos 3 is the last occurence of the week day in the month */
293 if (pos == 3 && ((icaltime.day + 7) > icaltime_days_in_month (icaltime.month, icaltime.year)))
300 adjust_dtstart_day_to_rrule (icalcomponent *comp, struct icalrecurrencetype rule)
302 time_t now, year_start;
303 struct icaltimetype start, comp_start, iter_start, itime;
304 icalrecur_iterator *iter;
307 itime = icaltime_from_timet (now, 0);
308 itime.month = itime.day = 1;
309 itime.hour = itime.minute = itime.second = 0;
310 year_start = icaltime_as_timet(itime);
312 comp_start = icalcomponent_get_dtstart (comp);
313 start = icaltime_from_timet (year_start, 0);
315 iter = icalrecur_iterator_new (rule, start);
316 iter_start = icalrecur_iterator_next (iter);
317 icalrecur_iterator_free (iter);
319 if (iter_start.day != comp_start.day) {
320 comp_start.day = iter_start.day;
321 icalcomponent_set_dtstart (comp, comp_start);
326 icaltzutil_fetch_timezone (const char *location)
331 unsigned int i, num_trans, num_types = 0, num_chars, num_leaps, num_isstd, num_isgmt;
332 time_t *transitions = NULL;
334 int *trans_idx = NULL, dstidx = -1, stdidx = -1, pos, sign, zidx, zp_idx;
335 ttinfo *types = NULL;
336 char *znames = NULL, *full_path, *tzid, *r_trans, *temp = NULL;
338 icalcomponent *tz_comp = NULL, *dst_comp = NULL, *std_comp = NULL;
339 icalproperty *icalprop;
340 icaltimetype dtstart, icaltime;
341 struct icalrecurrencetype ical_recur;
344 /* if (!location) return NULL; */
346 basedir = icaltzutil_get_zone_directory();
348 icalerror_set_errno (ICAL_FILE_ERROR);
352 full_path = (char *) malloc (strlen (basedir) + strlen (location) + 2);
353 sprintf (full_path,"%s/%s",basedir, location);
355 if ((f = fopen (full_path, "rb")) == 0) {
356 icalerror_set_errno (ICAL_FILE_ERROR);
361 if ((ret = fseek (f, 20, SEEK_SET)) != 0) {
362 icalerror_set_errno (ICAL_FILE_ERROR);
366 EFREAD(&type_cnts, 24, 1, f);
368 num_isgmt = decode (type_cnts.ttisgmtcnt);
369 num_leaps = decode (type_cnts.leapcnt);
370 num_chars = decode (type_cnts.charcnt);
371 num_trans = decode (type_cnts.timecnt);
372 num_isstd = decode (type_cnts.ttisstdcnt);
373 num_types = decode (type_cnts.typecnt);
375 transitions = calloc (num_trans, sizeof (time_t));
376 temp = calloc (num_trans, 4);
378 EFREAD(r_trans, 4, num_trans, f);
381 trans_idx = calloc (num_trans, sizeof (int));
382 for (i = 0; i < num_trans; i++) {
383 trans_idx [i] = fgetc (f);
384 transitions [i] = decode (r_trans);
392 types = calloc (num_types, sizeof (ttinfo));
393 for (i = 0; i < num_types; i++) {
400 if((c = fgetc (f)) < 0) {
405 types [i].gmtoff = decode (a);
408 znames = (char *) malloc (num_chars);
409 EFREAD(znames, num_chars, 1, f);
411 /* We got all the information which we need */
413 leaps = calloc (num_leaps, sizeof (leap));
414 for (i = 0; i < num_leaps; i++) {
418 leaps [i].transition = decode (c);
421 leaps [i].change = decode (c);
424 for (i = 0; i < num_isstd; ++i) {
426 types [i].isstd = c != 0;
429 while (i < num_types)
430 types [i++].isstd = 0;
432 for (i = 0; i < num_isgmt; ++i) {
434 types [i].isgmt = c != 0;
437 while (i < num_types)
438 types [i++].isgmt = 0;
440 /* Read all the contents now */
442 for (i = 0; i < num_types; i++)
443 types [i].zname = zname_from_stridx (znames, types [i].abbr);
446 find_transidx (transitions, types, trans_idx, num_trans, &stdidx, &dstidx);
450 tz_comp = icalcomponent_new (ICAL_VTIMEZONE_COMPONENT);
452 /* Add tzid property */
453 tzid = (char *) malloc (strlen (ical_tzid_prefix) + strlen (location) + 8);
454 sprintf (tzid, "%sTzfile/%s", ical_tzid_prefix, location);
455 icalprop = icalproperty_new_tzid (tzid);
456 icalcomponent_add_property (tz_comp, icalprop);
459 icalprop = icalproperty_new_x (location);
460 icalproperty_set_x_name (icalprop, "X-LIC-LOCATION");
461 icalcomponent_add_property (tz_comp, icalprop);
465 zidx = trans_idx [stdidx];
469 std_comp = icalcomponent_new (ICAL_XSTANDARD_COMPONENT);
470 icalprop = icalproperty_new_tzname (types [zidx].zname);
471 icalcomponent_add_property (std_comp, icalprop);
474 zp_idx = trans_idx [stdidx-1];
477 /* DTSTART localtime uses TZOFFSETFROM UTC offset */
479 trans = transitions [stdidx] + types [zp_idx].gmtoff;
481 trans = types [zp_idx].gmtoff;
482 icaltime = icaltime_from_timet (trans, 0);
485 dtstart.minute = dtstart.second = 0;
486 icalprop = icalproperty_new_dtstart (dtstart);
487 icalcomponent_add_property (std_comp, icalprop);
489 /* If DST changes are present use RRULE */
491 icalrecurrencetype_clear (&ical_recur);
492 ical_recur.freq = ICAL_YEARLY_RECURRENCE;
493 ical_recur.by_month [0] = icaltime.month;
494 pos = calculate_pos (icaltime);
495 pos < 0 ? (sign = -1): (sign = 1);
496 ical_recur.by_day [0] = sign * ((abs (pos) * 8) + icaltime_day_of_week (icaltime));
497 icalprop = icalproperty_new_rrule (ical_recur);
498 icalcomponent_add_property (std_comp, icalprop);
500 adjust_dtstart_day_to_rrule (std_comp, ical_recur);
502 icalprop = icalproperty_new_tzoffsetfrom (types [zp_idx].gmtoff);
503 icalcomponent_add_property (std_comp, icalprop);
505 icalprop = icalproperty_new_tzoffsetto (types [zidx].gmtoff);
506 icalcomponent_add_property (std_comp, icalprop);
508 icalcomponent_add_component (tz_comp, std_comp);
510 icalerror_set_errno (ICAL_MALFORMEDDATA_ERROR);
513 zidx = trans_idx [dstidx];
514 zp_idx = trans_idx [dstidx-1];
515 dst_comp = icalcomponent_new (ICAL_XDAYLIGHT_COMPONENT);
516 icalprop = icalproperty_new_tzname (types [zidx].zname);
517 icalcomponent_add_property (dst_comp, icalprop);
519 /* DTSTART localtime uses TZOFFSETFROM UTC offset */
521 trans = transitions [dstidx] + types [zp_idx].gmtoff;
523 trans = types [zp_idx].gmtoff;
524 icaltime = icaltime_from_timet (trans, 0);
527 dtstart.minute = dtstart.second = 0;
528 icalprop = icalproperty_new_dtstart (dtstart);
529 icalcomponent_add_property (dst_comp, icalprop);
531 icalrecurrencetype_clear (&ical_recur);
532 ical_recur.freq = ICAL_YEARLY_RECURRENCE;
533 ical_recur.by_month [0] = icaltime.month;
534 pos = calculate_pos (icaltime);
535 pos < 0 ? (sign = -1): (sign = 1);
536 ical_recur.by_day [0] = sign * ((abs (pos) * 8) + icaltime_day_of_week (icaltime));
537 icalprop = icalproperty_new_rrule (ical_recur);
538 icalcomponent_add_property (dst_comp, icalprop);
540 adjust_dtstart_day_to_rrule (dst_comp, ical_recur);
542 icalprop = icalproperty_new_tzoffsetfrom (types [zp_idx].gmtoff);
543 icalcomponent_add_property (dst_comp, icalprop);
545 icalprop = icalproperty_new_tzoffsetto (types [zidx].gmtoff);
546 icalcomponent_add_property (dst_comp, icalprop);
548 icalcomponent_add_component (tz_comp, dst_comp);
560 for (i = 0; i < num_types; i++)
562 free (types [i].zname);
577 * What follows is copied and slightly simplified (not thread-safe!)
578 * code from icaltimezone.c.
580 * This is necessary because otherwise, when libsynthesis.so calls
581 * icaltimezone_get_component(), libical.1.so uses its own builtin
582 * icaltzutil_fetch_timezone(). Apparently it exports that without
583 * looking it up via the dynamic linker itself. Therefore we have to
584 * redirect the original icaltimezone_get_component() call.
588 icaltimezone_load_builtin_timezone (icaltimezone *zone)
590 icalcomponent *subcomp;
591 const char *location;
593 /* If the location isn't set, it isn't a builtin timezone. */
594 location = icaltimezone_get_location(zone);
595 if (!location || !location[0])
598 subcomp = icaltzutil_fetch_timezone (location);
601 icalerror_set_errno(ICAL_PARSE_ERROR);
605 icaltimezone_set_component(zone, subcomp);
608 #undef icaltimezone_get_component
609 icalcomponent *icaltimezone_get_component(icaltimezone *zone)
612 /* If this is a floating time, without a timezone, return NULL. */
617 * Without this check, icaltimezone_set_component() in
618 * icaltimezone_load_builtin_timezone() will discard the
619 * already loaded component of builtin timezones and replace
620 * it with the new one, so there is no leak. It's just
623 * However, this method also gets called for non-internal
624 * timezones which were created from a VTIMEZONE and in
625 * that case not using the existing component is wrong.
627 * Hack: duplicate the internal _icaltimezone struct (from
628 * icaltimezoneimpl.h).
630 struct _my_icaltimezone {
636 icalcomponent *component;
638 comp = ((struct _my_icaltimezone *)zone)->component;
640 icaltimezone_load_builtin_timezone (zone);
641 comp = ((struct _my_icaltimezone *)zone)->component;
648 * For including the .o file in binaries via -Wl,-usyncevo_fetch_timezone.
649 * We cannot use -Wl,-uicaltzutil_fetch_timezone because that gets satisfied by
652 int syncevo_fetch_timezone;
655 * Avoid lazy resolution of the methods that we export. client-test otherwise
656 * ends up calling the libical version of the methods despite having its own
657 * copy compiled into the executable, at least on Ubuntu Saucy and Trusty.
659 /* void *syncevo_fetch_timezone_p = &icaltzutil_fetch_timezone; */
660 /* void *syncevo_get_component_p = &icaltimezone_get_component; */
662 #ifdef ICALTZ_UTIL_MAIN
664 main (int argc, char *argv [])
666 icalcomponent *comp = icaltzutil_fetch_timezone (argv [1]);
667 const char *str = comp ? icalcomponent_as_ical_string(comp) : "no such zone";