summaryrefslogtreecommitdiff
path: root/src/libical/icaltz-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libical/icaltz-util.c')
-rw-r--r--src/libical/icaltz-util.c547
1 files changed, 547 insertions, 0 deletions
diff --git a/src/libical/icaltz-util.c b/src/libical/icaltz-util.c
new file mode 100644
index 00000000..4c192139
--- /dev/null
+++ b/src/libical/icaltz-util.c
@@ -0,0 +1,547 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors :
+ * Chenthill Palanisamy <pchenthill@novell.com>
+ *
+ * Copyright 2007, Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <string.h>
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#if defined(sun) && defined(__SVR4)
+#include <sys/types.h>
+#include <sys/byteorder.h>
+#else
+# ifdef HAVE_BYTESWAP_H
+# include <byteswap.h>
+# endif
+# ifdef HAVE_ENDIAN_H
+# include <endian.h>
+# else
+# ifdef HAVE_SYS_ENDIAN_H
+# include <sys/endian.h>
+# ifdef bswap32
+# define bswap_32 bswap32
+# else
+# define bswap_32 swap32
+# endif
+# endif
+# endif
+#endif
+
+#ifdef _MSC_VER
+#if !defined(HAVE_BYTESWAP_H) && !defined(HAVE_SYS_ENDIAN_H) && !defined(HAVE_ENDIAN_H)
+#define bswap_16(x) (((x) << 8) & 0xff00) | (((x) >> 8 ) & 0xff)
+#define bswap_32(x) (((x) << 24) & 0xff000000) \
+ | (((x) << 8) & 0xff0000) \
+ | (((x) >> 8) & 0xff00) \
+ | (((x) >> 24) & 0xff )
+#define bswap_64(x) ((((x) & 0xff00000000000000ull) >> 56) \
+ | (((x) & 0x00ff000000000000ull) >> 40) \
+ | (((x) & 0x0000ff0000000000ull) >> 24) \
+ | (((x) & 0x000000ff00000000ull) >> 8) \
+ | (((x) & 0x00000000ff000000ull) << 8) \
+ | (((x) & 0x0000000000ff0000ull) << 24) \
+ | (((x) & 0x000000000000ff00ull) << 40) \
+ | (((x) & 0x00000000000000ffull) << 56))
+#endif
+#include <io.h>
+#endif
+
+#if defined(__APPLE__)
+#define bswap_16(x) (((x) << 8) & 0xff00) | (((x) >> 8 ) & 0xff)
+#define bswap_32 __builtin_bswap32
+#define bswap_64 __builtin_bswap64
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX 512
+#endif
+
+#ifndef F_OK
+#define F_OK 0
+#endif
+
+#ifndef R_OK
+#define R_OK 4
+#endif
+
+#include <limits.h>
+#include <time.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <libical/icalerror.h>
+#include <icaltz-util.h>
+
+typedef struct
+{
+ char ttisgmtcnt [4];
+ char ttisstdcnt[4];
+ char leapcnt[4];
+ char timecnt[4];
+ char typecnt[4];
+ char charcnt[4];
+} tzinfo;
+
+static int r_pos [] = {1, 2, 3, -2, -1};
+
+static char *search_paths [] = {"/usr/share/zoneinfo","/usr/lib/zoneinfo","/etc/zoneinfo","/usr/share/lib/zoneinfo"};
+static char *zdir = NULL;
+
+#define NUM_SEARCH_PATHS (sizeof (search_paths)/ sizeof (search_paths [0]))
+#define EFREAD(buf,size,num,fs) \
+ if (fread (buf, size, num, fs) == 0 && ferror (fs)) {\
+ icalerror_set_errno (ICAL_FILE_ERROR); \
+ goto error; \
+ } \
+
+typedef struct
+{
+ long int gmtoff;
+ unsigned char isdst;
+ unsigned int abbr;
+ unsigned char isstd;
+ unsigned char isgmt;
+ char *zname;
+
+} ttinfo;
+
+typedef struct
+{
+ time_t transition;
+ long int change;
+} leap;
+
+extern const char *ical_tzid_prefix;
+
+static int
+decode (const void *ptr)
+{
+#if defined(sun) && defined(__SVR4)
+ if (sizeof (int) == 4)
+#ifdef _BIG_ENDIAN
+ return *(const int *) ptr;
+#else
+ return BSWAP_32 (*(const int *) ptr);
+#endif
+#else
+ if ((BYTE_ORDER == BIG_ENDIAN) && sizeof (int) == 4)
+ return *(const int *) ptr;
+ else if (BYTE_ORDER == LITTLE_ENDIAN && sizeof (int) == 4)
+ return bswap_32 (*(const int *) ptr);
+#endif
+ else
+ {
+ const unsigned char *p = ptr;
+ int result = *p & (1 << (CHAR_BIT - 1)) ? ~0 : 0;
+
+ result = (result << 8) | *p++;
+ result = (result << 8) | *p++;
+ result = (result << 8) | *p++;
+ result = (result << 8) | *p++;
+
+ return result;
+ }
+}
+
+static char *
+zname_from_stridx (char *str, long int idx)
+{
+ int i = 0;
+ char *ret;
+ size_t size;
+
+ i = idx;
+
+ while (str [i] != '\0')
+ i++;
+
+ size = i - idx;
+ str += idx;
+ ret = (char *) malloc (size + 1);
+ ret = strncpy (ret, str, size);
+ ret [size] = '\0';
+
+ return ret;
+}
+
+static void
+find_transidx (time_t *transitions, ttinfo *types, int *trans_idx, long int num_trans, int *stdidx, int *dstidx)
+{
+ time_t now, year_start;
+ int i, found = 0;
+ struct icaltimetype itime;
+
+ now = time (NULL);
+ itime = icaltime_from_timet (now, 0);
+ itime.month = itime.day = 1;
+ itime.hour = itime.minute = itime.second = 0;
+ year_start = icaltime_as_timet(itime);
+
+ /* Set this by default */
+ *stdidx = (num_trans - 1);
+
+ for (i = (num_trans - 1); i >= 0; --i)
+ if (year_start < transitions [i]) {
+ int idx;
+ found = 1;
+ idx = trans_idx [i];
+ (types [idx].isdst) ? (*dstidx = i) : (*stdidx = i);
+ }
+
+ /* If the transition found is the last among the list, prepare to use the last two transtions.
+ * Using this will most likely throw the DTSTART of the resulting component off by 1 or 2 days
+ * but it would set right by the adjustment made.
+ * NOTE: We need to use the last two transitions only because there is no data for the future
+ * transitions.
+ */
+ if (found && (*dstidx == -1)) {
+ *dstidx = ((*stdidx) - 1);
+ }
+
+ return;
+}
+
+static void
+set_zonedir (void)
+{
+ char file_path[PATH_MAX];
+ const char *fname = ZONES_TAB_SYSTEM_FILENAME;
+ int i;
+
+ for (i = 0;i < NUM_SEARCH_PATHS; i++) {
+ sprintf (file_path, "%s/%s", search_paths [i], fname);
+ if (!access (file_path, F_OK|R_OK)) {
+ zdir = search_paths [i];
+ break;
+ }
+ }
+}
+
+
+const char *
+icaltzutil_get_zone_directory (void)
+{
+ if (!zdir)
+ set_zonedir ();
+
+ return zdir;
+}
+
+/* Calculate the relative position of the week in a month from a date */
+static int
+calculate_pos (icaltimetype icaltime)
+{
+ int pos;
+
+ pos = (icaltime.day -1) / 7;
+
+ /* Check if pos 3 is the last occurence of the week day in the month */
+ if (pos == 3 && ((icaltime.day + 7) > icaltime_days_in_month (icaltime.month, icaltime.year)))
+ pos = 4;
+
+ return r_pos [pos];
+}
+
+static void
+adjust_dtstart_day_to_rrule (icalcomponent *comp, struct icalrecurrencetype rule)
+{
+ time_t now, year_start;
+ struct icaltimetype start, comp_start, iter_start, itime;
+ icalrecur_iterator *iter;
+
+ now = time (NULL);
+ itime = icaltime_from_timet (now, 0);
+ itime.month = itime.day = 1;
+ itime.hour = itime.minute = itime.second = 0;
+ year_start = icaltime_as_timet(itime);
+
+ comp_start = icalcomponent_get_dtstart (comp);
+ start = icaltime_from_timet (year_start, 0);
+
+ iter = icalrecur_iterator_new (rule, start);
+ iter_start = icalrecur_iterator_next (iter);
+ icalrecur_iterator_free (iter);
+
+ if (iter_start.day != comp_start.day) {
+ comp_start.day = iter_start.day;
+ icalcomponent_set_dtstart (comp, comp_start);
+ }
+}
+
+icalcomponent*
+icaltzutil_fetch_timezone (const char *location)
+{
+ int ret = 0;
+ FILE *f;
+ tzinfo type_cnts;
+ unsigned int i, num_trans, num_types, num_chars, num_leaps, num_isstd, num_isgmt;
+ time_t *transitions = NULL;
+ time_t trans;
+ int *trans_idx = NULL, dstidx = -1, stdidx = -1, pos, sign, zidx, zp_idx;
+ ttinfo *types = NULL;
+ char *znames = NULL, *full_path, *tzid, *r_trans, *temp;
+ leap *leaps = NULL;
+ icalcomponent *tz_comp = NULL, *dst_comp = NULL, *std_comp = NULL;
+ icalproperty *icalprop;
+ icaltimetype dtstart, icaltime;
+ struct icalrecurrencetype ical_recur;
+ const char *basedir;
+
+ basedir = icaltzutil_get_zone_directory();
+ if (!basedir) {
+ icalerror_set_errno (ICAL_FILE_ERROR);
+ return NULL;
+ }
+
+ full_path = (char *) malloc (strlen (basedir) + strlen (location) + 2);
+ sprintf (full_path,"%s/%s",basedir, location);
+
+ if ((f = fopen (full_path, "rb")) == 0) {
+ icalerror_set_errno (ICAL_FILE_ERROR);
+ free (full_path);
+ return NULL;
+ }
+
+ if ((ret = fseek (f, 20, SEEK_SET)) != 0) {
+ icalerror_set_errno (ICAL_FILE_ERROR);
+ goto error;
+ }
+
+ EFREAD(&type_cnts, 24, 1, f);
+
+ num_isgmt = decode (type_cnts.ttisgmtcnt);
+ num_leaps = decode (type_cnts.leapcnt);
+ num_chars = decode (type_cnts.charcnt);
+ num_trans = decode (type_cnts.timecnt);
+ num_isstd = decode (type_cnts.ttisstdcnt);
+ num_types = decode (type_cnts.typecnt);
+
+ transitions = calloc (num_trans, sizeof (time_t));
+ r_trans = calloc (num_trans, 4);
+ EFREAD(r_trans, 4, num_trans, f);
+ temp = r_trans;
+
+ if (num_trans) {
+ trans_idx = calloc (num_trans, sizeof (int));
+ for (i = 0; i < num_trans; i++) {
+ trans_idx [i] = fgetc (f);
+ transitions [i] = decode (r_trans);
+ r_trans += 4;
+ }
+ }
+
+ free (temp);
+
+ types = calloc (num_types, sizeof (ttinfo));
+ for (i = 0; i < num_types; i++) {
+ unsigned char a [4];
+ int c;
+
+ EFREAD(a, 4, 1, f);
+ c = fgetc (f);
+ types [i].isdst = c;
+ if((c = fgetc (f)) < 0) {
+ c = 0;
+ break;
+ }
+ types [i].abbr = c;
+ types [i].gmtoff = decode (a);
+ }
+
+ znames = (char *) malloc (num_chars);
+ EFREAD(znames, num_chars, 1, f);
+
+ /* We got all the information which we need */
+
+ leaps = calloc (num_leaps, sizeof (leap));
+ for (i = 0; i < num_leaps; i++) {
+ char c [4];
+
+ EFREAD (c, 4, 1, f);
+ leaps [i].transition = decode (c);
+
+ EFREAD (c, 4, 1, f);
+ leaps [i].change = decode (c);
+ }
+
+ for (i = 0; i < num_isstd; ++i) {
+ int c = getc (f);
+ types [i].isstd = c != 0;
+ }
+
+ while (i < num_types)
+ types [i++].isstd = 0;
+
+ for (i = 0; i < num_isgmt; ++i) {
+ int c = getc (f);
+ types [i].isgmt = c != 0;
+ }
+
+ while (i < num_types)
+ types [i++].isgmt = 0;
+
+ /* Read all the contents now */
+
+ for (i = 0; i < num_types; i++)
+ types [i].zname = zname_from_stridx (znames, types [i].abbr);
+
+ if (num_trans != 0)
+ find_transidx (transitions, types, trans_idx, num_trans, &stdidx, &dstidx);
+ else
+ stdidx = 0;
+
+ tz_comp = icalcomponent_new (ICAL_VTIMEZONE_COMPONENT);
+
+ /* Add tzid property */
+ tzid = (char *) malloc (strlen (ical_tzid_prefix) + strlen (location) + 8);
+ sprintf (tzid, "%sTzfile/%s", ical_tzid_prefix, location);
+ icalprop = icalproperty_new_tzid (tzid);
+ icalcomponent_add_property (tz_comp, icalprop);
+ free (tzid);
+
+ icalprop = icalproperty_new_x (location);
+ icalproperty_set_x_name (icalprop, "X-LIC-LOCATION");
+ icalcomponent_add_property (tz_comp, icalprop);
+
+ if (stdidx != -1) {
+ if (num_trans != 0)
+ zidx = trans_idx [stdidx];
+ else
+ zidx = 0;
+
+ std_comp = icalcomponent_new (ICAL_XSTANDARD_COMPONENT);
+ icalprop = icalproperty_new_tzname (types [zidx].zname);
+ icalcomponent_add_property (std_comp, icalprop);
+
+ if (dstidx != -1)
+ zp_idx = trans_idx [stdidx-1];
+ else
+ zp_idx = zidx;
+ /* DTSTART localtime uses TZOFFSETFROM UTC offset */
+ if (num_trans != 0)
+ trans = transitions [stdidx] + types [zp_idx].gmtoff;
+ else
+ trans = types [zp_idx].gmtoff;
+ icaltime = icaltime_from_timet (trans, 0);
+ dtstart = icaltime;
+ dtstart.year = 1970;
+ dtstart.minute = dtstart.second = 0;
+ icalprop = icalproperty_new_dtstart (dtstart);
+ icalcomponent_add_property (std_comp, icalprop);
+
+ /* If DST changes are present use RRULE */
+ if (dstidx != -1) {
+ icalrecurrencetype_clear (&ical_recur);
+ ical_recur.freq = ICAL_YEARLY_RECURRENCE;
+ ical_recur.by_month [0] = icaltime.month;
+ pos = calculate_pos (icaltime);
+ pos < 0 ? (sign = -1): (sign = 1);
+ ical_recur.by_day [0] = sign * ((abs (pos) * 8) + icaltime_day_of_week (icaltime));
+ icalprop = icalproperty_new_rrule (ical_recur);
+ icalcomponent_add_property (std_comp, icalprop);
+
+ adjust_dtstart_day_to_rrule (std_comp, ical_recur);
+ }
+ icalprop = icalproperty_new_tzoffsetfrom (types [zp_idx].gmtoff);
+ icalcomponent_add_property (std_comp, icalprop);
+
+ icalprop = icalproperty_new_tzoffsetto (types [zidx].gmtoff);
+ icalcomponent_add_property (std_comp, icalprop);
+
+ icalcomponent_add_component (tz_comp, std_comp);
+ } else
+ icalerror_set_errno (ICAL_MALFORMEDDATA_ERROR);
+
+ if (dstidx != -1) {
+ zidx = trans_idx [dstidx];
+ zp_idx = trans_idx [dstidx-1];
+ dst_comp = icalcomponent_new (ICAL_XDAYLIGHT_COMPONENT);
+ icalprop = icalproperty_new_tzname (types [zidx].zname);
+ icalcomponent_add_property (dst_comp, icalprop);
+
+ /* DTSTART localtime uses TZOFFSETFROM UTC offset */
+ if (num_trans != 0)
+ trans = transitions [dstidx] + types [zp_idx].gmtoff;
+ else
+ trans = types [zp_idx].gmtoff;
+ icaltime = icaltime_from_timet (trans, 0);
+ dtstart = icaltime;
+ dtstart.year = 1970;
+ dtstart.minute = dtstart.second = 0;
+ icalprop = icalproperty_new_dtstart (dtstart);
+ icalcomponent_add_property (dst_comp, icalprop);
+
+ icalrecurrencetype_clear (&ical_recur);
+ ical_recur.freq = ICAL_YEARLY_RECURRENCE;
+ ical_recur.by_month [0] = icaltime.month;
+ pos = calculate_pos (icaltime);
+ pos < 0 ? (sign = -1): (sign = 1);
+ ical_recur.by_day [0] = sign * ((abs (pos) * 8) + icaltime_day_of_week (icaltime));
+ icalprop = icalproperty_new_rrule (ical_recur);
+ icalcomponent_add_property (dst_comp, icalprop);
+
+ adjust_dtstart_day_to_rrule (dst_comp, ical_recur);
+
+ icalprop = icalproperty_new_tzoffsetfrom (types [zp_idx].gmtoff);
+ icalcomponent_add_property (dst_comp, icalprop);
+
+ icalprop = icalproperty_new_tzoffsetto (types [zidx].gmtoff);
+ icalcomponent_add_property (dst_comp, icalprop);
+
+ icalcomponent_add_component (tz_comp, dst_comp);
+ }
+
+error:
+ if (f)
+ fclose (f);
+
+ if (transitions)
+ free (transitions);
+ if (trans_idx)
+ free (trans_idx);
+ if (types) {
+ for (i = 0; i < num_types; i++)
+ if (types [i].zname)
+ free (types [i].zname);
+ free (types);
+ }
+ if (znames)
+ free (znames);
+ free (full_path);
+ if (leaps)
+ free (leaps);
+
+ return tz_comp;
+}
+
+/*
+int
+main (int argc, char *argv [])
+{
+ tzutil_fetch_timezone (argv [1]);
+ return 0;
+}*/