diff options
Diffstat (limited to 'libkcal/libical/vzic-1.3/vzic-parse.c')
-rw-r--r-- | libkcal/libical/vzic-1.3/vzic-parse.c | 901 |
1 files changed, 0 insertions, 901 deletions
diff --git a/libkcal/libical/vzic-1.3/vzic-parse.c b/libkcal/libical/vzic-1.3/vzic-parse.c deleted file mode 100644 index aa50ff265..000000000 --- a/libkcal/libical/vzic-1.3/vzic-parse.c +++ /dev/null @@ -1,901 +0,0 @@ -/* - * Vzic - a program to convert Olson timezone database files into VZTIMEZONE - * files compatible with the iCalendar specification (RFC2445). - * - * Copyright (C) 2000-2001 Ximian, Inc. - * Copyright (C) 2003 Damon Chaplin. - * - * Author: Damon Chaplin <damon@gnome.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 of the License, 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, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include <ctype.h> -#include <limits.h> -#include <stdio.h> -#include <string.h> - -#include "vzic.h" -#include "vzic-parse.h" - -/* This is the maximum line length we allow. */ -#define MAX_LINE_LEN 1024 - -/* The maximum number of fields on a line. */ -#define MAX_FIELDS 12 - - -typedef enum -{ - ZONE_ID = 0, /* The 'Zone' at the start of the line. */ - ZONE_NAME = 1, - ZONE_GMTOFF = 2, - ZONE_RULES_SAVE = 3, - ZONE_FORMAT = 4, - ZONE_UNTIL_YEAR = 5, - ZONE_UNTIL_MONTH = 6, - ZONE_UNTIL_DAY = 7, - ZONE_UNTIL_TIME = 8 -} ZoneFieldNumber; - - -typedef enum -{ - RULE_ID = 0, /* The 'Rule' at the start of the line. */ - RULE_NAME = 1, - RULE_FROM = 2, - RULE_TO = 3, - RULE_TYPE = 4, - RULE_IN = 5, - RULE_ON = 6, - RULE_AT = 7, - RULE_SAVE = 8, - RULE_LETTER_S = 9 -} RuleFieldNumber; - - -typedef enum -{ - LINK_ID = 0, /* The 'Link' at the start of the line. */ - LINK_FROM = 1, - LINK_TO = 2 -} LinkFieldNumber; - - -/* This struct contains information used while parsing the files, and is - passed to most parsing functions. */ -typedef struct _ParsingData ParsingData; -struct _ParsingData -{ - /* This is the line being parsed. buffer is a copy that we break into fields - and sub-fields as it is parsed. */ - char line[MAX_LINE_LEN]; - char buffer[MAX_LINE_LEN]; - - /* These are pointers to the start of each field in buffer. */ - char *fields[MAX_FIELDS]; - int num_fields; - - /* These are just for producing error messages. */ - char *filename; - int line_number; - - - /* This is an array of ZoneData structs, 1 for each timezone read. */ - GArray *zone_data; - - /* This is a hash table of arrays of RuleData structs. As each Rule line is - read in, a new RuleData struct is filled in and appended to the - appropriate GArray in the hash table. */ - GHashTable *rule_data; - - /* A hash containing data on the Link lines. The keys are the timezones - where the link is from (i.e. the timezone we will be outputting anyway) - and the data is a GList of timezones to link to (where we will copy the - timezone data to). */ - GHashTable *link_data; - - int max_until_year; -}; - - -/* - * Parsing functions, used when reading the Olson timezone data file. - */ -static void parse_fields (ParsingData *data); -static gboolean parse_zone_line (ParsingData *data); -static gboolean parse_zone_continuation_line (ParsingData *data); -static gboolean parse_zone_common (ParsingData *data, - int offset); -static void parse_rule_line (ParsingData *data); -static void parse_link_line (ParsingData *data); - -static int parse_year (ParsingData *data, - char *field, - gboolean accept_only, - int only_value); -static int parse_month (ParsingData *data, - char *field); -static DayCode parse_day (ParsingData *data, - char *field, - int *day, - int *weekday); -static int parse_weekday (ParsingData *data, - char *field); -static int parse_time (ParsingData *data, - char *field, - TimeCode *time_code); -static int parse_number (ParsingData *data, - char **num); -static int parse_rules_save (ParsingData *data, - char *field, - char **rules); - -static void parse_coord (char *coord, - int len, - int *result); - -void -parse_olson_file (char *filename, - GArray **zone_data, - GHashTable **rule_data, - GHashTable **link_data, - int *max_until_year) -{ - ParsingData data; - FILE *fp; - int zone_continues = 0; - - *zone_data = g_array_new (FALSE, FALSE, sizeof (ZoneData)); - *rule_data = g_hash_table_new (g_str_hash, g_str_equal); - *link_data = g_hash_table_new (g_str_hash, g_str_equal); - - fp = fopen (filename, "r"); - if (!fp) { - fprintf (stderr, "Couldn't open file: %s\n", filename); - exit (1); - } - - data.filename = filename; - data.zone_data = *zone_data; - data.rule_data = *rule_data; - data.link_data = *link_data; - data.max_until_year = 0; - - for (data.line_number = 0; ; data.line_number++) { - if (fgets (data.line, sizeof (data.line), fp) != data.line) - break; - - strcpy (data.buffer, data.line); - - parse_fields (&data); - if (data.num_fields == 0) - continue; - - if (zone_continues) { - zone_continues = parse_zone_continuation_line (&data); - } else if (!strcmp (data.fields[0], "Zone")) { - zone_continues = parse_zone_line (&data); - } else if (!strcmp (data.fields[0], "Rule")) { - parse_rule_line (&data); - } else if (!strcmp (data.fields[0], "Link")) { - parse_link_line (&data); - } else if (!strcmp (data.fields[0], "Leap")) { - /* We don't care about Leap lines. */ - } else { - fprintf (stderr, "%s:%i: Invalid line.\n%s\n", filename, - data.line_number, data.line); - exit (1); - } - } - - if (ferror (fp)) { - fprintf (stderr, "Error reading file: %s\n", filename); - exit (1); - } - - if (zone_continues) { - fprintf (stderr, "%s:%i: Zone continuation line expected.\n%s\n", - filename, data.line_number, data.line); - exit (1); - } - - fclose (fp); - -#if 0 - printf ("Max UNTIL year: %i\n", data.max_until_year); -#endif - *max_until_year = data.max_until_year; -} - - -/* Converts the line into fields. */ -static void -parse_fields (ParsingData *data) -{ - int i; - char *p, *s, ch; - - /* Reset all fields to NULL. */ - for (i = 0; i < MAX_FIELDS; i++) - data->fields[i] = 0; - - data->num_fields = 0; - p = data->buffer; - - for (;;) { - /* Skip whitespace. */ - while (isspace (*p)) - p++; - - /* See if we have reached the end of the line or a comment. */ - if (*p == '\0' || *p == '#') - break; - - /* We must have another field, so save the start position. */ - data->fields[data->num_fields++] = p; - - /* Now find the end of the field. If the field contains '"' characters - they are removed and we have to move the rest of the chars back. */ - s = p; - for (;;) { - ch = *p; - if (ch == '\0' || ch == '#') { - /* Don't move p on since this is the end of the line. */ - *s = '\0'; - break; - } else if (isspace (ch)) { - *s = '\0'; - p++; - break; - } else if (ch == '"') { - p++; - for (;;) { - ch = *p; - if (ch == '\0') { - fprintf (stderr, - "%s:%i: Closing quote character ('\"') missing.\n%s\n", - data->filename, data->line_number, data->line); - exit (1); - } else if (ch == '"') { - p++; - break; - } else { - *s++ = ch; - } - p++; - } - } else { - *s++ = ch; - } - p++; - } - } - -#if 0 - printf ("%i fields: ", data->num_fields); - for (i = 0; i < data->num_fields; i++) - printf ("'%s' ", data->fields[i]); - printf ("\n"); -#endif -} - - -static gboolean -parse_zone_line (ParsingData *data) -{ - ZoneData zone; - - /* All 5 fields up to FORMAT must be present. */ - if (data->num_fields < 5 || data->num_fields > 9) { - fprintf (stderr, "%s:%i: Invalid Zone line - %i fields.\n%s\n", - data->filename, data->line_number, data->num_fields, - data->line); - exit (1); - } - - zone.zone_name = g_strdup (data->fields[ZONE_NAME]); - zone.zone_line_data = g_array_new (FALSE, FALSE, sizeof (ZoneLineData)); - - g_array_append_val (data->zone_data, zone); - - return parse_zone_common (data, 0); -} - - -static gboolean -parse_zone_continuation_line (ParsingData *data) -{ - /* All 3 fields up to FORMAT must be present. */ - if (data->num_fields < 3 || data->num_fields > 7) { - fprintf (stderr, - "%s:%i: Invalid Zone continuation line - %i fields.\n%s\n", - data->filename, data->line_number, data->num_fields, - data->line); - exit (1); - } - - return parse_zone_common (data, -2); -} - - -static gboolean -parse_zone_common (ParsingData *data, - int offset) -{ - ZoneData *zone; - ZoneLineData zone_line; - TimeCode time_code; - - zone_line.stdoff_seconds = parse_time (data, - data->fields[ZONE_GMTOFF + offset], - &time_code); - zone_line.save_seconds = parse_rules_save (data, - data->fields[ZONE_RULES_SAVE + offset], - &zone_line.rules); - - if (!VzicPureOutput) { - /* We round the UTC offsets to the nearest minute, to be compatible with - Outlook. This also works with -ve numbers, I think. - -56 % 60 = -59. -61 % 60 = -1. */ - if (zone_line.stdoff_seconds >= 0) - zone_line.stdoff_seconds += 30; - else - zone_line.stdoff_seconds -= 29; - zone_line.stdoff_seconds -= zone_line.stdoff_seconds % 60; - - if (zone_line.save_seconds >= 0) - zone_line.save_seconds += 30; - else - zone_line.save_seconds -= 29; - zone_line.save_seconds -= zone_line.save_seconds % 60; - } - - zone_line.format = g_strdup (data->fields[ZONE_FORMAT + offset]); - - if (data->num_fields - offset >= 6) { - zone_line.until_set = TRUE; - zone_line.until_year = parse_year (data, - data->fields[ZONE_UNTIL_YEAR + offset], - FALSE, 0); - zone_line.until_month = parse_month (data, - data->fields[ZONE_UNTIL_MONTH + offset]); - zone_line.until_day_code = parse_day (data, - data->fields[ZONE_UNTIL_DAY + offset], - &zone_line.until_day_number, - &zone_line.until_day_weekday); - zone_line.until_time_seconds = parse_time (data, - data->fields[ZONE_UNTIL_TIME + offset], - &zone_line.until_time_code); - - /* We also want to know the maximum year used in any UNTIL value, so we - know where to expand all the infinite Rule data to. */ - if (zone_line.until_year != YEAR_MAXIMUM - && zone_line.until_year != YEAR_MINIMUM) - data->max_until_year = MAX (data->max_until_year, zone_line.until_year); - - } else { - zone_line.until_set = FALSE; - } - - /* Append it to the last Zone, since that is the one we are currently - reading. */ - zone = &g_array_index (data->zone_data, ZoneData, data->zone_data->len - 1); - g_array_append_val (zone->zone_line_data, zone_line); - - return zone_line.until_set; -} - - -static void -parse_rule_line (ParsingData *data) -{ - GArray *rule_array; - RuleData rule; - char *name; - TimeCode time_code; - - /* All 10 fields must be present. */ - if (data->num_fields != 10) { - fprintf (stderr, "%s:%i: Invalid Rule line - %i fields.\n%s\n", - data->filename, data->line_number, data->num_fields, - data->line); - exit (1); - } - - name = data->fields[RULE_NAME]; - - /* Create the GArray and add it to the hash table if it doesn't already - exist. */ - rule_array = g_hash_table_lookup (data->rule_data, name); - if (!rule_array) { - rule_array = g_array_new (FALSE, FALSE, sizeof (RuleData)); - g_hash_table_insert (data->rule_data, g_strdup (name), rule_array); - } - - rule.from_year = parse_year (data, data->fields[RULE_FROM], FALSE, 0); - if (rule.from_year == YEAR_MAXIMUM) { - fprintf (stderr, "%s:%i: Invalid Rule FROM value: '%s'\n", - data->filename, data->line_number, data->fields[RULE_FROM]); - exit (1); - } - - rule.to_year = parse_year (data, data->fields[RULE_TO], TRUE, - rule.from_year); - if (rule.to_year == YEAR_MINIMUM) { - fprintf (stderr, "%s:%i: Invalid Rule TO value: %s\n", - data->filename, data->line_number, data->fields[RULE_TO]); - exit (1); - } - - if (!strcmp (data->fields[RULE_TYPE], "-")) - rule.type = NULL; - else { - printf ("Type: %s\n", data->fields[RULE_TYPE]); - rule.type = g_strdup (data->fields[RULE_TYPE]); - } - - rule.in_month = parse_month (data, data->fields[RULE_IN]); - rule.on_day_code = parse_day (data, data->fields[RULE_ON], - &rule.on_day_number, &rule.on_day_weekday); - rule.at_time_seconds = parse_time (data, data->fields[RULE_AT], - &rule.at_time_code); - rule.save_seconds = parse_time (data, data->fields[RULE_SAVE], &time_code); - - if (!strcmp (data->fields[RULE_LETTER_S], "-")) { - rule.letter_s = NULL; - } else { - rule.letter_s = g_strdup (data->fields[RULE_LETTER_S]); - } - - rule.is_shallow_copy = FALSE; - - g_array_append_val (rule_array, rule); -} - - -static void -parse_link_line (ParsingData *data) -{ - char *from, *to, *old_from; - GList *zone_list; - - /* We must have 3 fields for a Link. */ - if (data->num_fields != 3) { - fprintf (stderr, "%s:%i: Invalid Rule line - %i fields.\n%s\n", - data->filename, data->line_number, data->num_fields, - data->line); - exit (1); - } - - from = data->fields[LINK_FROM]; - to = data->fields[LINK_TO]; - -#if 0 - printf ("LINK FROM: %s\tTO: %s\n", from, to); -#endif - - if (g_hash_table_lookup_extended (data->link_data, from, - (gpointer) &old_from, - (gpointer) &zone_list)) { - from = old_from; - } else { - from = g_strdup (from); - zone_list = NULL; - } - - zone_list = g_list_prepend (zone_list, g_strdup (to)); - - g_hash_table_insert (data->link_data, from, zone_list); -} - - -static int -parse_year (ParsingData *data, - char *field, - gboolean accept_only, - int only_value) -{ - int len, year = 0; - char *p; - - if (!field) { - fprintf (stderr, "%s:%i: Missing year.\n%s\n", data->filename, - data->line_number, data->line); - exit (1); - } - - len = strlen (field); - if (accept_only && !strncmp (field, "only", len)) - return only_value; - if (len >= 2) { - if (!strncmp (field, "maximum", len)) - return YEAR_MAXIMUM; - else if (!strncmp (field, "minimum", len)) - return YEAR_MINIMUM; - } - - for (p = field; *p; p++) { - if (*p < '0' || *p > '9') { - fprintf (stderr, "%s:%i: Invalid year: %s\n%s\n", data->filename, - data->line_number, field, data->line); - exit (1); - } - - year = year * 10 + *p - '0'; - } - - if (year < 1000 || year > 2037) { - fprintf (stderr, "%s:%i: Strange year: %s\n%s\n", data->filename, - data->line_number, field, data->line); - exit (1); - } - - return year; -} - - -/* Parses a month name, returning 0 (Jan) to 11 (Dec). */ -static int -parse_month (ParsingData *data, - char *field) -{ - static char* months[] = { "january", "february", "march", "april", "may", - "june", "july", "august", "september", "october", - "november", "december" }; - char *p; - int len, i; - - /* If the field is missing, it must be the optional UNTIL month, so we return - 0 for January. */ - if (!field) - return 0; - - for (p = field, len = 0; *p; p++, len++) { - *p = tolower (*p); - } - - for (i = 0; i < 12; i++) { - if (!strncmp (field, months[i], len)) - return i; - } - - fprintf (stderr, "%s:%i: Invalid month: %s\n%s\n", data->filename, - data->line_number, field, data->line); - exit (1); -} - - -/* Parses a day specifier, returning a code representing the type of match - together with a day of the month and a weekday number (0=Sun). */ -static DayCode -parse_day (ParsingData *data, - char *field, - int *day, - int *weekday) -{ - char *day_part, *p; - DayCode day_code; - - if (!field) { - *day = 1; - return DAY_SIMPLE; - } - - *day = *weekday = 0; - - if (!strncmp (field, "last", 4)) { - *weekday = parse_weekday (data, field + 4); - /* We set the day to the end of the month to make sorting Rules easy. */ - *day = 31; - return DAY_LAST_WEEKDAY; - } - - day_part = field; - day_code = DAY_SIMPLE; - - for (p = field; *p; p++) { - if (*p == '<' || *p == '>') { - if (*(p + 1) == '=') { - day_code = (*p == '<') ? DAY_WEEKDAY_ON_OR_BEFORE - : DAY_WEEKDAY_ON_OR_AFTER; - *p = '\0'; - *weekday = parse_weekday (data, field); - day_part = p + 2; - break; - } - - fprintf (stderr, "%s:%i: Invalid day: %s\n%s\n", data->filename, - data->line_number, field, data->line); - exit (1); - } - } - - for (p = day_part; *p; p++) { - if (*p < '0' || *p > '9') { - fprintf (stderr, "%s:%i: Invalid day: %s\n%s\n", data->filename, - data->line_number, field, data->line); - exit (1); - } - - *day = *day * 10 + *p - '0'; - } - - if (*day < 1 || *day > 31) { - fprintf (stderr, "%s:%i: Invalid day: %s\n%s\n", data->filename, - data->line_number, field, data->line); - exit (1); - } - - return day_code; -} - - -/* Parses a weekday name, returning 0 (Sun) to 6 (Sat). */ -static int -parse_weekday (ParsingData *data, - char *field) -{ - static char* weekdays[] = { "sunday", "monday", "tuesday", "wednesday", - "thursday", "friday", "saturday" }; - char *p; - int len, i; - - for (p = field, len = 0; *p; p++, len++) { - *p = tolower (*p); - } - - for (i = 0; i < 7; i++) { - if (!strncmp (field, weekdays[i], len)) - return i; - } - - fprintf (stderr, "%s:%i: Invalid weekday: %s\n%s\n", data->filename, - data->line_number, field, data->line); - exit (1); -} - - -/* Parses a time (hour + minute + second) and returns the result in seconds, - together with a time code specifying whether it is Wall clock time, - local standard time, or universal time. - The time can start with a '-' in which case it will be negative. */ -static int -parse_time (ParsingData *data, - char *field, - TimeCode *time_code) -{ - char *p; - int hours = 0, minutes = 0, seconds = 0, result, negative = 0; - - if (!field) { - *time_code = TIME_WALL; - return 0; - } - - p = field; - if (*p == '-') { - p++; - negative = 1; - } - - hours = parse_number (data, &p); - - if (*p == ':') { - p++; - minutes = parse_number (data, &p); - - if (*p == ':') { - p++; - seconds = parse_number (data, &p); - } - } - - if (hours < 0 || hours > 24 - || minutes < 0 || minutes > 59 - || seconds < 0 || seconds > 59 - || (hours == 24 && (minutes != 0 || seconds != 0))) { - fprintf (stderr, "%s:%i: Invalid time: %s\n%s\n", data->filename, - data->line_number, field, data->line); - exit (1); - } - - if (hours == 24) { - hours = 23; - minutes = 59; - seconds = 59; - } - -#if 0 - printf ("Time: %s -> %i:%02i:%02i\n", field, hours, minutes, seconds); -#endif - - result = hours * 3600 + minutes * 60 + seconds; - if (negative) - result = -result; - - if (*p == '\0') { - *time_code = TIME_WALL; - return result; - } - - if (*(p + 1) == '\0') { - if (*p == 'w') { - *time_code = TIME_WALL; - return result; - } else if (*p == 's') { - *time_code = TIME_STANDARD; - return result; - } else if (*p == 'u' || *p == 'g' || *p == 'z') { - *time_code = TIME_UNIVERSAL; - return result; - } - } - - fprintf (stderr, "%s:%i: Invalid time: %s\n%s\n", data->filename, - data->line_number, field, data->line); - exit (1); -} - - -/* Parses a simple number and returns the result. The pointer argument - is moved to the first character after the number. */ -static int -parse_number (ParsingData *data, - char **num) -{ - char *p; - int result; - - p = *num; - -#if 0 - printf ("In parse_number p:%s\n", p); -#endif - - if (*p < '0' || *p > '9') { - fprintf (stderr, "%s:%i: Invalid number: %s\n%s\n", data->filename, - data->line_number, *num, data->line); - exit (1); - } - - result = *p++ - '0'; - - while (*p >= '0' && *p <= '9') - result = result * 10 + *p++ - '0'; - - *num = p; - return result; -} - - -static int -parse_rules_save (ParsingData *data, - char *field, - char **rules) -{ - TimeCode time_code; - - *rules = NULL; - - /* Check for just "-". */ - if (field[0] == '-' && field[1] == '\0') - return 0; - - /* Check for a time to add to local standard time. We don't care about a - time code here, since it is just an offset. */ - if (*field == '-' || (*field >= '0' && *field <= '9')) - return parse_time (data, field, &time_code); - - /* It must be a rules name. */ - *rules = g_strdup (field); - return 0; -} - - - - - -GHashTable* -parse_zone_tab (char *filename) -{ - GHashTable *zones_hash; - ZoneDescription *zone_desc; - FILE *fp; - char buf[4096]; - gchar **fields, *zone_name, *latitude, *longitude, *p; - - - fp = fopen (filename, "r"); - if (!fp) { - fprintf (stderr, "Couldn't open file: %s\n", filename); - exit (1); - } - - zones_hash = g_hash_table_new (g_str_hash, g_str_equal); - - while (fgets (buf, sizeof(buf), fp)) { - if (*buf == '#') continue; - - g_strchomp (buf); - fields = g_strsplit (buf,"\t", 4); - - if (strlen (fields[0]) != 2) { - fprintf (stderr, "Invalid zone description line: %s\n", buf); - exit (1); - } - - zone_name = g_strdup (fields[2]); - - zone_desc = g_new (ZoneDescription, 1); - zone_desc->country_code[0] = fields[0][0]; - zone_desc->country_code[1] = fields[0][1]; - zone_desc->comment = (fields[3] && fields[3][0]) ? g_strdup (fields[3]) - : NULL; - - /* Now parse the latitude and longitude. */ - latitude = fields[1]; - longitude = latitude + 1; - while (*longitude != '+' && *longitude != '-') - longitude++; - - parse_coord (latitude, longitude - latitude, zone_desc->latitude); - parse_coord (longitude, strlen (longitude), zone_desc->longitude); - - g_hash_table_insert (zones_hash, zone_name, zone_desc); - -#if 0 - g_print ("Found zone: %s %i %02i %02i,%i %02i %02i\n", zone_name, - zone_desc->latitude[0], zone_desc->latitude[1], - zone_desc->latitude[2], - zone_desc->longitude[0], zone_desc->longitude[1], - zone_desc->longitude[2]); -#endif - } - - fclose (fp); - - return zones_hash; -} - - -static void -parse_coord (char *coord, - int len, - int *result) -{ - int degrees = 0, minutes = 0, seconds = 0; - - if (len == 5) - sscanf (coord + 1, "%2d%2d", °rees, &minutes); - else if (len == 6) - sscanf (coord + 1, "%3d%2d", °rees, &minutes); - else if (len == 7) - sscanf (coord + 1, "%2d%2d%2d", °rees, &minutes, &seconds); - else if (len == 8) - sscanf (coord + 1, "%3d%2d%2d", °rees, &minutes, &seconds); - else { - fprintf (stderr, "Invalid coordinate: %s\n", coord); - exit (1); - } - - if (coord[0] == '-') - degrees = -degrees; - - result[0] = degrees; - result[1] = minutes; - result[2] = seconds; -} - |