PageRenderTime 35ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/libical-0.44/src/libical/icaltz-util.c

https://github.com/nolta/libical-universal
C | 525 lines | 394 code | 88 blank | 43 comment | 73 complexity | be207971ecc0c5bafc3a686b2346d102 MD5 | raw file
  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. #ifdef HAVE_CONFIG_H
  23. #include "config.h"
  24. #endif
  25. #include <string.h>
  26. #if defined(sun) && defined(__SVR4)
  27. #include <sys/byteorder.h>
  28. #else
  29. # ifdef HAVE_BYTESWAP_H
  30. # include <byteswap.h>
  31. # endif
  32. # ifdef HAVE_SYS_ENDIAN_H
  33. # include <sys/endian.h>
  34. # ifdef bswap32
  35. # define bswap_32 bswap32
  36. # else
  37. # define bswap_32 swap32
  38. # endif
  39. # endif
  40. # ifdef HAVE_ENDIAN_H
  41. # include <endian.h>
  42. # endif
  43. # if !defined(HAVE_BYTESWAP_H) && !defined(HAVE_SYS_ENDIAN_H) && !defined(HAVE_ENDIAN_H)
  44. # define bswap_16(x) (((x) << 8) & 0xff00) | (((x) >> 8 ) & 0xff)
  45. # define bswap_32(x) (((x) << 24) & 0xff000000) \
  46. | (((x) << 8) & 0xff0000) \
  47. | (((x) >> 8) & 0xff00) \
  48. | (((x) >> 24) & 0xff )
  49. # define bswap_64(x) ((((x) & 0xff00000000000000ull) >> 56) \
  50. | (((x) & 0x00ff000000000000ull) >> 40) \
  51. | (((x) & 0x0000ff0000000000ull) >> 24) \
  52. | (((x) & 0x000000ff00000000ull) >> 8) \
  53. | (((x) & 0x00000000ff000000ull) << 8) \
  54. | (((x) & 0x0000000000ff0000ull) << 24) \
  55. | (((x) & 0x000000000000ff00ull) << 40) \
  56. | (((x) & 0x00000000000000ffull) << 56))
  57. # endif
  58. #endif
  59. #ifdef WIN32
  60. #include <io.h>
  61. #endif
  62. #ifndef PATH_MAX
  63. #define PATH_MAX 512
  64. #endif
  65. #ifndef F_OK
  66. #define F_OK 0
  67. #endif
  68. #ifndef R_OK
  69. #define R_OK 4
  70. #endif
  71. #include <limits.h>
  72. #include <time.h>
  73. #include <stdlib.h>
  74. #ifdef HAVE_UNISTD_H
  75. #include <unistd.h>
  76. #endif
  77. #include <libical/icalerror.h>
  78. #include <icaltz-util.h>
  79. typedef struct
  80. {
  81. char ttisgmtcnt [4];
  82. char ttisstdcnt[4];
  83. char leapcnt[4];
  84. char timecnt[4];
  85. char typecnt[4];
  86. char charcnt[4];
  87. } tzinfo;
  88. static int r_pos [] = {1, 2, 3, -2, -1};
  89. static char *search_paths [] = {"/usr/share/zoneinfo","/usr/lib/zoneinfo","/etc/zoneinfo","/usr/share/lib/zoneinfo"};
  90. static char *zdir = NULL;
  91. #define NUM_SEARCH_PATHS (sizeof (search_paths)/ sizeof (search_paths [0]))
  92. #define EFREAD(buf,size,num,fs) \
  93. if (fread (buf, size, num, fs) == 0 && ferror (fs)) {\
  94. icalerror_set_errno (ICAL_FILE_ERROR); \
  95. goto error; \
  96. } \
  97. typedef struct
  98. {
  99. long int gmtoff;
  100. unsigned char isdst;
  101. unsigned int abbr;
  102. unsigned char isstd;
  103. unsigned char isgmt;
  104. char *zname;
  105. } ttinfo;
  106. typedef struct
  107. {
  108. time_t transition;
  109. long int change;
  110. } leap;
  111. extern const char *ical_tzid_prefix;
  112. static int
  113. decode (const void *ptr)
  114. {
  115. #if defined(sun) && defined(__SVR4)
  116. if (sizeof (int) == 4)
  117. #ifdef _BIG_ENDIAN
  118. return *(const int *) ptr;
  119. #else
  120. return BSWAP_32 (*(const int *) ptr);
  121. #endif
  122. #else
  123. if ((BYTE_ORDER == BIG_ENDIAN) && sizeof (int) == 4)
  124. return *(const int *) ptr;
  125. else if (BYTE_ORDER == LITTLE_ENDIAN && sizeof (int) == 4)
  126. return bswap_32 (*(const int *) ptr);
  127. #endif
  128. else
  129. {
  130. const unsigned char *p = ptr;
  131. int result = *p & (1 << (CHAR_BIT - 1)) ? ~0 : 0;
  132. result = (result << 8) | *p++;
  133. result = (result << 8) | *p++;
  134. result = (result << 8) | *p++;
  135. result = (result << 8) | *p++;
  136. return result;
  137. }
  138. }
  139. static char *
  140. zname_from_stridx (char *str, long int idx)
  141. {
  142. int i = 0;
  143. char *ret;
  144. size_t size;
  145. i = idx;
  146. while (str [i] != '\0')
  147. i++;
  148. size = i - idx;
  149. str += idx;
  150. ret = (char *) malloc (size + 1);
  151. ret = strncpy (ret, str, size);
  152. ret [size] = '\0';
  153. return ret;
  154. }
  155. static void
  156. find_transidx (time_t *transitions, ttinfo *types, int *trans_idx, long int num_trans, int *stdidx, int *dstidx)
  157. {
  158. time_t now, year_start;
  159. int i, found = 0;
  160. struct icaltimetype itime;
  161. now = time (NULL);
  162. itime = icaltime_from_timet (now, 0);
  163. itime.month = itime.day = 1;
  164. itime.hour = itime.minute = itime.second = 0;
  165. year_start = icaltime_as_timet(itime);
  166. /* Set this by default */
  167. *stdidx = (num_trans - 1);
  168. for (i = (num_trans - 1); i >= 0; --i)
  169. if (year_start < transitions [i]) {
  170. int idx;
  171. found = 1;
  172. idx = trans_idx [i];
  173. (types [idx].isdst) ? (*dstidx = i) : (*stdidx = i);
  174. }
  175. /* If the transition found is the last among the list, prepare to use the last two transtions.
  176. * Using this will most likely throw the DTSTART of the resulting component off by 1 or 2 days
  177. * but it would set right by the adjustment made.
  178. * NOTE: We need to use the last two transitions only because there is no data for the future
  179. * transitions.
  180. */
  181. if (found && (*dstidx == -1)) {
  182. *dstidx = ((*stdidx) - 1);
  183. }
  184. return;
  185. }
  186. static void
  187. set_zone_directory (void)
  188. {
  189. char file_path[PATH_MAX];
  190. const char *fname = ZONES_TAB_SYSTEM_FILENAME;
  191. int i;
  192. for (i = 0;i < NUM_SEARCH_PATHS; i++) {
  193. sprintf (file_path, "%s/%s", search_paths [i], fname);
  194. if (!access (file_path, F_OK|R_OK)) {
  195. zdir = search_paths [i];
  196. break;
  197. }
  198. }
  199. }
  200. const char *
  201. icaltzutil_get_zone_directory (void)
  202. {
  203. if (!zdir)
  204. set_zone_directory ();
  205. return zdir;
  206. }
  207. /* Calculate the relative position of the week in a month from a date */
  208. static int
  209. calculate_pos (icaltimetype icaltime)
  210. {
  211. int pos;
  212. pos = (icaltime.day -1) / 7;
  213. /* Check if pos 3 is the last occurence of the week day in the month */
  214. if (pos == 3 && ((icaltime.day + 7) > icaltime_days_in_month (icaltime.month, icaltime.year)))
  215. pos = 4;
  216. return r_pos [pos];
  217. }
  218. static void
  219. adjust_dtstart_day_to_rrule (icalcomponent *comp, struct icalrecurrencetype rule)
  220. {
  221. time_t now, year_start;
  222. struct icaltimetype start, comp_start, iter_start, itime;
  223. icalrecur_iterator *iter;
  224. now = time (NULL);
  225. itime = icaltime_from_timet (now, 0);
  226. itime.month = itime.day = 1;
  227. itime.hour = itime.minute = itime.second = 0;
  228. year_start = icaltime_as_timet(itime);
  229. comp_start = icalcomponent_get_dtstart (comp);
  230. start = icaltime_from_timet (year_start, 0);
  231. iter = icalrecur_iterator_new (rule, start);
  232. iter_start = icalrecur_iterator_next (iter);
  233. icalrecur_iterator_free (iter);
  234. if (iter_start.day != comp_start.day) {
  235. comp_start.day = iter_start.day;
  236. icalcomponent_set_dtstart (comp, comp_start);
  237. }
  238. }
  239. icalcomponent*
  240. icaltzutil_fetch_timezone (const char *location)
  241. {
  242. int ret = 0;
  243. FILE *f;
  244. tzinfo type_cnts;
  245. unsigned int num_trans, num_types, num_chars, num_leaps, num_isstd, num_isgmt;
  246. time_t *transitions = NULL;
  247. time_t trans;
  248. int *trans_idx = NULL, dstidx = -1, stdidx = -1, pos, sign, zidx, zp_idx, i;
  249. ttinfo *types = NULL;
  250. char *znames = NULL, *full_path, *tzid, *r_trans, *temp;
  251. leap *leaps = NULL;
  252. icalcomponent *tz_comp = NULL, *dst_comp = NULL, *std_comp = NULL;
  253. icalproperty *icalprop;
  254. icaltimetype dtstart, icaltime;
  255. struct icalrecurrencetype ical_recur;
  256. if (!zdir)
  257. set_zone_directory ();
  258. full_path = (char *) malloc (strlen (zdir) + strlen (location) + 2);
  259. sprintf (full_path,"%s/%s",zdir, location);
  260. if ((f = fopen (full_path, "rb")) == 0) {
  261. icalerror_set_errno (ICAL_FILE_ERROR);
  262. free (full_path);
  263. return NULL;
  264. }
  265. if ((ret = fseek (f, 20, SEEK_SET)) != 0) {
  266. icalerror_set_errno (ICAL_FILE_ERROR);
  267. goto error;
  268. }
  269. EFREAD(&type_cnts, 24, 1, f);
  270. num_isgmt = decode (type_cnts.ttisgmtcnt);
  271. num_leaps = decode (type_cnts.leapcnt);
  272. num_chars = decode (type_cnts.charcnt);
  273. num_trans = decode (type_cnts.timecnt);
  274. num_isstd = decode (type_cnts.ttisstdcnt);
  275. num_types = decode (type_cnts.typecnt);
  276. transitions = calloc (num_trans, sizeof (time_t));
  277. r_trans = calloc (num_trans, 4);
  278. EFREAD(r_trans, 4, num_trans, f);
  279. temp = r_trans;
  280. if (num_trans) {
  281. trans_idx = calloc (num_trans, sizeof (int));
  282. for (i = 0; i < num_trans; i++) {
  283. trans_idx [i] = fgetc (f);
  284. transitions [i] = decode (r_trans);
  285. r_trans += 4;
  286. }
  287. }
  288. free (temp);
  289. types = calloc (num_types, sizeof (ttinfo));
  290. for (i = 0; i < num_types; i++) {
  291. unsigned char a [4];
  292. int c;
  293. EFREAD(a, 4, 1, f);
  294. c = fgetc (f);
  295. types [i].isdst = c;
  296. if((c = fgetc (f)) < 0) {
  297. c = 0;
  298. break;
  299. }
  300. types [i].abbr = c;
  301. types [i].gmtoff = decode (a);
  302. }
  303. znames = (char *) malloc (num_chars);
  304. EFREAD(znames, num_chars, 1, f);
  305. /* We got all the information which we need */
  306. leaps = calloc (num_leaps, sizeof (leap));
  307. for (i = 0; i < num_leaps; i++) {
  308. char c [4];
  309. EFREAD (c, 4, 1, f);
  310. leaps [i].transition = decode (c);
  311. EFREAD (c, 4, 1, f);
  312. leaps [i].change = decode (c);
  313. }
  314. for (i = 0; i < num_isstd; ++i) {
  315. int c = getc (f);
  316. types [i].isstd = c != 0;
  317. }
  318. while (i < num_types)
  319. types [i++].isstd = 0;
  320. for (i = 0; i < num_isgmt; ++i) {
  321. int c = getc (f);
  322. types [i].isgmt = c != 0;
  323. }
  324. while (i < num_types)
  325. types [i++].isgmt = 0;
  326. /* Read all the contents now */
  327. for (i = 0; i < num_types; i++)
  328. types [i].zname = zname_from_stridx (znames, types [i].abbr);
  329. if (num_trans != 0)
  330. find_transidx (transitions, types, trans_idx, num_trans, &stdidx, &dstidx);
  331. else
  332. stdidx = 0;
  333. tz_comp = icalcomponent_new (ICAL_VTIMEZONE_COMPONENT);
  334. /* Add tzid property */
  335. tzid = (char *) malloc (strlen (ical_tzid_prefix) + strlen (location) + 8);
  336. sprintf (tzid, "%sTzfile/%s", ical_tzid_prefix, location);
  337. icalprop = icalproperty_new_tzid (tzid);
  338. icalcomponent_add_property (tz_comp, icalprop);
  339. free (tzid);
  340. icalprop = icalproperty_new_x (location);
  341. icalproperty_set_x_name (icalprop, "X-LIC-LOCATION");
  342. icalcomponent_add_property (tz_comp, icalprop);
  343. if (stdidx != -1) {
  344. if (num_trans != 0)
  345. zidx = trans_idx [stdidx];
  346. else
  347. zidx = 0;
  348. std_comp = icalcomponent_new (ICAL_XSTANDARD_COMPONENT);
  349. icalprop = icalproperty_new_tzname (types [zidx].zname);
  350. icalcomponent_add_property (std_comp, icalprop);
  351. if (dstidx != -1)
  352. zp_idx = trans_idx [stdidx-1];
  353. else
  354. zp_idx = zidx;
  355. /* DTSTART localtime uses TZOFFSETFROM UTC offset */
  356. trans = transitions [stdidx] + types [zp_idx].gmtoff;
  357. icaltime = icaltime_from_timet (trans, 0);
  358. dtstart = icaltime;
  359. dtstart.year = 1970;
  360. dtstart.minute = dtstart.second = 0;
  361. icalprop = icalproperty_new_dtstart (dtstart);
  362. icalcomponent_add_property (std_comp, icalprop);
  363. /* If DST changes are present use RRULE */
  364. if (dstidx != -1) {
  365. icalrecurrencetype_clear (&ical_recur);
  366. ical_recur.freq = ICAL_YEARLY_RECURRENCE;
  367. ical_recur.by_month [0] = icaltime.month;
  368. pos = calculate_pos (icaltime);
  369. pos < 0 ? (sign = -1): (sign = 1);
  370. ical_recur.by_day [0] = sign * ((abs (pos) * 8) + icaltime_day_of_week (icaltime));
  371. icalprop = icalproperty_new_rrule (ical_recur);
  372. icalcomponent_add_property (std_comp, icalprop);
  373. adjust_dtstart_day_to_rrule (std_comp, ical_recur);
  374. }
  375. icalprop = icalproperty_new_tzoffsetfrom (types [zp_idx].gmtoff);
  376. icalcomponent_add_property (std_comp, icalprop);
  377. icalprop = icalproperty_new_tzoffsetto (types [zidx].gmtoff);
  378. icalcomponent_add_property (std_comp, icalprop);
  379. icalcomponent_add_component (tz_comp, std_comp);
  380. } else
  381. icalerror_set_errno (ICAL_MALFORMEDDATA_ERROR);
  382. if (dstidx != -1) {
  383. zidx = trans_idx [dstidx];
  384. zp_idx = trans_idx [dstidx-1];
  385. dst_comp = icalcomponent_new (ICAL_XDAYLIGHT_COMPONENT);
  386. icalprop = icalproperty_new_tzname (types [zidx].zname);
  387. icalcomponent_add_property (dst_comp, icalprop);
  388. /* DTSTART localtime uses TZOFFSETFROM UTC offset */
  389. trans = transitions [dstidx] + types [zp_idx].gmtoff;
  390. icaltime = icaltime_from_timet (trans, 0);
  391. dtstart = icaltime;
  392. dtstart.year = 1970;
  393. dtstart.minute = dtstart.second = 0;
  394. icalprop = icalproperty_new_dtstart (dtstart);
  395. icalcomponent_add_property (dst_comp, icalprop);
  396. icalrecurrencetype_clear (&ical_recur);
  397. ical_recur.freq = ICAL_YEARLY_RECURRENCE;
  398. ical_recur.by_month [0] = icaltime.month;
  399. pos = calculate_pos (icaltime);
  400. pos < 0 ? (sign = -1): (sign = 1);
  401. ical_recur.by_day [0] = sign * ((abs (pos) * 8) + icaltime_day_of_week (icaltime));
  402. icalprop = icalproperty_new_rrule (ical_recur);
  403. icalcomponent_add_property (dst_comp, icalprop);
  404. adjust_dtstart_day_to_rrule (dst_comp, ical_recur);
  405. icalprop = icalproperty_new_tzoffsetfrom (types [zp_idx].gmtoff);
  406. icalcomponent_add_property (dst_comp, icalprop);
  407. icalprop = icalproperty_new_tzoffsetto (types [zidx].gmtoff);
  408. icalcomponent_add_property (dst_comp, icalprop);
  409. icalcomponent_add_component (tz_comp, dst_comp);
  410. }
  411. error:
  412. if (f)
  413. fclose (f);
  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. return tz_comp;
  430. }
  431. /*
  432. int
  433. main (int argc, char *argv [])
  434. {
  435. tzutil_fetch_timezone (argv [1]);
  436. return 0;
  437. }*/