aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/calcurse.h2
-rw-r--r--src/ical.c118
-rw-r--r--src/utils.c23
-rw-r--r--test/data/ical-007.ical5
-rw-r--r--test/data/ical-008.ical4
-rwxr-xr-xtest/ical-007.sh8
6 files changed, 112 insertions, 48 deletions
diff --git a/src/calcurse.h b/src/calcurse.h
index 25c63a0..1f28c37 100644
--- a/src/calcurse.h
+++ b/src/calcurse.h
@@ -1219,7 +1219,7 @@ int get_item_min(time_t);
struct tm date2tm(struct date, unsigned, unsigned);
time_t date2sec(struct date, unsigned, unsigned);
struct date sec2date(time_t);
-time_t utcdate2sec(struct date, unsigned, unsigned);
+time_t tzdate2sec(struct date, unsigned, unsigned, char *);
int date_cmp(struct date *, struct date *);
int date_cmp_day(time_t, time_t);
char *date_sec2date_str(time_t, const char *);
diff --git a/src/ical.c b/src/ical.c
index 5ab6846..d372396 100644
--- a/src/ical.c
+++ b/src/ical.c
@@ -579,6 +579,32 @@ ical_chk_header(FILE * fd, char *buf, char *lstore, unsigned *lineno,
}
/*
+ * Return the TZID property parameter value from a DTSTART/DTEND/EXDATE property
+ * in an allocated string. The value may be any text string not containing the
+ * characters '"', ';', ':' and ',' (RFC 5545, sections 3.2.19 and 3.1).
+ */
+static char *ical_get_tzid(char *p)
+{
+ const char param[] = ";TZID=";
+ char *q;
+ int s;
+
+ if (!(p = strstr(p, param)))
+ return NULL;
+ p += sizeof(param) - 1;
+ if (*p == '"')
+ return NULL;
+
+ q = strpbrk(p, ":;");
+ s = q - p + 1;
+ q = mem_malloc(s);
+ strncpy(q, p, s);
+ q[s - 1] = '\0';
+
+ return q;
+}
+
+/*
* Return event type from a DTSTART/DTEND/EXDATE property.
*/
static ical_vevent_e ical_get_type(char *c_line)
@@ -601,23 +627,23 @@ static ical_vevent_e ical_get_type(char *c_line)
* where DATE is 'YYYYMMDD' and TIME is 'HHMMSS'.
* The time and 'T' separator are optional (in the case of an day-long event).
*
- * The type argument is either APPOINTMENT or EVENT and the time format must
- * agree.
- *
- * The timezone is not yet handled by calcurse.
+ * The type argument is either APPOINTMENT or EVENT, and the time format must
+ * match (either DATE-TIME or DATE). The time zone identifier is ignored in an
+ * EVENT or in an APPOINTMENT with UTC time.
*/
-static time_t ical_datetime2time_t(char *datestr, ical_vevent_e type)
+static time_t ical_datetime2time_t(char *datestr, char *tzid, ical_vevent_e type)
{
const int INVALID = 0, DATE = 3, DATETIME = 6, DATETIMEZ = 7;
struct date date;
unsigned hour, min, sec;
- char c;
+ char c, UTC[] = "";
int format;
EXIT_IF(type == UNDEFINED, "event type not set");
format = sscanf(datestr, "%04u%02u%02uT%02u%02u%02u%c",
&date.yyyy, &date.mm, &date.dd, &hour, &min, &sec, &c);
+
if (format == DATE && strlen(datestr) > 8)
format = INVALID;
if (format == DATETIMEZ && c != 'Z')
@@ -626,9 +652,9 @@ static time_t ical_datetime2time_t(char *datestr, ical_vevent_e type)
if (format == DATE && type == EVENT)
return date2sec(date, 0, 0);
else if (format == DATETIME && type == APPOINTMENT)
- return date2sec(date, hour, min);
+ return tzdate2sec(date, hour, min, tzid);
else if (format == DATETIMEZ && type == APPOINTMENT)
- return utcdate2sec(date, hour, min);
+ return tzdate2sec(date, hour, min, UTC);
return 0;
}
@@ -869,7 +895,7 @@ static ical_rpt_t *ical_read_rrule(FILE *log, char *rrulestr,
* specified, counts as the first occurrence.
*/
if ((p = strstr(rrulestr, "UNTIL")) != NULL) {
- rpt->until = ical_datetime2time_t(strchr(p, '=') + 1, type);
+ rpt->until = ical_datetime2time_t(strchr(p, '=') + 1, NULL, type);
if (!(rpt->until)) {
ical_log(log, ICAL_VEVENT, itemline,
_("invalid until format."));
@@ -915,47 +941,47 @@ static int
ical_read_exdate(llist_t * exc, FILE * log, char *exstr, unsigned *noskipped,
const int itemline, ical_vevent_e type)
{
- char *p, *q;
+ char *p, *q, *tzid = NULL;
time_t t;
int n;
- /* See DTSTART. */
if (type == UNDEFINED) {
ical_log(log, ICAL_VEVENT, itemline,
_("need DTSTART to determine event type."));
- (*noskipped)++;
- return 0;
+ goto cleanup;
}
if (type != ical_get_type(exstr)) {
ical_log(log, ICAL_VEVENT, itemline,
_("invalid exception date value type."));
- (*noskipped)++;
- return 0;
+ goto cleanup;
}
p = ical_get_value(exstr);
if (!p) {
ical_log(log, ICAL_VEVENT, itemline,
_("malformed exceptions line."));
- (*noskipped)++;
- return 0;
+ goto cleanup;
}
-
+ tzid = ical_get_tzid(exstr);
/* Count the exceptions and replace commas by zeroes */
for (q = p, n = 1; (q = strchr(q, ',')); *q = '\0', q++, n++)
;
while (n) {
- if (!(t = ical_datetime2time_t(p, type))) {
+ if (!(t = ical_datetime2time_t(p, tzid, type))) {
ical_log(log, ICAL_VEVENT, itemline,
_("invalid exception."));
- (*noskipped)++;
- return 0;
+ goto cleanup;
}
ical_add_exc(exc, t);
p = strchr(p, '\0') + 1;
n--;
}
-
return 1;
+
+cleanup:
+ (*noskipped)++;
+ if (tzid)
+ mem_free(tzid);
+ return 0;
}
/*
@@ -1050,13 +1076,13 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents,
const int ITEMLINE = *lineno - !feof(fdi);
ical_vevent_e vevent_type;
ical_property_e property;
- char *p, *note = NULL, *comment;
+ char *p, *note = NULL, *tmp, *tzid;
const char *SEPARATOR = "-- \n";
struct string s;
struct {
llist_t exc;
ical_rpt_t *rpt;
- char *mesg, *desc, *loc, *comm, *stat, *note;
+ char *mesg, *desc, *loc, *comm, *stat, *imp, *note;
long start, end, dur;
int has_alarm;
} vevent;
@@ -1129,6 +1155,11 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents,
vevent.stat);
mem_free(vevent.stat);
}
+ if (vevent.imp) {
+ string_catf(&s, ("Import: %s\n"),
+ vevent.imp);
+ mem_free(vevent.imp);
+ }
vevent.note = generate_note(string_buf(&s));
mem_free(s.buf);
}
@@ -1167,6 +1198,18 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents,
* event.
*/
vevent_type = ical_get_type(buf);
+ if ((tzid = ical_get_tzid(buf)) &&
+ vevent_type == APPOINTMENT) {
+ /* Add note on TZID. */
+ if (vevent.imp) {
+ asprintf(&tmp, "%s, TZID=%s",
+ vevent.imp, tzid);
+ mem_free(vevent.imp);
+ vevent.imp = tmp;
+ } else
+ asprintf(&vevent.imp, "TZID=%s", tzid);
+ has_note = separator = 1;
+ }
p = ical_get_value(buf);
if (!p) {
ical_log(log, ICAL_VEVENT, ITEMLINE,
@@ -1174,7 +1217,9 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents,
goto skip;
}
- vevent.start = ical_datetime2time_t(p, vevent_type);
+ vevent.start = ical_datetime2time_t(p, tzid, vevent_type);
+ if (tzid)
+ mem_free(tzid);
if (!vevent.start) {
ical_log(log, ICAL_VEVENT, ITEMLINE,
_("invalid or malformed event "
@@ -1194,6 +1239,7 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents,
_("invalid end time value type."));
goto skip;
}
+ tzid = ical_get_tzid(buf);
p = ical_get_value(buf);
if (!p) {
ical_log(log, ICAL_VEVENT, ITEMLINE,
@@ -1201,7 +1247,9 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents,
goto skip;
}
- vevent.end = ical_datetime2time_t(p, vevent_type);
+ vevent.end = ical_datetime2time_t(p, tzid, vevent_type);
+ if (tzid)
+ mem_free(tzid);
if (!vevent.end) {
ical_log(log, ICAL_VEVENT, ITEMLINE,
_("malformed event end time."));
@@ -1257,7 +1305,8 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents,
ICAL_VEVENT, ITEMLINE, log);
if (!note)
goto cleanup;
- separator = (property != DESCRIPTION);
+ if (!separator)
+ separator = (property != DESCRIPTION);
has_note = 1;
}
switch (property) {
@@ -1280,10 +1329,10 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents,
case COMMENT:
/* There may be more than one. */
if (vevent.comm) {
- asprintf(&comment, "%sComment: %s",
+ asprintf(&tmp, "%sComment: %s",
vevent.comm, note);
mem_free(vevent.comm);
- vevent.comm = comment;
+ vevent.comm = tmp;
} else
vevent.comm = note;
break;
@@ -1322,6 +1371,8 @@ cleanup:
mem_free(vevent.comm);
if (vevent.stat)
mem_free(vevent.stat);
+ if (vevent.imp)
+ mem_free(vevent.imp);
if (vevent.mesg)
mem_free(vevent.mesg);
if (vevent.rpt)
@@ -1335,7 +1386,7 @@ ical_read_todo(FILE * fdi, FILE * log, unsigned *notodos, unsigned *noskipped,
{
const int ITEMLINE = *lineno - !feof(fdi);
ical_property_e property;
- char *note = NULL, *comment;
+ char *note = NULL, *tmp;
const char *SEPARATOR = "-- \n";
struct string s;
struct {
@@ -1430,7 +1481,8 @@ ical_read_todo(FILE * fdi, FILE * log, unsigned *notodos, unsigned *noskipped,
ICAL_VTODO, ITEMLINE, log);
if (!note)
goto cleanup;
- separator = (property != DESCRIPTION);
+ if (!separator)
+ separator = (property != DESCRIPTION);
has_note = 1;
}
switch (property) {
@@ -1453,10 +1505,10 @@ ical_read_todo(FILE * fdi, FILE * log, unsigned *notodos, unsigned *noskipped,
case COMMENT:
/* There may be more than one. */
if (vtodo.comm) {
- asprintf(&comment, "%sComment: %s",
+ asprintf(&tmp, "%sComment: %s",
vtodo.comm, note);
mem_free(vtodo.comm);
- vtodo.comm = comment;
+ vtodo.comm = tmp;
} else
vtodo.comm = note;
break;
diff --git a/src/utils.c b/src/utils.c
index 6f849ea..23b9d89 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -423,22 +423,25 @@ struct date sec2date(time_t t)
return d;
}
-time_t utcdate2sec(struct date day, unsigned hour, unsigned min)
+time_t tzdate2sec(struct date day, unsigned hour, unsigned min, char *tznew)
{
- char *tz;
+ char *tzold;
time_t t;
- tz = getenv("TZ");
- if (tz)
- tz = mem_strdup(tz);
- setenv("TZ", "", 1);
- tzset();
+ if (!tznew)
+ return date2sec(day, hour, min);
+
+ tzold = getenv("TZ");
+ if (tzold)
+ tzold = mem_strdup(tzold);
+ setenv("TZ", tznew, 1);
+ tzset();
t = date2sec(day, hour, min);
- if (tz) {
- setenv("TZ", tz, 1);
- mem_free(tz);
+ if (tzold) {
+ setenv("TZ", tzold, 1);
+ mem_free(tzold);
} else {
unsetenv("TZ");
}
diff --git a/test/data/ical-007.ical b/test/data/ical-007.ical
index e46c3fb..a7dfdd5 100644
--- a/test/data/ical-007.ical
+++ b/test/data/ical-007.ical
@@ -10,4 +10,9 @@ SUMMARY:UTC
DTSTART:20150223T110000Z
DURATION:PT1H
END:VEVENT
+BEGIN:VEVENT
+SUMMARY:CET
+DTSTART;TZID=CET:20150223T110000
+DURATION:PT1H
+END:VEVENT
END:VCALENDAR
diff --git a/test/data/ical-008.ical b/test/data/ical-008.ical
index 51625d5..7789734 100644
--- a/test/data/ical-008.ical
+++ b/test/data/ical-008.ical
@@ -1,8 +1,8 @@
BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
-DTSTART;TZID="(UTC+01:00) Amsterdam, Berlin, Bern, Rom, Stockholm, Wien":19800101T000100
-DURATION:P1DT9H17M0S
+DTSTART:19800101T000100
+DURATION;TESTPARAM="Quoted string with colon(:), semicolon(;) and comma(,)":P1DT9H17M0S
SUMMARY:Calibrator's
END:VEVENT
BEGIN:VTODO
diff --git a/test/ical-007.sh b/test/ical-007.sh
index 0a5ad2a..da5002c 100755
--- a/test/ical-007.sh
+++ b/test/ical-007.sh
@@ -8,16 +8,20 @@ if [ "$1" = 'actual' ]; then
TZ="America/New_York" "$CALCURSE" -D "$PWD/.calcurse" \
-i "$DATA_DIR/ical-007.ical"
"$CALCURSE" -D "$PWD/.calcurse" -s02/23/2015
+ cat "$PWD/.calcurse/notes/"*
rm -rf .calcurse || exit 1
elif [ "$1" = 'expected' ]; then
cat <<EOD
-Import process report: 0013 lines read
-2 apps / 0 events / 0 todos / 0 skipped
+Import process report: 0018 lines read
+3 apps / 0 events / 0 todos / 0 skipped
02/23/15:
+ - 05:00 -> 06:00
+ CET
- 06:00 -> 07:00
UTC
- 11:00 -> 12:00
Local time
+Import: TZID=CET
EOD
else
./run-test "$0"