Imported Upstream version 1.0.0
[platform/upstream/libical.git] / src / libical / icaltz-util.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* 
3  * Authors : 
4  *  Chenthill Palanisamy <pchenthill@novell.com>
5  *
6  * Copyright 2007, Novell, Inc.
7  *
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.
11  *
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.
16  *
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.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include <string.h>
27
28 #ifdef HAVE_STDINT_H
29 #include <stdint.h>
30 #endif
31
32 #if defined(sun) && defined(__SVR4)
33 #include <sys/types.h>
34 #include <sys/byteorder.h>
35 #else
36 # ifdef HAVE_BYTESWAP_H
37 #  include <byteswap.h>
38 # endif
39 # ifdef HAVE_ENDIAN_H
40 #  include <endian.h>
41 # else
42 # ifdef HAVE_SYS_ENDIAN_H
43 #  include <sys/endian.h>
44 #  ifdef bswap32
45 #   define bswap_32 bswap32
46 #  else
47 #   define bswap_32 swap32
48 #  endif
49 # endif
50 # endif
51 #endif
52
53 #ifdef _MSC_VER
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))
68 #endif
69 #include <io.h>
70 #endif
71
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
76 #endif
77
78 #ifndef PATH_MAX
79 #define PATH_MAX 512
80 #endif
81
82 #ifndef F_OK
83 #define F_OK 0
84 #endif
85
86 #ifndef R_OK
87 #define R_OK 4
88 #endif
89
90 #include <limits.h>
91 #include <time.h>
92 #include <stdlib.h>
93 #ifdef HAVE_UNISTD_H
94 #include <unistd.h>
95 #endif
96 #include <libical/icalerror.h>
97 #include <icaltz-util.h>
98
99 typedef struct 
100 {
101         char    ttisgmtcnt [4]; 
102         char    ttisstdcnt[4];  
103         char    leapcnt[4];             
104         char    timecnt[4];     
105         char    typecnt[4];
106         char    charcnt[4];                     
107 } tzinfo; 
108
109 static int r_pos [] = {1, 2, 3, -2, -1};
110
111 static char *search_paths [] = {"/usr/share/zoneinfo","/usr/lib/zoneinfo","/etc/zoneinfo","/usr/share/lib/zoneinfo"};
112 static char *zdir = NULL;
113
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);          \
118                 goto error;                                     \
119         }                                                       \
120
121 typedef struct
122 {
123         long int gmtoff;
124         unsigned char isdst;
125         unsigned int abbr;      
126         unsigned char isstd;
127         unsigned char isgmt;
128         char *zname;
129
130 } ttinfo;
131
132 typedef struct
133 {
134         time_t transition;
135         long int change;
136 } leap;
137
138 extern const char *ical_tzid_prefix;
139
140 static int
141 decode (const void *ptr)
142 {
143 #if defined(sun) && defined(__SVR4)
144     if (sizeof (int) == 4)
145 #ifdef _BIG_ENDIAN
146         return *(const int *) ptr;
147 #else
148         return BSWAP_32 (*(const int *) ptr);
149 #endif
150 #else
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);
155 #endif
156         else
157         {
158                 const unsigned char *p = ptr;
159                 int result = *p & (1 << (CHAR_BIT - 1)) ? ~0 : 0;
160
161                 result = (result << 8) | *p++;
162                 result = (result << 8) | *p++;
163                 result = (result << 8) | *p++;
164                 result = (result << 8) | *p++;
165
166                 return result;
167         }
168 }
169
170 static char *
171 zname_from_stridx (char *str, long int idx) 
172 {
173         int i = 0;
174         char *ret;
175         size_t size;
176
177         i = idx;
178
179         while (str [i] != '\0') 
180                 i++;
181
182         size = i - idx;
183         str += idx;
184         ret = (char *) malloc (size + 1);
185         ret = strncpy (ret, str, size);
186         ret [size] = '\0';
187
188         return ret;
189 }
190
191 static void
192 set_zonedir (void)
193 {
194         char file_path[PATH_MAX];
195         const char *fname = ZONES_TAB_SYSTEM_FILENAME;
196         int i;  
197
198         for (i = 0;i < NUM_SEARCH_PATHS; i++) {
199                 sprintf (file_path, "%s/%s", search_paths [i], fname);
200                 if (!access (file_path, F_OK|R_OK)) {
201                         zdir = search_paths [i];
202                         break;
203                 }
204         }
205 }
206
207
208 const char *
209 icaltzutil_get_zone_directory (void)
210 {
211         if (!zdir)
212                 set_zonedir ();
213
214         return zdir;
215 }
216
217 /* Calculate the relative position of the week in a month from a date */
218 static int
219 calculate_pos (icaltimetype icaltime)
220 {
221         int pos;
222
223         pos = (icaltime.day -1) / 7;
224
225         /* Check if pos 3 is the last occurence of the week day in the month */ 
226         if (pos == 3 && ((icaltime.day + 7) > icaltime_days_in_month (icaltime.month, icaltime.year))) 
227                 pos = 4;
228
229         return r_pos [pos];
230 }
231
232 icalcomponent*
233 icaltzutil_fetch_timezone (const char *location)
234 {
235         int ret = 0;
236         FILE *f;
237         tzinfo type_cnts;
238         unsigned int i, num_trans, num_types, num_chars, num_leaps, num_isstd, num_isgmt;
239         time_t *transitions = NULL;
240         time_t trans, start, end;
241         int *trans_idx = NULL, pos, sign, zidx, zp_idx, idx, prev_idx;
242         ttinfo *types = NULL;
243         char *znames = NULL, *full_path, *tzid, *r_trans, *temp;
244         leap *leaps = NULL;
245         icalcomponent *tz_comp = NULL, *comp = NULL;
246         icalproperty *icalprop;
247         icaltimetype dtstart, icaltime;
248         struct icalrecurrencetype ical_recur;
249         const char *basedir;
250                
251         basedir = icaltzutil_get_zone_directory();
252         if (!basedir) {
253                 icalerror_set_errno (ICAL_FILE_ERROR);
254                 return NULL;
255         }
256
257         full_path = (char *) malloc (strlen (basedir) + strlen (location) + 2);
258         sprintf (full_path,"%s/%s",basedir, location);
259
260         if ((f = fopen (full_path, "rb")) == 0) {
261                 icalerror_set_errno (ICAL_FILE_ERROR);
262                 free (full_path);
263                 return NULL;
264         }
265
266         if ((ret = fseek (f, 20, SEEK_SET)) != 0) {
267                 icalerror_set_errno (ICAL_FILE_ERROR);
268                 goto error;     
269         }
270
271         EFREAD(&type_cnts, 24, 1, f);
272
273         num_isgmt = decode (type_cnts.ttisgmtcnt);
274         num_leaps = decode (type_cnts.leapcnt);
275         num_chars = decode (type_cnts.charcnt);
276         num_trans = decode (type_cnts.timecnt);
277         num_isstd = decode (type_cnts.ttisstdcnt);
278         num_types = decode (type_cnts.typecnt);
279
280         transitions = calloc (num_trans, sizeof (time_t));
281         r_trans = calloc (num_trans, 4);
282         EFREAD(r_trans, 4, num_trans, f);
283         temp = r_trans; 
284
285         if (num_trans) {
286                 trans_idx = calloc (num_trans, sizeof (int));
287                 for (i = 0; i < num_trans; i++) {
288                         trans_idx [i] = fgetc (f);
289                         transitions [i] = decode (r_trans);
290                         r_trans += 4;
291                 }
292         }
293         
294         free (temp);
295
296         types = calloc (num_types, sizeof (ttinfo));
297         for (i = 0; i < num_types; i++) {
298                 unsigned char a [4];
299                 int c;
300
301                 EFREAD(a, 4, 1, f);
302                 c = fgetc (f);
303                 types [i].isdst = c;
304                 if((c = fgetc (f)) < 0) {
305                    c = 0;
306                    break;
307                 }
308                 types [i].abbr = c;
309                 types [i].gmtoff = decode (a);
310         }
311
312         znames = (char *) malloc (num_chars);
313         EFREAD(znames, num_chars, 1, f);
314
315         /* We got all the information which we need */
316
317         leaps = calloc (num_leaps, sizeof (leap));
318         for (i = 0; i < num_leaps; i++) {
319                 char c [4];
320
321                 EFREAD (c, 4, 1, f);
322                 leaps [i].transition = decode (c);
323
324                 EFREAD (c, 4, 1, f);
325                 leaps [i].change = decode (c);
326         }
327
328         for (i = 0; i < num_isstd; ++i) {
329                 int c = getc (f);
330                 types [i].isstd = c != 0;
331         }
332
333         while (i < num_types)
334                 types [i++].isstd = 0;
335
336         for (i = 0; i <  num_isgmt; ++i) {
337                 int c = getc (f);
338                 types [i].isgmt = c != 0;
339         }
340
341         while (i < num_types)
342                 types [i++].isgmt = 0;
343
344         /* Read all the contents now */
345
346         for (i = 0; i < num_types; i++) 
347                 types [i].zname = zname_from_stridx (znames, types [i].abbr);
348
349         tz_comp = icalcomponent_new (ICAL_VTIMEZONE_COMPONENT);
350
351         /* Add tzid property */
352         tzid = (char *) malloc (strlen (ical_tzid_prefix) + strlen (location) + 8);
353         sprintf (tzid, "%sTzfile/%s", ical_tzid_prefix, location);
354         icalprop = icalproperty_new_tzid (tzid);
355         icalcomponent_add_property (tz_comp, icalprop);
356         free (tzid);
357
358         icalprop = icalproperty_new_x (location);
359         icalproperty_set_x_name (icalprop, "X-LIC-LOCATION");
360         icalcomponent_add_property (tz_comp, icalprop);
361
362         prev_idx = 0;
363         if (num_trans == 0) {
364                 prev_idx = idx = 0;
365                 
366         } else {
367                 idx = trans_idx[0];
368         }
369         start = 0;
370         for (i = 1; i < num_trans; i++, start = end) {
371                 prev_idx = idx;
372                 idx = trans_idx [i];
373                 end = transitions [i] + types [prev_idx].gmtoff;
374                 /* don't bother starting until the epoch */
375                 if (0 > end)
376                         continue;
377
378                 if (types [prev_idx].isdst)
379                         comp = icalcomponent_new (ICAL_XDAYLIGHT_COMPONENT);
380                 else
381                         comp = icalcomponent_new (ICAL_XSTANDARD_COMPONENT);
382                 icalprop = icalproperty_new_tzname (types [prev_idx].zname);
383                 icalcomponent_add_property (comp, icalprop);
384                 dtstart = icaltime_from_timet(start, 0);
385                 icalprop = icalproperty_new_dtstart (dtstart);
386                 icalcomponent_add_property (comp, icalprop);
387                 icalprop = icalproperty_new_tzoffsetfrom (types [idx].gmtoff);
388                 icalcomponent_add_property (comp, icalprop);
389                 icalprop = icalproperty_new_tzoffsetto (types [prev_idx].gmtoff);
390                 icalcomponent_add_property (comp, icalprop);
391                 icalcomponent_add_component (tz_comp, comp);
392         }
393         /* finally, add a last zone with no end date */
394         if (types [idx].isdst)
395                 comp = icalcomponent_new (ICAL_XDAYLIGHT_COMPONENT);
396         else
397                 comp = icalcomponent_new (ICAL_XSTANDARD_COMPONENT);
398         icalprop = icalproperty_new_tzname (types [idx].zname);
399         icalcomponent_add_property (comp, icalprop);
400         dtstart = icaltime_from_timet(start, 0);
401         icalprop = icalproperty_new_dtstart (dtstart);
402         icalcomponent_add_property (comp, icalprop);
403         icalprop = icalproperty_new_tzoffsetfrom (types [prev_idx].gmtoff);
404         icalcomponent_add_property (comp, icalprop);
405         icalprop = icalproperty_new_tzoffsetto (types [idx].gmtoff);
406         icalcomponent_add_property (comp, icalprop);
407         icalcomponent_add_component (tz_comp, comp);
408
409
410 error:
411         if (f)
412                 fclose  (f);
413
414         if (transitions)
415                 free (transitions);
416         if (trans_idx)
417                 free (trans_idx);
418         if (types) {
419                 for (i = 0; i < num_types; i++) 
420                         if (types [i].zname)
421                                 free (types [i].zname);
422                 free (types);
423         }
424         if (znames)
425                 free (znames);
426         free (full_path);
427         if (leaps)
428                 free (leaps);
429
430         return tz_comp;
431 }
432
433 /*
434 int 
435 main (int argc, char *argv [])
436 {
437         tzutil_fetch_timezone (argv [1]);
438         return 0;
439 }*/