[Xfce4-commits] r29901 - in xfcalendar/trunk: . tz_convert

Juha Kautto juha at xfce.org
Fri Apr 24 13:48:31 CEST 2009


Author: juha
Date: 2009-04-24 11:48:30 +0000 (Fri, 24 Apr 2009)
New Revision: 29901

Added:
   xfcalendar/trunk/tz_convert/
   xfcalendar/trunk/tz_convert/tz_convert.c
Modified:
   xfcalendar/trunk/configure.in.in
Log:
version 4.7.2.0
tz_convert routine added.
still need to add make files etc.


Modified: xfcalendar/trunk/configure.in.in
===================================================================
--- xfcalendar/trunk/configure.in.in	2009-04-24 07:18:45 UTC (rev 29900)
+++ xfcalendar/trunk/configure.in.in	2009-04-24 11:48:30 UTC (rev 29901)
@@ -9,7 +9,7 @@
 dnl
 
 dnl Version information
-m4_define([orage_version], [4.7.1.1-svn])
+m4_define([orage_version], [4.7.2.0-svn])
 
 m4_define([gtk_minimum_version], [2.10.0])
 m4_define([xfce_minimum_version], [4.6.0])

Added: xfcalendar/trunk/tz_convert/tz_convert.c
===================================================================
--- xfcalendar/trunk/tz_convert/tz_convert.c	                        (rev 0)
+++ xfcalendar/trunk/tz_convert/tz_convert.c	2009-04-24 11:48:30 UTC (rev 29901)
@@ -0,0 +1,1395 @@
+/*      Orage - Calendar and alarm handler
+ *
+ * Copyright (c) 2008 Juha Kautto  (juha at xfce.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the 
+        Free Software Foundation
+        51 Franklin Street, 5th Floor
+        Boston, MA 02110-1301 USA
+ */
+
+#include <error.h>
+#include <errno.h>
+    /* errno */
+
+#include <stdlib.h>
+    /* malloc, atoi, free, setenv */
+
+#include <stdio.h>
+    /* printf, fopen, fread, fclose, perror, rename */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+    /* stat, mkdir */
+
+#include <time.h>
+    /* localtime, gmtime, asctime */
+
+#include <string.h>
+    /* strncmp, strcmp, strlen, strncat, strncpy, strdup, strstr */
+
+#include <popt.h>
+    /* poptGetContext */
+
+/* This define is needed to get nftw instead if ftw.
+ * Documentation says the define is _XOPEN_SOURCE, but it
+ * does not work. __USE_XOPEN_EXTENDED works 
+ * Same with _GNU_SOURCE and __USE_GNU */
+#define _XOPEN_SOURCE 500
+#define __USE_XOPEN_EXTENDED 1
+#define _GNU_SOURCE 1
+#define __USE_GNU 1
+#include <ftw.h>
+    /* nftw */
+
+#define DEFAULT_ZONEINFO_DIRECTORY  "/usr/share/zoneinfo"
+#define DEFAULT_ZONETAB_FILE        "/usr/share/zoneinfo/zone.tab"
+
+int debug = 1; /* bigger number => more output */
+char version[] = "1.0.0";
+int file_cnt = 0; /* number of processed files */
+
+unsigned char *in_buf, *in_head, *in_tail;
+int in_file_base_offset = 0;
+
+char *in_file = NULL, *out_file = NULL;
+int in_file_is_dir = 0;
+int only_one_level = 0;
+int excl_dir_cnt = 5;
+char **excl_dir = NULL;
+
+FILE *ical_file;
+
+/* in_timezone_name is the real timezone name from the infile 
+ * we are processing.
+ * in_timezone_name is the timezone we are writing. Usually it is the same
+ * than in_timezone_name. 
+ * timezone name is for example Europe/Helsinki */
+char *timezone_name = NULL;  
+char *in_timezone_name = NULL;
+
+int ignore_older = 1970; /* Ignore rules which are older or equal than this */
+
+/* time change table starts here */
+unsigned char *begin_timechanges;           
+
+/* time change type index table starts here */
+unsigned char *begin_timechangetypeindexes; 
+
+/* time change type table starts here */
+unsigned char *begin_timechangetypes;       
+
+/* timezone name table */
+unsigned char *begin_timezonenames;         
+
+unsigned long gmtcnt;
+unsigned long stdcnt;
+unsigned long leapcnt;
+unsigned long timecnt;  /* points when time changes */
+unsigned long typecnt;  /* table of different time changes = types */
+unsigned long charcnt;  /* length of timezone name table */
+
+struct ical_timezone_data {
+    struct tm start_time;
+    int gmt_offset, gmt_offset_hh, gmt_offset_mm;
+    int prev_gmt_offset, prev_gmt_offset_hh, prev_gmt_offset_mm;
+    unsigned int is_dst;
+    char *tz;
+};
+
+struct rdate_prev_data {
+    struct ical_timezone_data data;
+    struct rdate_prev_data   *next;
+};
+
+read_file(const char *file_name, const struct stat *file_stat)
+{
+    FILE *file;
+
+    if (debug > 1) {
+        printf("read_file: start\n");
+        printf("\n***** size of file %s is %d bytes *****\n\n", file_name
+                , file_stat->st_size);
+    }
+    in_buf = malloc(file_stat->st_size);
+    in_head = in_buf;
+    in_tail = in_buf + file_stat->st_size - 1;
+    file = fopen(file_name, "r");
+    fread(in_buf, 1, file_stat->st_size, file);
+    fclose(file);
+    if (debug > 1)
+        printf("read_file: end\n");
+}
+
+long get_long()
+{
+    unsigned long tmp;
+
+    tmp = (((long)in_head[0]<<24)
+         + ((long)in_head[1]<<16)
+         + ((long)in_head[2]<<8)
+         +  (long)in_head[3]);
+    in_head += 4;
+    return(tmp);
+}
+
+int process_header()
+{
+    if (debug > 2)
+        printf("file id: %s\n", in_head);
+    if (strncmp((char *)in_head, "TZif", 4)) { /* we accept version 1 and 2 */
+        return(1);
+    }
+    /* header */
+    in_head += 4; /* type */
+    in_head += 16; /* reserved */
+    gmtcnt  = get_long();
+    if (debug > 2)
+        printf("gmtcnt=%u \n", gmtcnt);
+    stdcnt  = get_long();
+    if (debug > 2)
+        printf("stdcnt=%u \n", stdcnt);
+    leapcnt = get_long();
+    if (debug > 2)
+        printf("leapcnt=%u \n", leapcnt);
+    timecnt = get_long();
+    if (debug > 2)
+        printf("number of time changes: timecnt=%u \n", timecnt);
+    typecnt = get_long();
+    if (debug > 2)
+        printf("number of time change types: typecnt=%u \n", typecnt);
+    charcnt = get_long();
+    if (debug > 2)
+        printf("lenght of different timezone names table: charcnt=%u \n"
+                , charcnt);
+    return(0);
+}
+
+process_local_time_table()
+{ /* points when time changes */
+    unsigned long tmp;
+    int i;
+
+    begin_timechanges = in_head;
+    if (debug > 3)
+        printf("\n***** printing time change dates *****\n");
+    for (i = 0; i < timecnt; i++) {
+        tmp = get_long();
+        if (debug > 3) {
+            printf("GMT %d: %u =  %s", i, tmp
+                    , asctime(gmtime((const time_t*)&tmp)));
+            printf("\tLOC %d: %u =  %s", i, tmp
+                    , asctime(localtime((const time_t*)&tmp)));
+        }
+    }
+}
+
+process_local_time_type_table()
+{ /* pointers to table, which explain how time changes */
+    unsigned char tmp;
+    int i;
+
+    begin_timechangetypeindexes = in_head;
+    if (debug > 3)
+        printf("\n***** printing time change type indekses *****\n");
+    for (i = 0; i < timecnt; i++) { /* we need to walk over the table */
+        tmp = in_head[0];
+        in_head++;
+        if (debug > 3)
+            printf("type %d: %d\n", i, (unsigned int)tmp);
+    }
+}
+
+process_ttinfo_table()
+{ /* table of different time changes = types */
+    long tmp;
+    unsigned char tmp2, tmp3;
+    int i;
+
+    begin_timechangetypes = in_head;
+    if (debug > 3)
+        printf("\n***** printing different time change types *****\n");
+    for (i = 0; i < typecnt; i++) { /* we need to walk over the table */
+        tmp = get_long();
+        tmp2 = in_head[0];
+        in_head++;
+        tmp3 = in_head[0];
+        in_head++;
+        if (debug > 3)
+            printf("%d: gmtoffset:%d isdst:%d abbr:%d\n", i, tmp
+                    , (unsigned int)tmp2, (unsigned int)tmp3);
+    }
+}
+
+process_abbr_table()
+{
+    unsigned char *tmp;
+    int i;
+
+    begin_timezonenames = in_head;
+    if (debug > 3)
+        printf("\n***** printing different timezone names *****\n");
+    tmp = in_head;
+    for (i = 0; i < charcnt; i++) { /* we need to walk over the table */
+        if (debug > 3)
+            printf("Abbr:%d (%d)(%s)\n", i, strlen((char *)(tmp + i)), tmp + i);
+        i += strlen((char *)(tmp + i));
+    }
+    in_head += charcnt;
+}
+
+process_leap_table()
+{
+    unsigned long tmp, tmp2;
+    int i;
+
+    if (debug > 3)
+        printf("\n***** printing leap time table *****\n");
+    for (i = 0; i < leapcnt; i++) { /* we need to walk over the table */
+        tmp = get_long();
+        tmp2 = get_long();
+        if (debug > 3)
+            printf("leaps %d: %u =  %s (%u)", i, tmp
+                    , asctime(localtime((const time_t *)&tmp)), tmp2);
+    }
+}
+
+process_std_table()
+{
+    unsigned char tmp;
+    int i;
+
+    if (debug > 3)
+        printf("\n***** printing std table *****\n");
+    for (i = 0; i < stdcnt; i++) { /* we need to walk over the table */
+        tmp = (unsigned long)in_head[0];
+        in_head++;
+        if (debug > 3)
+            printf("stds %d: %d\n", i, (unsigned int)tmp);
+    }
+}
+
+process_gmt_table()
+{
+    unsigned char tmp;
+    int i;
+
+    if (debug > 3)
+        printf("\n***** printing gmt table *****\n");
+    for (i = 0; i < gmtcnt; i++) { /* we need to walk over the table */
+        tmp = (unsigned long)in_head[0];
+        in_head++;
+        if (debug > 3)
+            printf("gmts %d: %d\n", i, (unsigned int)tmp);
+    }
+}
+
+/* go through the contents of the file and find the positions of 
+ * needed data. Uses global pointer: in_head */
+int process_file(const char *file_name)
+{
+    if (debug > 1)
+        printf("process_file: start\n");
+    if (process_header(file_name)) {
+        printf("File (%s) does not look like tz file. Skipping it.\n"
+                , file_name);
+        return(1);
+    }
+    process_local_time_table();
+    process_local_time_type_table();
+    process_ttinfo_table();
+    process_abbr_table();
+    process_leap_table();
+    process_std_table();
+    process_gmt_table();
+    if (debug > 1)
+        printf("process_file: end\n");
+}
+
+void create_backup_file(char *out_file)
+{
+    char *backup_out_file, backup_ending[]=".old";
+    int out_file_name_len, backup_ending_len, backup_out_file_name_len;
+
+    out_file_name_len = strlen(out_file);
+    backup_ending_len = strlen(backup_ending);
+    backup_out_file_name_len = out_file_name_len + backup_ending_len;
+
+    backup_out_file = malloc(backup_out_file_name_len + 1);
+    strncpy(backup_out_file, out_file, out_file_name_len);
+    backup_out_file[out_file_name_len] = '\0';
+    strncat(backup_out_file, backup_ending, backup_ending_len);
+
+    if (rename(out_file, backup_out_file)) {
+        printf("Error creating backup file %s:\n", backup_out_file);
+        perror("\trename");
+    }
+    free(backup_out_file);
+}
+
+void create_ical_directory(const char *in_file_name)
+{
+    char *ical_dir_name;
+
+    if (debug > 1)
+        printf("create_ical_directory: start\n");
+    ical_dir_name = strdup(&in_file_name[in_file_base_offset]);
+    if (mkdir(ical_dir_name, 0777)) {
+        if (errno == EEXIST) {
+            if (debug > 2) {
+                printf("create_ical_directory: ignoring error (%s)\n"
+                        , ical_dir_name);
+                perror("\tcreate_ical_directory: mkdir");
+            }
+        }
+        else {
+            printf("create_ical_directory: creating directory=(%s) failed\n"
+                    , ical_dir_name);
+            perror("\tcreate_ical_directory: mkdir");
+        }
+    }
+    free(ical_dir_name);
+    if (debug > 1)
+        printf("create_ical_directory: end\n");
+}
+
+int create_ical_file(const char *in_file_name)
+{
+    struct stat out_stat;
+    char ical_ending[]=".ics";
+    int in_file_name_len, ical_ending_len, out_file_name_len;
+
+    if (debug > 1)
+        printf("create_ical_file: start\n");
+    if (out_file == NULL) { /* this is the normal case */
+        in_file_name_len = strlen(&in_file_name[in_file_base_offset]);
+        ical_ending_len = strlen(ical_ending);
+        out_file_name_len = in_file_name_len + ical_ending_len;
+
+        out_file = malloc(out_file_name_len + 1);
+        strncpy(out_file, &in_file_name[in_file_base_offset], in_file_name_len);
+        out_file[in_file_name_len] = '\0';
+        strncat(out_file, ical_ending, ical_ending_len);
+
+        in_timezone_name = strdup(&in_file_name[in_file_base_offset 
+                + strlen("zoneinfo/")]);
+        timezone_name = strdup(in_timezone_name);
+        if (debug > 1) {
+            printf("create_ical_file:\n\tinfile:(%s)\n\toutfile:(%s)\n\tin timezone:(%s)\n\tout timezone:(%s)\n"
+                    , in_file_name, out_file, in_timezone_name, timezone_name);
+        }
+    }
+
+    if (stat(out_file, &out_stat) == -1) { /* error */
+        if (errno != ENOENT) { /* quite ok. we just need to create it */
+            perror("\tstat");
+            return(1);
+        }
+    }
+    else { /* file exists, need to create backup */
+        create_backup_file(out_file);
+    }
+
+    if (!(ical_file = fopen(out_file, "w"))) {
+        if (errno == ENOENT) {
+            /* This error probably happens since we miss some directories
+             * before the actual file name. Let's try to create those and
+             * then create the file again */
+            char *s_dir;
+
+            s_dir = out_file;
+            for (s_dir = strchr(s_dir, '/'); s_dir != NULL
+                    ; s_dir = strchr(s_dir, '/')) {
+                *s_dir = '\0';
+                printf("create_ical_file: creating directory=(%s)\n", out_file);
+                if (mkdir(out_file, 0777))
+                {
+                    if (errno == EEXIST) {
+                        printf("create_ical_file: ignoring error (%s)\n"
+                                , out_file);
+                        perror("\tcreate_ical_file: mkdir 1");
+                    }
+                    else {
+                        printf("create_ical_file: creating directory=(%s) failed\n"
+                                , out_file);
+                        perror("\tcreate_ical_file: mkdir 2");
+                        return(2);
+                    }
+                }
+                *s_dir = '/';
+                *s_dir++;
+            }
+            if (!(ical_file = fopen(out_file, "w"))) {
+                /* still failed; real error, which we can not fix */
+                printf("create_ical_file: outfile creation failed (%s)\n"
+                        , out_file);
+                perror("\tfopen2");
+                return(3);
+            }
+        }
+        else {
+            printf("create_ical_file: outfile creation failed (%s)\n"
+                    , out_file);
+            perror("\tfopen1");
+            return(4);
+        }
+    }
+    if (debug > 1)
+        printf("create_ical_file: end\n");
+    return(0);
+}
+
+void write_ical_str(char *data)
+{
+    int len;
+
+    len = strlen(data);
+    fwrite(data, 1, len, ical_file);
+}
+
+void write_ical_header()
+{
+    char *vcalendar = "BEGIN:VCALENDAR\nPRODID:-//Xfce//NONSGML Orage Olson-VTIMEZONE Converter//EN\nVERSION:2.0\n";
+    char *vtimezone = "BEGIN:VTIMEZONE\nTZID:/softwarestudio.org/Olson_20011030_5/";
+    char *xlic = "X-LIC-LOCATION:";
+    char *line = "\n";
+
+    write_ical_str(vcalendar);
+    write_ical_str(vtimezone);
+    write_ical_str(timezone_name);
+    write_ical_str(line);
+    write_ical_str(xlic);
+    write_ical_str(timezone_name);
+    write_ical_str(line);
+}
+
+struct ical_timezone_data wit_get_data(int i
+        , struct ical_timezone_data *prev) {
+    unsigned long tc_time;
+    unsigned int tct_i, abbr_i;
+    struct ical_timezone_data data;
+
+    /* get timechange time */
+    in_head = begin_timechanges;
+    in_head += 4*i; /* point to our row */
+    tc_time = get_long();
+    /* later...when we have all the data
+    localtime_r((const time_t *)&tc_time, &data.start_time );
+    */
+
+    /* get timechange type index */
+    in_head = begin_timechangetypeindexes;
+    tct_i = (unsigned int)in_head[i];
+
+    /* get timechange type */
+    in_head = begin_timechangetypes;
+    in_head += 6*tct_i;
+    data.gmt_offset = (int)get_long();
+    data.gmt_offset_hh = data.gmt_offset / (60*60);
+    data.gmt_offset_mm = (data.gmt_offset - data.gmt_offset_hh * (60*60)) / 60;
+    data.is_dst = in_head[0];
+    abbr_i =  in_head[1];
+
+    /* get timezone name */
+    in_head = begin_timezonenames;
+    data.tz = (char *)in_head + abbr_i;
+
+    /* ical needs the startime in the previous (=current) time, so we need to
+     * adjust by the difference. 
+     * First round we do not have the prev data, so we can not do this. */
+    if (i) { 
+        tc_time += (prev->gmt_offset - data.gmt_offset);
+    }
+    localtime_r((const time_t *)&tc_time, &data.start_time );
+    /* we need to remember also the previous value. Note that this is from
+     * dst if we are in std and vice versa */
+    data.prev_gmt_offset_hh = prev->gmt_offset_hh;
+    data.prev_gmt_offset_mm = prev->gmt_offset_mm;
+
+    return(data);
+}
+
+int wit_get_rrule(struct ical_timezone_data *prev
+        , struct ical_timezone_data *cur) {
+    int monthdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+    int rrule_day_cnt;
+    int leap_year = 0, prev_leap_year = 0;
+
+    if (cur->gmt_offset == prev->gmt_offset
+    &&  !strcmp(cur->tz, prev->tz)) {
+        /* 2) check if we can use RRULE. 
+         * We only check yearly same month and same week day changing
+         * rules (which should cover most real world cases). */
+        if (cur->start_time.tm_year == prev->start_time.tm_year + 1
+        &&  cur->start_time.tm_mon  == prev->start_time.tm_mon
+        &&  cur->start_time.tm_wday == prev->start_time.tm_wday
+        &&  cur->start_time.tm_hour == prev->start_time.tm_hour
+        &&  cur->start_time.tm_min  == prev->start_time.tm_min
+        &&  cur->start_time.tm_sec  == prev->start_time.tm_sec) {
+            /* so far so good, now check that our weekdays are on
+             * the same week */
+            if (prev->start_time.tm_mon == 1) {
+                if (((prev->start_time.tm_year%4) == 0)
+                && (((prev->start_time.tm_year%100) != 0) 
+                    || ((prev->start_time.tm_year%400) == 0)))
+                    prev_leap_year = 1; /* leap year, february has 29 days */
+            }
+            /* most often these change on the last week day
+             * (and on sunday, but that does not matter now) */
+            if ((monthdays[cur->start_time.tm_mon] + leap_year - 
+                    cur->start_time.tm_mday) < 7
+            &&  (monthdays[prev->start_time.tm_mon] + prev_leap_year - 
+                    prev->start_time.tm_mday) < 7) {
+                /* yep, it is last */
+                rrule_day_cnt = -1;
+            }
+            else if (cur->start_time.tm_mday < 8
+                 &&  prev->start_time.tm_mday < 8) {
+                /* ok, it is first */
+                rrule_day_cnt = 1;
+            }
+            else if (cur->start_time.tm_mday < 15
+                 &&  prev->start_time.tm_mday < 15
+                 /* prevent moving from rule 1 (prev) to rule 2 (cur) */
+                 &&  cur->start_time.tm_mday >= 8 
+                 &&  prev->start_time.tm_mday >= 8) {
+                /* fine, it is second */
+                rrule_day_cnt = 2;
+            }
+            else if (cur->start_time.tm_mday < 22
+                 &&  prev->start_time.tm_mday < 22
+                 /* prevent moving from rule 1 or 2 (prev) to rule 3 (cur) */
+                 &&  cur->start_time.tm_mday >= 15 
+                 &&  prev->start_time.tm_mday >= 15) {
+                /* must be the third then */
+                rrule_day_cnt = 3;
+            }
+            else { 
+                /* give up, it did not work after all.
+                 * It is quite possible that rule changed, but
+                 * in that case we need to write this out anyway */
+                rrule_day_cnt = 100; /* RDATE is still possible */
+            }
+        }
+        else { /* 2) failed, need to use RDATE */
+            rrule_day_cnt = 100;
+        }
+    }
+    else { /* 1) not possible to use RRULE nor RDATE, need new entry */
+        rrule_day_cnt = 0;
+    }
+    return(rrule_day_cnt);
+}
+
+void wit_write_data(int rrule_day_cnt, struct rdate_prev_data *rdate
+        , struct ical_timezone_data *first
+        , struct ical_timezone_data *prev)
+{
+    char str[100];
+    int len;
+    char *dst_begin="BEGIN:DAYLIGHT\n";
+    char *dst_end="END:DAYLIGHT\n";
+    char *std_begin="BEGIN:STANDARD\n";
+    char *std_end="END:STANDARD\n";
+    char *day[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA" };
+    struct rdate_prev_data *tmp_data = NULL, *tmp_data2;
+    struct ical_timezone_data tmp_prev;
+
+    if (debug > 3)
+        printf("\tWriting data (%s) %s"
+                , first->is_dst ? "dst" : "std"
+                , asctime(&first->start_time));
+
+    /****** First Check that we really need to write this record *****/
+    if (rrule_day_cnt == 100) { /* RDATE rule */
+        if (rdate == NULL) {
+        /* we actually have nothing to print. This happens seldom, but
+         * is possible if we found RDATE rule, but after that we actually
+         * found that it is the first RRULE element and we did not push
+         * anything into the rdate store */
+            if (debug > 3)
+                printf("\tWrite aborted. RDATE rule changed to RRULE\n");
+            return;
+        }
+    }
+    else if (rrule_day_cnt == 0) { /* non repeating rule */
+        if ((prev->start_time.tm_year + 1900) <= ignore_older) {
+        /* We again have nothing to print. We got non repeating case, which
+         * happens before our limit year */
+            if (debug > 3)
+                printf("\tWrite aborted. Too old non repeating year\n");
+            return;
+        }
+    }
+
+    /****** Write the data ******/
+    if (prev->is_dst)
+        write_ical_str(dst_begin);
+    else
+        write_ical_str(std_begin);
+
+    len = snprintf(str, 30, "TZOFFSETFROM:%+03d%02d\n"
+            , prev->prev_gmt_offset_hh, prev->prev_gmt_offset_mm);
+    fwrite(str, 1, len, ical_file);
+
+    len = snprintf(str, 30, "TZOFFSETTO:%+03d%02d\n"
+            , prev->gmt_offset_hh, prev->gmt_offset_mm);
+    fwrite(str, 1, len, ical_file);
+
+    len = snprintf(str, 99, "TZNAME:%s\n", prev->tz);
+    fwrite(str, 1, len, ical_file);
+
+    len = snprintf(str, 30, "DTSTART:%04d%02d%02dT%02d%02d%02d\n"
+            , first->start_time.tm_year + 1900
+            , first->start_time.tm_mon  + 1
+            , first->start_time.tm_mday
+            , first->start_time.tm_hour
+            , first->start_time.tm_min
+            , first->start_time.tm_sec);
+    fwrite(str, 1, len, ical_file);
+
+    if (rrule_day_cnt) { /* we had repeating appointment */
+        if (rrule_day_cnt < 10) { /* RRULE */
+            if (debug > 3)
+                printf("\t\t...RRULE\n");
+            len = snprintf(str, 50, "RRULE:FREQ=YEARLY;BYMONTH=%d;BYDAY=%d%s\n"
+                    , first->start_time.tm_mon + 1
+                    , rrule_day_cnt
+                    , day[first->start_time.tm_wday]);
+            fwrite(str, 1, len, ical_file);
+        }
+        else { /* RDATE */
+            if (debug > 3)
+                printf("\t\t...RDATE\n");
+            for (tmp_data = rdate; tmp_data ; ) {
+                tmp_prev = tmp_data->data;
+                len = snprintf(str, 30, "RDATE:%04d%02d%02dT%02d%02d%02d\n"
+                        , tmp_prev.start_time.tm_year + 1900
+                        , tmp_prev.start_time.tm_mon  + 1
+                        , tmp_prev.start_time.tm_mday
+                        , tmp_prev.start_time.tm_hour
+                        , tmp_prev.start_time.tm_min
+                        , tmp_prev.start_time.tm_sec);
+                fwrite(str, 1, len, ical_file);
+
+                tmp_data2 = tmp_data;
+                tmp_data = tmp_data->next;
+                free(tmp_data2);
+            }
+        }
+    }
+    else {
+        if (debug > 3)
+            printf("\t\t...single\n");
+        len = snprintf(str, 30, "RDATE:%04d%02d%02dT%02d%02d%02d\n"
+                , prev->start_time.tm_year + 1900
+                , prev->start_time.tm_mon  + 1
+                , prev->start_time.tm_mday
+                , prev->start_time.tm_hour
+                , prev->start_time.tm_min
+                , prev->start_time.tm_sec);
+        fwrite(str, 1, len, ical_file);
+    }
+
+    if (prev->is_dst)
+        write_ical_str(dst_end);
+    else
+        write_ical_str(std_end);
+}
+
+void write_ical_timezones()
+{
+    int i;
+    struct ical_timezone_data ical_data
+        , ical_data_prev = { {0, 0, 0, 0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, NULL}
+        , data_prev_std = { {0, 0, 0, 0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, NULL}
+        , data_first_std = { {0, 0, 0, 0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, NULL}
+        , data_prev_dst = { {0, 0, 0, 0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, NULL}
+        , data_first_dst = { {0, 0, 0, 0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, NULL};
+        /* pointers either to data_prev_std and data_first_std
+         * or to data_prev_dst and data_first_dst. 
+         * These avoid having separate code paths for std and dst */
+    struct ical_timezone_data *p_data_prev, *p_data_first;
+
+    int std_init_done = 0;
+    int dst_init_done = 0;
+    int rrule_day_cnt_std = 0, rrule_day_cnt_std_prev = 0;
+    int rrule_day_cnt_dst = 0, rrule_day_cnt_dst_prev = 0;
+        /* pointers either to rrule_day_cnt_std and rrule_day_cnt_std_prev
+         * or to rrule_day_cnt_dst and rrule_day_cnt_dst_prev. 
+         * These avoid having separate code paths for std and dst */
+    int *p_rrule_day_cnt = 0, *p_rrule_day_cnt_prev = 0;
+    struct rdate_prev_data *prev_dst_data = NULL, *prev_std_data = NULL
+        /* points either to prev_dst_data or to prev_std_data */
+        , **p_prev_data 
+        , *tmp_data = NULL, *tmp_data2 = NULL;
+
+    /* we are processing "in_timezone_name" so we know it exists in this 
+     * system and it is then safe to use that in the localtime conversion */
+    if (setenv("TZ", in_timezone_name, 1)) {
+        printf("changing timezone=(%s) failed\n", in_timezone_name);
+        perror("\tsetenv");
+        return;
+    }
+    for (i = 0; i < timecnt; i++) {
+    /***** get data *****/
+        ical_data = wit_get_data(i, &ical_data_prev);
+        if (i == 0) { /* first round, do starting values */
+            ical_data_prev = ical_data;
+        }
+
+        if (ical_data.is_dst) {
+            if (!dst_init_done) {
+                data_first_dst = ical_data;
+                data_prev_dst  = ical_data;
+                dst_init_done  = 1;
+                if (debug > 2)
+                    printf("init %d = (%s) %s", i
+                            , ical_data.is_dst ? "dst" : "std"
+                            , asctime(&ical_data.start_time));
+                continue; /* we never write the first record */
+            }
+            p_data_first = &data_first_dst;
+            p_data_prev = &data_prev_dst;
+            p_rrule_day_cnt = &rrule_day_cnt_dst;
+            p_rrule_day_cnt_prev = &rrule_day_cnt_dst_prev;
+            p_prev_data = &prev_dst_data;
+        }
+        else { /* !dst == std */
+            if (!std_init_done) {
+                data_first_std = ical_data;
+                data_prev_std  = ical_data;
+                std_init_done  = 1;
+                if (debug > 2)
+                    printf("init %d = (%s) %s", i
+                            , ical_data.is_dst ? "dst" : "std"
+                            , asctime(&ical_data.start_time));
+                continue; /* we never write the first record */
+            }
+            p_data_first = &data_first_std;
+            p_data_prev = &data_prev_std;
+            p_rrule_day_cnt = &rrule_day_cnt_std;
+            p_rrule_day_cnt_prev = &rrule_day_cnt_std_prev;
+            p_prev_data = &prev_std_data;
+        }
+
+    /***** check if we need this data *****/
+        /* we only take newer than threshold values */
+        if (ical_data.start_time.tm_year + 1900 <= ignore_older) {
+            if (debug > 2)
+                printf("skip %d = (%s) %s", i
+                        , ical_data.is_dst ? "dst" : "std"
+                        , asctime(&ical_data.start_time));
+            ical_data_prev = ical_data;
+            *p_data_first = ical_data;
+            *p_data_prev = ical_data;
+            *p_rrule_day_cnt_prev = *p_rrule_day_cnt;
+            *p_rrule_day_cnt = wit_get_rrule(p_data_prev, &ical_data);
+            /* without p_ variables this looked like:
+            if (ical_data.is_dst) {
+                data_first_dst = ical_data;
+                data_prev_dst  = ical_data;
+                rrule_day_cnt_dst_prev = rrule_day_cnt_dst;
+                rrule_day_cnt_dst = wit_get_rrule(&data_prev_dst, &ical_data);
+            }
+            else {
+                data_first_std = ical_data;
+                data_prev_std  = ical_data;
+                rrule_day_cnt_std_prev = rrule_day_cnt_std;
+                rrule_day_cnt_std = wit_get_rrule(&data_prev_std, &ical_data);
+            }
+            */
+            continue;
+        }
+        if (debug > 2)
+            printf("process %d: (%s) from %02d:%02d to %02d:%02d (%s) %s", i
+                    , ical_data.is_dst ? "dst" : "std"
+                    , ical_data.prev_gmt_offset_hh,ical_data.prev_gmt_offset_mm
+                    , ical_data.gmt_offset_hh, ical_data.gmt_offset_mm
+                    , ical_data.tz
+                    , asctime(&ical_data.start_time));
+    /***** check if we can shortcut the entry with RRULE or RDATE *****/
+        /* 1) check if it is similar to the previous values */
+        *p_rrule_day_cnt_prev = *p_rrule_day_cnt;
+        *p_rrule_day_cnt = wit_get_rrule(p_data_prev, &ical_data);
+        if (*p_rrule_day_cnt
+        && (*p_rrule_day_cnt == *p_rrule_day_cnt_prev
+            && *p_rrule_day_cnt_prev)) {
+            /* we continue with either real RRULE or RDATE */
+            if (*p_rrule_day_cnt < 10) {
+                /* we found RRULE, so we do not have to do anything 
+                 * since this just continues. */
+                if (debug > 3)
+                    printf("\tRRULE value found\n");
+                *p_data_prev = ical_data;
+            }
+            else { /* we actually found RDATE */
+                if (debug > 3)
+                    printf("\tRDATE value found\n");
+                if (p_data_prev->start_time.tm_year + 1900 <= ignore_older) {
+                    /* do not push old values into queue, ignore them */
+                    if (debug > 3)
+                        printf("\tAborted push to RRULE queue, too old (%s) %s"
+                                , p_data_prev->is_dst ? "dst" : "std"
+                                , asctime(&p_data_prev->start_time));
+                }
+                else {
+                    if (debug > 3)
+                        printf("\tpushed to RRULE queue (%s) %s"
+                                , p_data_prev->is_dst ? "dst" : "std"
+                                , asctime(&p_data_prev->start_time));
+                    for (tmp_data = *p_prev_data; tmp_data ; ) {
+                        /* find the last one, which is still empty */
+                        tmp_data2 = tmp_data;
+                        tmp_data  = tmp_data->next;
+                    }
+                    tmp_data = malloc(sizeof(struct rdate_prev_data));
+                    tmp_data->data = *p_data_prev;
+                    tmp_data->next = NULL; /* last */
+                    if (!*p_prev_data) {
+                        *p_prev_data = tmp_data;
+                    }
+                    else {
+                        tmp_data2->next = tmp_data;
+                    }
+                }
+                *p_data_prev = ical_data;
+            }
+        }
+        else { /* not RRULE or we changed to/from RRULE from/to RDATE, 
+                * so write previous and init new round */
+            if (debug > 3)
+                printf("\tnon repeating value found\n");
+            wit_write_data(*p_rrule_day_cnt_prev, *p_prev_data
+                    , p_data_first, p_data_prev);
+            if (*p_rrule_day_cnt_prev > 10 && *p_rrule_day_cnt < 10) {
+                /* we had RDATE and now we found RRULE */
+                /* so this was actually the first RRULE */
+                if (debug > 3)
+                    printf("\tchanged from RDATE to RRULE\n");
+                if (p_data_prev->start_time.tm_year + 1900 <= ignore_older) {
+                    /* we ignore too old record and take current as first */
+                    if (debug > 3) {
+                        printf("\tIgnoring too old (%s) %s"
+                                , p_data_prev->is_dst ? "dst" : "std"
+                                , asctime(&p_data_prev->start_time));
+                        printf("\tUsing RRULE (%s) %s"
+                                , ical_data.is_dst ? "dst" : "std"
+                                , asctime(&ical_data.start_time));
+                    }
+                    *p_data_first = ical_data; 
+                }
+                else {
+                    if (debug > 3)
+                        printf("\tUsing RRULE (%s) %s"
+                                , p_data_prev->is_dst ? "dst" : "std"
+                                , asctime(&p_data_prev->start_time));
+                    *p_data_first = *p_data_prev; 
+                }
+            }
+            else {
+                *p_data_first = ical_data;
+            }
+            *p_data_prev  = ical_data;
+            *p_prev_data  = NULL;
+        } 
+        ical_data_prev = ical_data;
+    } /* for (i = 0; i < timecnt; i++) */
+    /* need to write the last one also */
+    if (debug > 3)
+        printf("writing last values:\n");
+    wit_write_data(rrule_day_cnt_std_prev, prev_std_data
+            , &data_first_std, &data_prev_std);
+    wit_write_data(rrule_day_cnt_dst_prev, prev_dst_data
+            , &data_first_dst, &data_prev_dst);
+}
+
+void write_ical_ending()
+{
+    char *end= "END:VTIMEZONE\nEND:VCALENDAR\n";
+
+    write_ical_str(end);
+}
+
+/* FIXME: need to check that if OUTFILE is given as a parameter,
+ * INFILE is not a directory (or make outfile to act like directory also ? */
+int write_ical_file(const char *in_file_name, const struct stat *in_file_stat)
+{
+    if (debug > 1)
+        printf("write_ical_file: start\n");
+    if (create_ical_file(in_file_name))
+        return(1);
+    write_ical_header();
+    write_ical_timezones();
+    write_ical_ending();
+    fclose(ical_file);
+    if (debug > 1)
+        printf("write_ical_file: end\n");
+    return(0);
+}
+
+int par_version()
+{
+    printf(
+        "orage_tz_to_ical_convert version (Orage utility) %s\n"
+        "Copyright © 2008 Juha Kautto\n"
+        "License: GNU GPL <http://gnu.org/licenses/gpl.html>\n"
+        "This is free software: you are free to change and redistribute it.\n"
+        "There is NO WARRANTY.\n"
+        , version
+    );
+    return(1);
+}
+
+int par_help()
+{
+    printf(
+        "orage_tz_to_ical_convert converts operating system timezone (tz) files\n"
+        "to ical format. Often you find tz files in /usr/share/zoneinfo/\n\n"
+        "Usage: orage_tz_to_ical_convert [in_file] [out_file] [parameters]...\n"
+        "parameters:\n"
+        "\t -h, -?, --help\t\t this help text\n"
+        "\t -V, --version\t\t orage_tz_to_ical_convert version\n"
+        "\t -i, --infile\t\t tz file name from operating system.\n"
+        "\t\t\t\t If this is directory, all files in it are\n"
+        "\t\t\t\t processed\n"
+        "\t -o, --outfile\t\t ical file name to be written\n"
+        "\t\t\t\t If this is directory, file is written into\n"
+        "\t\t\t\t it using the same name, but adding .ics to\n"
+        "\t\t\t\t the end of it.\n"
+    );
+    return(1);
+}
+
+int get_parameters_popt(int argc, const char **argv)
+{
+    int par_type = 0, val, res = 0, i;
+    char *tmp_str = NULL;
+    poptContext popt_con;
+    struct poptOption parameters[] = {
+        /*
+        {"help", 'h', POPT_ARG_NONE, &par_type, 1
+            , "this help text", NULL}
+            */
+        {"version", 'V', POPT_ARG_NONE, &par_type, 2
+            , "orage_tz_to_ical_convert version", NULL},
+        {"infile", 'i', POPT_ARG_STRING, &tmp_str, 3
+            , "tz file name from operating system."
+              " If this is directory, all files in it are processed"
+            , "filename"},
+        {"outfile", 'o', POPT_ARG_STRING, &tmp_str, 4
+            , "ical file name to be written."
+              " This can not be directory."
+              " It is meant to be used together with timezone parameter."
+            , "filename"},
+        {"message", 'm', POPT_ARG_INT, &debug, 5
+            , "message level. How much exra information is shown."
+              " 0 is least and 10 is highest (1=default)."
+            , "level"},
+        {"limit", 'l', POPT_ARG_INT, &ignore_older, 6
+            , "limit proocessing to newer than this year."
+            , "year"},
+        {"timezone", 't', POPT_ARG_STRING, &tmp_str, 7
+            , "timezone name. For example Europe/Helsinki"
+            , "filename"},
+        {"norecursive", 'r', POPT_ARG_INT, &only_one_level, 8
+            , "process only main directory, instead of all subdirectories."
+              " 0 = recursive  1 = only main directory (0=default)."
+            , "level"},
+        {"excluce count", 'c', POPT_ARG_INT, &excl_dir_cnt, 9
+            , "number of excluded directories."
+              " 5 = default (You only need this if you have more"
+              " than 5 excluded directories)."
+            , "count"},
+        {"exclude", 'x', POPT_ARG_STRING, &tmp_str, 10
+            , "do not process this directory, skip it."
+              " You can give several directories with separate parameters."
+              " By default directories right and posix are excluded, but if"
+              " you use this parameter, you have to specify those also."
+            , "directory"},
+        POPT_AUTOHELP
+        {NULL, '\0', POPT_ARG_NONE, NULL, 0, NULL, NULL}
+    };
+
+    if (debug > 1)
+        printf("get_parameters_popt: start\n");
+    popt_con = poptGetContext("Orage", argc, argv, parameters, 0);
+    poptSetOtherOptionHelp(popt_con, "[OPTION...] [INFILE]");
+    while ((val = poptGetNextOpt(popt_con)) > 0) {
+        if (debug > 2)
+            printf("Read parameter %d (%s)\n", val, tmp_str);
+        switch (val) {
+            case 1:
+                par_help();
+                res = val;
+                break;
+            case 2:
+                par_version();
+                res = val;
+                break;
+            case 3:
+                in_file = strdup(tmp_str);
+                break;
+            case 4:
+                out_file = strdup(tmp_str);
+                break;
+            case 5:
+                printf("Debug level set to %d\n", debug);
+                break;
+            case 6:
+                printf("Only including newer than year %d time changes\n"
+                        , ignore_older);
+                break;
+            case 7:
+                timezone_name = strdup(tmp_str);
+                break;
+            case 8:
+                if (only_one_level)
+                    printf("Processing only main directory\n");
+                else
+                    printf("Processing recursively all directories\n");
+                break;
+            case 9:
+                if (excl_dir) 
+                printf("Number of excluded directories set to %d\n"
+                        , excl_dir_cnt);
+                break;
+            case 10:
+                printf("Skipping directory (%s)\n", tmp_str);
+                if (!excl_dir) {
+                    excl_dir = calloc(excl_dir_cnt + 1, sizeof(char *));
+                }
+                for (i = 0; (i <= excl_dir_cnt) && excl_dir[i]; i++)
+                    /* loop until array ends or we find empty place */
+                    ;
+                if (i < excl_dir_cnt) {
+                    excl_dir[i] = strdup(tmp_str);
+                }
+                else {
+                    printf("not enough room (%s). Use bigger exclude count\n"
+                            , tmp_str);
+                    res = val;
+                }
+                break;
+            default:
+                res = val;
+                printf("unknown parameter\n");
+        }
+    }
+    if (val != -1) {
+        res = val;
+        printf("Error in parameter handling (popt) %s: %s\n"
+                , poptBadOption(popt_con, POPT_BADOPTION_NOALIAS)
+                , poptStrerror(val));
+    }
+    else { /* process optional arguments: infile, outfile */
+        if ((tmp_str = (char *)poptGetArg(popt_con))) {
+            if (in_file)
+                free(in_file);
+            in_file = strdup(tmp_str);
+            while ((tmp_str = (char *)poptGetArg(popt_con))) {
+                printf("ignoring leftover parameter (%s)\n", tmp_str);
+            }
+        }
+    }
+    poptFreeContext(popt_con);
+    if (debug > 1)
+        printf("get_parameters_popt: end\n");
+    return(res);
+}
+
+void add_zone_tabs()
+{
+    /* ical index filename is zoneinfo/zones.tab 
+     * and os file is zone.tab */
+    char ical_zone[]="zoneinfo/zones.tab";
+    FILE *os_zone_tab, *ical_zone_tab;
+    struct stat ical_zone_stat;
+    char *ical_zone_buf, *line_end, *buf;
+    int offset; /* offset to next timezone in libical zones.tab */
+    int before; /* current timezone appears before ours in zones.tab */
+    int len = strlen(timezone_name), buf_len;
+
+    if (debug > 1)
+        printf("add_zone_tabs: start\n");
+    ical_zone_tab = fopen(ical_zone, "r+"); 
+    if (ical_zone_tab == NULL) { /* does not exist or other error */
+        printf("Error opening (%s) file, creating it.\n", ical_zone);
+        perror("\tfopen");
+        /*  let's try to create it */
+        ical_zone_tab = fopen(ical_zone, "w"); 
+        if (ical_zone_tab == NULL) { /* real, unrecoverable error */
+            printf("Error creating (%s) file. Do it manually.\n", ical_zone);
+            perror("\tfopen");
+            return; /* we do not update it */
+        }
+        else { /* success, reopen to correct mode */
+            fclose(ical_zone_tab);
+            ical_zone_tab = fopen(ical_zone, "r+"); 
+            if (ical_zone_tab == NULL) { /* does not exist or other error */
+                printf("Error opening (%s) file. Update it manually.\n"
+                        , ical_zone);
+                perror("\tfopen");
+                return; /* we do not update it */
+            }
+        }
+    }
+    if (stat(ical_zone, &ical_zone_stat) == -1) {
+        perror("\tstat");
+        fclose(ical_zone_tab);
+        return;
+    }
+
+    ical_zone_buf = malloc(ical_zone_stat.st_size+1);
+    fread(ical_zone_buf, 1, ical_zone_stat.st_size, ical_zone_tab);
+    if (ferror(ical_zone_tab)) {
+        printf("add_zone_tabs: error reading (%s).\n", ical_zone);
+        perror("\tfread");
+        free(ical_zone_buf);
+        fclose(ical_zone_tab);
+        return;
+    }
+    ical_zone_buf[ical_zone_stat.st_size] = 0; /* end with null */
+    if (strstr(ical_zone_buf, timezone_name)) {
+        if (debug > 1)
+            printf("add_zone_tabs: timezone exists already, returning.\n");
+        free(ical_zone_buf);
+        fclose(ical_zone_tab);
+        return;
+    }
+    for (offset = 18, before = 1; 
+            offset < ical_zone_stat.st_size && before;
+            offset = line_end - ical_zone_buf + 19) {
+        line_end = strchr(&ical_zone_buf[offset], '\n');
+        if (line_end == NULL)
+            break; /* end of file */
+        if (strncmp(&ical_zone_buf[offset], timezone_name, len) > 0) {
+            before = 0;
+            break;
+        }
+    }
+    buf_len=len+18+1; /* +1=add \n */
+    buf = malloc(buf_len+1); /* +1=add \0 */
+    sprintf(buf, "+0000000 -0000000 %s\n", timezone_name);
+    if (before) {
+        if (fseek(ical_zone_tab, 0l, SEEK_END))
+            perror("\tfseek-end");
+        else
+            fwrite(buf, 1, buf_len, ical_zone_tab);
+    }
+    else {
+        if (fseek(ical_zone_tab, offset-18, SEEK_SET))
+            perror("\tfseek-set");
+        else {
+            fwrite(buf, 1, buf_len, ical_zone_tab);
+            buf_len = strlen(&ical_zone_buf[offset-18]);
+            fwrite(&ical_zone_buf[offset-18], 1, buf_len, ical_zone_tab);
+        }
+    }
+
+    free(buf);
+    free(ical_zone_buf);
+    fclose(ical_zone_tab);
+    if (debug > 1)
+        printf("add_zone_tabs: end\n");
+}
+
+/* The main code. This is called once per each file found */
+int file_call(const char *file_name, const struct stat *sb, int flags
+        , struct FTW *f)
+{
+    int i;
+
+    if (debug > 1)
+        printf("file_call: start\n");
+    file_cnt++;
+    /* we are only interested about files and directories we can access */
+    if (flags == FTW_F) {
+        if (debug > 0)
+            printf("\t\tfile_call: processing file=(%s)\n", file_name);
+        if (only_one_level && (f->level > 1)) {
+            /* we never actually come here since we use FTW_SKIP_SUBTREE
+             * in the directory level */
+            if (debug > 0)
+                printf("\t\tfile_call: skipping it, not on top level\n");
+            return(FTW_CONTINUE);
+        }
+        read_file(file_name, sb);
+        process_file(file_name);
+        write_ical_file(file_name, sb);
+        add_zone_tabs();
+        free(in_buf);
+        free(out_file);
+        out_file = NULL;
+        free(in_timezone_name);
+        free(timezone_name);
+    }
+    else if (flags == FTW_D) {
+        if (debug > 0)
+            printf("\tfile_call: processing directory=(%s)\n", file_name);
+        if (only_one_level && f->level > 0) {
+            if (debug > 0)
+                printf("\t\tfile_call: skipping it, not on top level\n");
+            return(FTW_SKIP_SUBTREE);
+        }
+        /* need to check if we have excluded directory */
+        for (i = 0; (i <= excl_dir_cnt) && excl_dir[i]; i++) {
+            if (strcmp(excl_dir[i],  file_name+f->base) == 0) {
+                if (debug > 0)
+                    printf("\t\tfile_call: skipping excluded directory (%s)\n"
+                            , file_name+f->base);
+                return(FTW_SKIP_SUBTREE);
+            }
+        }
+        create_ical_directory(file_name);
+    }
+    else if (flags == FTW_SL) {
+        if (debug > 0) {
+            printf("\t\tfile_call: skipping symbolic link=(%s)\n", file_name);
+        }
+    }
+    else {
+        if (debug > 0) {
+            printf("\t\tfile_call: skipping inaccessible file=(%s)\n", file_name);
+        }
+    }
+
+    if (debug > 1)
+        printf("file_call: end\n");
+    return(FTW_CONTINUE);
+}
+
+/* check the parameters and use defaults when possible */
+int check_parameters()
+{
+    char *s_tz, *last_tz = NULL, tz[]="/zoneinfo", tz2[]="zoneinfo/";
+    int tz_len, i;
+    struct stat in_stat;
+
+    if (debug > 1)
+        printf("check_parameters: start\n");
+    if (in_file == NULL) /* in file not found */
+        in_file = strdup(DEFAULT_ZONEINFO_DIRECTORY);
+
+    if (in_file[0] != '/') {
+        printf("check_parameters: in_file name (%s) is not absolute file name. Ending\n"
+                , in_file);
+        return(1);
+    }
+    if (stat(in_file, &in_stat) == -1) { /* error */
+        perror("\tcheck_parameters: stat");
+        return(2);
+    }
+    if (S_ISDIR(in_stat.st_mode)) {
+        in_file_is_dir = 1;
+        if (timezone_name) {
+            printf("\tcheck_parameters: when infile (%s) is directory, you can not specify timezone name (%s), but it is copied from each in file. Ending\n"
+                , in_file, timezone_name);
+            return(3);
+        }
+        if (out_file) {
+            printf("\tcheck_parameters: when infile (%s) is directory, you can not specify outfile name (%s), but it is copied from each in file. Ending\n"
+                , in_file, out_file);
+            return(3);
+        }
+    }
+    else {
+        in_file_is_dir = 0;
+        if (!S_ISREG(in_stat.st_mode)) {
+            printf("\tcheck_parameters: in_file (%s) is not directory nor normal file. Ending\n"
+                , in_file);
+            return(3);
+        }
+    }
+
+    if (excl_dir == NULL) { /* use default */
+        excl_dir_cnt = 5; /* just in case it was changed by parameter */
+        excl_dir = calloc(3, sizeof(char *));
+        excl_dir[0] = strdup("posix");
+        excl_dir[1] = strdup("right");
+    }
+
+    /* find last "/zoneinfo" from the infile (directory) name. 
+     * Normally there is only one. 
+     * It needs to be at the end of the string or be followed by '/' */
+    tz_len = strlen(tz);
+    s_tz = in_file;
+    for (s_tz = strstr(s_tz, tz); s_tz != NULL; s_tz = strstr(s_tz, tz)) {
+        if (s_tz[tz_len] == '\0' || s_tz[tz_len] == '/')
+            last_tz = s_tz;
+        *s_tz++;
+    }
+    if (last_tz == NULL) {
+        printf("check_parameters: in_file name (%s) does not contain (%s). Ending\n"
+                , in_file, tz);
+        return(4);
+    }
+
+    in_file_base_offset = last_tz - in_file + 1; /* skip '/' */
+
+    if (!in_file_is_dir) {
+        in_timezone_name = strdup(&in_file[in_file_base_offset + strlen(tz2)]);
+        if (timezone_name == NULL)
+            timezone_name = strdup(in_timezone_name);
+    }
+
+    if (debug > 1) {
+        printf("\n***** Parameters *****\n");
+        printf("\tversion: %s\n", version);
+        printf("\tdebug level: %d\n", debug);
+        printf("\tyear limit: %d\n", ignore_older);
+        printf("\tinfile: (%s) %s\n", in_file
+                , in_file_is_dir ? "directory" : "normal file");
+        printf("\tinfile timezone: (%s)\n", in_timezone_name);
+        printf("\toutfile: (%s)\n", out_file);
+        printf("\toutfile timezone: (%s)\n", timezone_name);
+        printf("\trecursive directories: %s\n"
+                , only_one_level ? "FALSE" : "TRUE");
+        printf("\tmaximum exclude directory count: (%d)\n", excl_dir_cnt);
+        for (i = 0; (i <= excl_dir_cnt) && excl_dir[i];i++)
+            printf("\t\texclude directory %d: (%s)\n"
+                    , i, excl_dir[i]);
+        printf("***** Parameters *****\n\n");
+    }
+    if (debug > 1)
+        printf("check_parameters: end\n");
+    return(0); /* continue */
+}
+
+main(int argc, const char **argv)
+{
+    if (debug > 1)
+        printf("main: start\n");
+    /* if (exit_code = process_parameters(argc, argv)) */
+    if (get_parameters_popt(argc, argv))
+        exit(EXIT_FAILURE); /* help, version or error => end processing */
+    if (check_parameters())
+        exit(EXIT_FAILURE);
+
+    /* nftw goes through the whole file structure and calls "file_call"
+     * with each file. It returns 0 when everything has been done and -1
+     * if it run into an error. */
+    if (nftw(in_file, file_call, 10, FTW_PHYS | FTW_ACTIONRETVAL) == -1) {
+        perror("nftw error in file handling");
+        exit(EXIT_FAILURE);
+    }
+
+    printf("Processed %d files\n", file_cnt);
+
+    free(in_file);
+    if (debug > 1)
+        printf("main: end\n");
+    exit(EXIT_SUCCESS);
+}




More information about the Xfce4-commits mailing list