4c192139fd5b13b78f00055732112b1895dba98c
[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 find_transidx (time_t *transitions, ttinfo *types, int *trans_idx, long int num_trans, int *stdidx, int *dstidx) 
193 {
194         time_t now, year_start;
195         int i, found = 0;
196         struct icaltimetype itime;
197
198         now = time (NULL);
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);
203
204         /* Set this by default */
205         *stdidx = (num_trans - 1);
206
207         for (i = (num_trans - 1); i >= 0; --i)
208                 if (year_start < transitions [i]) {
209                         int idx;
210                         found = 1;
211                         idx = trans_idx [i];
212                         (types [idx].isdst) ? (*dstidx = i) : (*stdidx = i);
213                 }
214
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 
219          * transitions. 
220          */
221         if (found && (*dstidx == -1)) {
222                 *dstidx = ((*stdidx) - 1);
223         }
224
225         return;
226 }
227
228 static void
229 set_zonedir (void)
230 {
231         char file_path[PATH_MAX];
232         const char *fname = ZONES_TAB_SYSTEM_FILENAME;
233         int i;  
234
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];
239                         break;
240                 }
241         }
242 }
243
244
245 const char *
246 icaltzutil_get_zone_directory (void)
247 {
248         if (!zdir)
249                 set_zonedir ();
250
251         return zdir;
252 }
253
254 /* Calculate the relative position of the week in a month from a date */
255 static int
256 calculate_pos (icaltimetype icaltime)
257 {
258         int pos;
259
260         pos = (icaltime.day -1) / 7;
261
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))) 
264                 pos = 4;
265
266         return r_pos [pos];
267 }
268
269 static void
270 adjust_dtstart_day_to_rrule (icalcomponent *comp, struct icalrecurrencetype rule)
271 {
272         time_t now, year_start;
273         struct icaltimetype start, comp_start, iter_start, itime;
274         icalrecur_iterator *iter;
275
276         now = time (NULL);
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);
281
282         comp_start = icalcomponent_get_dtstart (comp);
283         start = icaltime_from_timet (year_start, 0);
284
285         iter = icalrecur_iterator_new (rule, start);
286         iter_start = icalrecur_iterator_next (iter);
287         icalrecur_iterator_free (iter);
288
289         if (iter_start.day != comp_start.day) {
290                 comp_start.day = iter_start.day;
291                 icalcomponent_set_dtstart (comp, comp_start);
292         }
293 }
294
295 icalcomponent*
296 icaltzutil_fetch_timezone (const char *location)
297 {
298         int ret = 0;
299         FILE *f;
300         tzinfo type_cnts;
301         unsigned int i, num_trans, num_types, num_chars, num_leaps, num_isstd, num_isgmt;
302         time_t *transitions = NULL;
303         time_t trans;
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;
307         leap *leaps = NULL;
308         icalcomponent *tz_comp = NULL, *dst_comp = NULL, *std_comp = NULL;
309         icalproperty *icalprop;
310         icaltimetype dtstart, icaltime;
311         struct icalrecurrencetype ical_recur;
312         const char *basedir;
313                
314         basedir = icaltzutil_get_zone_directory();
315         if (!basedir) {
316                 icalerror_set_errno (ICAL_FILE_ERROR);
317                 return NULL;
318         }
319
320         full_path = (char *) malloc (strlen (basedir) + strlen (location) + 2);
321         sprintf (full_path,"%s/%s",basedir, location);
322
323         if ((f = fopen (full_path, "rb")) == 0) {
324                 icalerror_set_errno (ICAL_FILE_ERROR);
325                 free (full_path);
326                 return NULL;
327         }
328
329         if ((ret = fseek (f, 20, SEEK_SET)) != 0) {
330                 icalerror_set_errno (ICAL_FILE_ERROR);
331                 goto error;     
332         }
333
334         EFREAD(&type_cnts, 24, 1, f);
335
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);
342
343         transitions = calloc (num_trans, sizeof (time_t));
344         r_trans = calloc (num_trans, 4);
345         EFREAD(r_trans, 4, num_trans, f);
346         temp = r_trans; 
347
348         if (num_trans) {
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);
353                         r_trans += 4;
354                 }
355         }
356         
357         free (temp);
358
359         types = calloc (num_types, sizeof (ttinfo));
360         for (i = 0; i < num_types; i++) {
361                 unsigned char a [4];
362                 int c;
363
364                 EFREAD(a, 4, 1, f);
365                 c = fgetc (f);
366                 types [i].isdst = c;
367                 if((c = fgetc (f)) < 0) {
368                    c = 0;
369                    break;
370                 }
371                 types [i].abbr = c;
372                 types [i].gmtoff = decode (a);
373         }
374
375         znames = (char *) malloc (num_chars);
376         EFREAD(znames, num_chars, 1, f);
377
378         /* We got all the information which we need */
379
380         leaps = calloc (num_leaps, sizeof (leap));
381         for (i = 0; i < num_leaps; i++) {
382                 char c [4];
383
384                 EFREAD (c, 4, 1, f);
385                 leaps [i].transition = decode (c);
386
387                 EFREAD (c, 4, 1, f);
388                 leaps [i].change = decode (c);
389         }
390
391         for (i = 0; i < num_isstd; ++i) {
392                 int c = getc (f);
393                 types [i].isstd = c != 0;
394         }
395
396         while (i < num_types)
397                 types [i++].isstd = 0;
398
399         for (i = 0; i <  num_isgmt; ++i) {
400                 int c = getc (f);
401                 types [i].isgmt = c != 0;
402         }
403
404         while (i < num_types)
405                 types [i++].isgmt = 0;
406
407         /* Read all the contents now */
408
409         for (i = 0; i < num_types; i++) 
410                 types [i].zname = zname_from_stridx (znames, types [i].abbr);
411
412         if (num_trans != 0)
413                 find_transidx (transitions, types, trans_idx, num_trans, &stdidx, &dstidx);
414         else
415                 stdidx = 0;
416
417         tz_comp = icalcomponent_new (ICAL_VTIMEZONE_COMPONENT);
418
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);
424         free (tzid);
425
426         icalprop = icalproperty_new_x (location);
427         icalproperty_set_x_name (icalprop, "X-LIC-LOCATION");
428         icalcomponent_add_property (tz_comp, icalprop);
429         
430         if (stdidx != -1) {
431                 if (num_trans != 0)
432                         zidx = trans_idx [stdidx];
433                 else 
434                         zidx = 0;
435
436                 std_comp = icalcomponent_new (ICAL_XSTANDARD_COMPONENT);
437                 icalprop = icalproperty_new_tzname (types [zidx].zname);
438                 icalcomponent_add_property (std_comp, icalprop);
439
440                 if (dstidx != -1)
441                         zp_idx = trans_idx [stdidx-1]; 
442                 else
443                         zp_idx = zidx;
444                 /* DTSTART localtime uses TZOFFSETFROM UTC offset */
445                 if (num_trans != 0)
446                         trans = transitions [stdidx] + types [zp_idx].gmtoff;
447                 else
448                         trans = types [zp_idx].gmtoff;
449                 icaltime = icaltime_from_timet (trans, 0);
450                 dtstart = icaltime;
451                 dtstart.year = 1970;
452                 dtstart.minute = dtstart.second = 0;
453                 icalprop = icalproperty_new_dtstart (dtstart);
454                 icalcomponent_add_property (std_comp, icalprop);
455
456                 /* If DST changes are present use RRULE */
457                 if (dstidx != -1) {
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);
466
467                         adjust_dtstart_day_to_rrule (std_comp, ical_recur);
468                 }
469         icalprop = icalproperty_new_tzoffsetfrom (types [zp_idx].gmtoff);
470         icalcomponent_add_property (std_comp, icalprop);
471
472                 icalprop = icalproperty_new_tzoffsetto (types [zidx].gmtoff);
473                 icalcomponent_add_property (std_comp, icalprop);
474
475                 icalcomponent_add_component (tz_comp, std_comp);
476         } else 
477                 icalerror_set_errno (ICAL_MALFORMEDDATA_ERROR);
478         
479         if (dstidx != -1) {
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);
485
486                 /* DTSTART localtime uses TZOFFSETFROM UTC offset */
487                 if (num_trans != 0)
488                         trans = transitions [dstidx] + types [zp_idx].gmtoff;
489                 else
490                         trans = types [zp_idx].gmtoff;
491                 icaltime = icaltime_from_timet (trans, 0);
492                 dtstart = icaltime;
493                 dtstart.year = 1970;
494                 dtstart.minute = dtstart.second = 0;
495                 icalprop = icalproperty_new_dtstart (dtstart);
496                 icalcomponent_add_property (dst_comp, icalprop);
497
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);
506
507                 adjust_dtstart_day_to_rrule (dst_comp, ical_recur);
508
509                 icalprop = icalproperty_new_tzoffsetfrom (types [zp_idx].gmtoff);
510                 icalcomponent_add_property (dst_comp, icalprop);
511
512                 icalprop = icalproperty_new_tzoffsetto (types [zidx].gmtoff);
513                 icalcomponent_add_property (dst_comp, icalprop);
514
515                 icalcomponent_add_component (tz_comp, dst_comp);
516         }
517
518 error:
519         if (f)
520                 fclose  (f);
521
522         if (transitions)
523                 free (transitions);
524         if (trans_idx)
525                 free (trans_idx);
526         if (types) {
527                 for (i = 0; i < num_types; i++) 
528                         if (types [i].zname)
529                                 free (types [i].zname);
530                 free (types);
531         }
532         if (znames)
533                 free (znames);
534         free (full_path);
535         if (leaps)
536                 free (leaps);
537
538         return tz_comp;
539 }
540
541 /*
542 int 
543 main (int argc, char *argv [])
544 {
545         tzutil_fetch_timezone (argv [1]);
546         return 0;
547 }*/