ical.c: Reduce nesting depth

Refactor the iCal parser to reduce nesting depth.

Signed-off-by: Lukas Fleischer <calcurse@cryptocrack.de>
This commit is contained in:
Lukas Fleischer 2015-02-24 11:43:45 +01:00
parent 52779d2ec6
commit 329ef1c22a

View File

@ -569,45 +569,39 @@ static long ical_durtime2long(char *timestr)
*/ */
static long ical_dur2long(char *durstr) static long ical_dur2long(char *durstr)
{ {
const int NOTFOUND = -1;
long durlong;
char *p; char *p;
struct { struct {
unsigned week, day; unsigned week, day;
} date; } date;
memset(&date, 0, sizeof date); memset(&date, 0, sizeof date);
if ((p = strchr(durstr, 'P')) == NULL) {
durlong = NOTFOUND;
} else {
p++;
if (*p == '-')
return NOTFOUND;
else if (*p == '+')
p++;
if (*p == 'T') { /* dur-time */ p = strchr(durstr, 'P');
durlong = ical_durtime2long(p); if (!p)
} else if (strchr(p, 'W')) { /* dur-week */ return -1;
if (sscanf(p, "%u", &date.week) == 1) p++;
durlong =
date.week * WEEKINDAYS * DAYINSEC; if (*p == '-')
else return -1;
durlong = NOTFOUND; if (*p == '+')
} else { p++;
if (strchr(p, 'D')) { /* dur-date */
if (sscanf(p, "%uD", &date.day) == 1) { if (*p == 'T') {
durlong = date.day * DAYINSEC; /* dur-time */
durlong += ical_durtime2long(p); return ical_durtime2long(p);
} else { } else if (strchr(p, 'W')) {
durlong = NOTFOUND; /* dur-week */
} if (sscanf(p, "%u", &date.week) == 1)
} else { return date.week * WEEKINDAYS * DAYINSEC;
durlong = NOTFOUND; } else if (strchr(p, 'D')) {
} /* dur-date */
if (sscanf(p, "%uD", &date.day) == 1) {
return date.day * DAYINSEC +
ical_durtime2long(p);
} }
} }
return durlong;
return -1;
} }
/* /*
@ -620,35 +614,20 @@ static long ical_dur2long(char *durstr)
*/ */
static long ical_compute_rpt_until(long start, ical_rpt_t * rpt) static long ical_compute_rpt_until(long start, ical_rpt_t * rpt)
{ {
long until;
switch (rpt->type) { switch (rpt->type) {
case RECUR_DAILY: case RECUR_DAILY:
until = return date_sec_change(start, 0, rpt->freq * (rpt->count - 1));
date_sec_change(start, 0,
rpt->freq * (rpt->count - 1));
break;
case RECUR_WEEKLY: case RECUR_WEEKLY:
until = date_sec_change(start, 0, return date_sec_change(start, 0,
rpt->freq * WEEKINDAYS * rpt->freq * WEEKINDAYS * (rpt->count - 1));
(rpt->count - 1));
break;
case RECUR_MONTHLY: case RECUR_MONTHLY:
until = return date_sec_change(start, rpt->freq * (rpt->count - 1), 0);
date_sec_change(start, rpt->freq * (rpt->count - 1),
0);
break;
case RECUR_YEARLY: case RECUR_YEARLY:
until = return date_sec_change(start,
date_sec_change(start,
rpt->freq * 12 * (rpt->count - 1), 0); rpt->freq * 12 * (rpt->count - 1), 0);
break;
default: default:
until = 0; return 0;
break;
/* NOTREACHED */
} }
return until;
} }
/* /*
@ -693,99 +672,89 @@ static ical_rpt_t *ical_read_rrule(FILE * log, char *rrulestr,
{ {
const char count[] = "COUNT="; const char count[] = "COUNT=";
const char interv[] = "INTERVAL="; const char interv[] = "INTERVAL=";
char freqstr[BUFSIZ];
unsigned interval; unsigned interval;
ical_rpt_t *rpt; ical_rpt_t *rpt;
char *p; char *p;
rpt = NULL; p = strchr(rrulestr, ':');
if ((p = strchr(rrulestr, ':')) != NULL) { if (!p) {
char freqstr[BUFSIZ];
p++;
rpt = mem_malloc(sizeof(ical_rpt_t));
memset(rpt, 0, sizeof(ical_rpt_t));
if (sscanf(p, "FREQ=%s", freqstr) != 1) {
ical_log(log, ICAL_VEVENT, itemline,
_("recurrence frequence not found."));
(*noskipped)++;
mem_free(rpt);
return NULL;
} else {
if (starts_with(freqstr, "DAILY")) {
rpt->type = RECUR_DAILY;
} else if (starts_with(freqstr, "WEEKLY")) {
rpt->type = RECUR_WEEKLY;
} else if (starts_with(freqstr, "MONTHLY")) {
rpt->type = RECUR_MONTHLY;
} else if (starts_with(freqstr, "YEARLY")) {
rpt->type = RECUR_YEARLY;
} else {
ical_log(log, ICAL_VEVENT, itemline,
_("recurrence frequence not recognized."));
(*noskipped)++;
mem_free(rpt);
return NULL;
}
}
/*
The UNTIL rule part defines a date-time value which bounds the
recurrence rule in an inclusive manner. If not present, and the
COUNT rule part is also not present, the RRULE is considered to
repeat forever.
The COUNT rule part defines the number of occurrences at which to
range-bound the recurrence. The "DTSTART" property value, if
specified, counts as the first occurrence.
*/
if ((p = strstr(rrulestr, "UNTIL")) != NULL) {
char *untilstr;
untilstr = strchr(p, '=');
rpt->until = ical_datetime2long(++untilstr, NULL);
} else {
unsigned cnt;
char *countstr;
if ((countstr = strstr(rrulestr, count)) != NULL) {
countstr += sizeof(count) - 1;
if (sscanf(countstr, "%u", &cnt) != 1) {
rpt->until = 0;
/* endless repetition */
} else {
rpt->count = cnt;
}
} else {
rpt->until = 0;
}
}
if ((p = strstr(rrulestr, interv)) != NULL) {
p += sizeof(interv) - 1;
if (sscanf(p, "%u", &interval) != 1) {
rpt->freq = 1;
/* default frequence if none specified */
} else {
rpt->freq = interval;
}
} else {
rpt->freq = 1;
}
} else {
ical_log(log, ICAL_VEVENT, itemline, ical_log(log, ICAL_VEVENT, itemline,
_("recurrence rule malformed.")); _("recurrence rule malformed."));
(*noskipped)++; (*noskipped)++;
return NULL;
} }
p++;
rpt = mem_malloc(sizeof(ical_rpt_t));
memset(rpt, 0, sizeof(ical_rpt_t));
if (sscanf(p, "FREQ=%s", freqstr) != 1) {
ical_log(log, ICAL_VEVENT, itemline,
_("recurrence frequence not found."));
(*noskipped)++;
mem_free(rpt);
return NULL;
}
if (starts_with(freqstr, "DAILY")) {
rpt->type = RECUR_DAILY;
} else if (starts_with(freqstr, "WEEKLY")) {
rpt->type = RECUR_WEEKLY;
} else if (starts_with(freqstr, "MONTHLY")) {
rpt->type = RECUR_MONTHLY;
} else if (starts_with(freqstr, "YEARLY")) {
rpt->type = RECUR_YEARLY;
} else {
ical_log(log, ICAL_VEVENT, itemline,
_("recurrence frequence not recognized."));
(*noskipped)++;
mem_free(rpt);
return NULL;
}
/*
* The UNTIL rule part defines a date-time value which bounds the
* recurrence rule in an inclusive manner. If not present, and the
* COUNT rule part is also not present, the RRULE is considered to
* repeat forever.
* The COUNT rule part defines the number of occurrences at which to
* range-bound the recurrence. The "DTSTART" property value, if
* specified, counts as the first occurrence.
*/
if ((p = strstr(rrulestr, "UNTIL")) != NULL) {
rpt->until = ical_datetime2long(strchr(p, '=') + 1, NULL);
} else {
unsigned cnt;
char *countstr;
rpt->until = 0;
if ((countstr = strstr(rrulestr, count))) {
countstr += sizeof(count) - 1;
if (sscanf(countstr, "%u", &cnt) == 1)
rpt->count = cnt;
}
}
rpt->freq = 1;
if ((p = strstr(rrulestr, interv))) {
p += sizeof(interv) - 1;
if (sscanf(p, "%u", &interval) == 1)
rpt->freq = interval;
}
return rpt; return rpt;
} }
static void ical_add_exc(llist_t * exc_head, long date) static void ical_add_exc(llist_t * exc_head, long date)
{ {
if (date != 0) { if (date == 0)
struct excp *exc = mem_malloc(sizeof(struct excp)); return;
exc->st = date;
LLIST_ADD(exc_head, exc); struct excp *exc = mem_malloc(sizeof(struct excp));
} exc->st = date;
LLIST_ADD(exc_head, exc);
} }
/* /*
@ -799,25 +768,27 @@ ical_read_exdate(llist_t * exc, FILE * log, char *exstr,
char *p, *q; char *p, *q;
long date; long date;
if ((p = strchr(exstr, ':')) != NULL) { p = strchr(exstr, ':');
p++; if (!p) {
while ((q = strchr(p, ',')) != NULL) {
char buf[BUFSIZ];
const int buflen = q - p;
strncpy(buf, p, buflen);
buf[buflen] = '\0';
date = ical_datetime2long(buf, NULL);
ical_add_exc(exc, date);
p = ++q;
}
date = ical_datetime2long(p, NULL);
ical_add_exc(exc, date);
} else {
ical_log(log, ICAL_VEVENT, itemline, ical_log(log, ICAL_VEVENT, itemline,
_("recurrence exception dates malformed.")); _("recurrence exception dates malformed."));
(*noskipped)++; (*noskipped)++;
return;
} }
p++;
while ((q = strchr(p, ',')) != NULL) {
char buf[BUFSIZ];
const int buflen = q - p;
strncpy(buf, p, buflen);
buf[buflen] = '\0';
date = ical_datetime2long(buf, NULL);
ical_add_exc(exc, date);
p = ++q;
}
date = ical_datetime2long(p, NULL);
ical_add_exc(exc, date);
} }
/* Return an allocated string containing the name of the newly created note. */ /* Return an allocated string containing the name of the newly created note. */
@ -827,28 +798,29 @@ static char *ical_read_note(char *line, unsigned *noskipped,
{ {
char *p, *notestr, *note; char *p, *notestr, *note;
if ((p = strchr(line, ':')) != NULL) { p = strchr(line, ':');
p++; if (!p) {
notestr = ical_unformat_line(p);
if (notestr == NULL) {
ical_log(log, item_type, itemline,
_("could not get entire item description."));
(*noskipped)++;
return NULL;
} else if (strlen(notestr) == 0) {
mem_free(notestr);
return NULL;
} else {
note = generate_note(notestr);
mem_free(notestr);
return note;
}
} else {
ical_log(log, item_type, itemline, ical_log(log, item_type, itemline,
_("description malformed.")); _("description malformed."));
(*noskipped)++; (*noskipped)++;
return NULL; return NULL;
} }
p++;
notestr = ical_unformat_line(p);
if (notestr == NULL) {
ical_log(log, item_type, itemline,
_("could not get entire item description."));
(*noskipped)++;
return NULL;
} else if (strlen(notestr) == 0) {
mem_free(notestr);
return NULL;
} else {
note = generate_note(notestr);
mem_free(notestr);
return note;
}
} }
/* Returns an allocated string containing the ical item summary. */ /* Returns an allocated string containing the ical item summary. */
@ -902,147 +874,119 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents,
skip_alarm = 0; skip_alarm = 0;
continue; continue;
} }
if (starts_with_ci(buf, "END:VEVENT")) {
if (vevent.mesg) {
if (vevent.rpt && vevent.rpt->count)
vevent.rpt->until =
ical_compute_rpt_until(vevent.
start,
vevent.
rpt);
switch (vevent_type) { if (starts_with_ci(buf, "END:VEVENT")) {
case APPOINTMENT: if (!vevent.mesg) {
if (vevent.start == 0) {
ical_log(log, ICAL_VEVENT,
ITEMLINE,
_("appointment has no start time."));
goto cleanup;
}
if (vevent.dur == 0) {
if (vevent.end == 0) {
ical_log(log,
ICAL_VEVENT,
ITEMLINE,
_("could not compute duration "
"(no end time)."));
goto cleanup;
} else if (vevent.start ==
vevent.end) {
vevent_type =
EVENT;
vevent.end = 0L;
ical_store_event
(vevent.mesg,
vevent.note,
vevent.start,
vevent.end,
vevent.rpt,
&vevent.exc);
(*noevents)++;
return;
} else {
vevent.dur =
vevent.end -
vevent.start;
if (vevent.dur < 0) {
ical_log
(log,
ICAL_VEVENT,
ITEMLINE,
_("item has a negative duration."));
goto cleanup;
}
}
}
ical_store_apoint(vevent.mesg,
vevent.note,
vevent.start,
vevent.dur,
vevent.rpt,
&vevent.exc,
vevent.
has_alarm);
(*noapoints)++;
break;
case EVENT:
if (vevent.start == 0) {
ical_log(log, ICAL_VEVENT,
ITEMLINE,
_("event date is not defined."));
goto cleanup;
}
ical_store_event(vevent.mesg,
vevent.note,
vevent.start,
vevent.end,
vevent.rpt,
&vevent.exc);
(*noevents)++;
break;
case UNDEFINED:
ical_log(log, ICAL_VEVENT,
ITEMLINE,
_("item could not be identified."));
goto cleanup;
break;
}
} else {
ical_log(log, ICAL_VEVENT, ITEMLINE, ical_log(log, ICAL_VEVENT, ITEMLINE,
_("could not retrieve item summary.")); _("could not retrieve item summary."));
goto cleanup; goto cleanup;
} }
return; if (vevent.start == 0) {
} else { ical_log(log, ICAL_VEVENT, ITEMLINE,
if (starts_with_ci(buf, "DTSTART")) { _("item start date is not defined."));
if ((p = strchr(buf, ':')) != NULL) goto cleanup;
vevent.start =
ical_datetime2long(++p,
&vevent_type);
if (!vevent.start) {
ical_log(log, ICAL_VEVENT,
ITEMLINE,
_("could not retrieve event start time."));
goto cleanup;
}
} else if (starts_with_ci(buf, "DTEND")) {
if ((p = strchr(buf, ':')) != NULL)
vevent.end =
ical_datetime2long(++p,
&vevent_type);
if (!vevent.end) {
ical_log(log, ICAL_VEVENT,
ITEMLINE,
_("could not retrieve event end time."));
goto cleanup;
}
} else if (starts_with_ci(buf, "DURATION")) {
if ((vevent.dur = ical_dur2long(buf)) <= 0) {
ical_log(log, ICAL_VEVENT,
ITEMLINE,
_("item duration malformed."));
goto cleanup;
}
} else if (starts_with_ci(buf, "RRULE")) {
vevent.rpt =
ical_read_rrule(log, buf, noskipped,
ITEMLINE);
} else if (starts_with_ci(buf, "EXDATE")) {
ical_read_exdate(&vevent.exc, log, buf,
noskipped, ITEMLINE);
} else if (starts_with_ci(buf, "SUMMARY")) {
vevent.mesg = ical_read_summary(buf);
} else if (starts_with_ci(buf, "BEGIN:VALARM")) {
skip_alarm = 1;
vevent.has_alarm = 1;
} else if (starts_with_ci(buf, "DESCRIPTION")) {
vevent.note =
ical_read_note(buf, noskipped,
ICAL_VEVENT, ITEMLINE,
log);
} }
if (vevent_type == APPOINTMENT && vevent.dur == 0) {
if (vevent.end == 0) {
ical_log(log, ICAL_VEVENT, ITEMLINE,
_("could not compute duration "
"(no end time)."));
goto cleanup;
}
vevent.dur = vevent.end - vevent.start;
if (vevent.dur == 0) {
vevent_type = EVENT;
vevent.end = 0L;
} else if (vevent.dur < 0) {
ical_log(log, ICAL_VEVENT, ITEMLINE,
_("item has a negative duration."));
goto cleanup;
}
}
if (vevent.rpt && vevent.rpt->count) {
vevent.rpt->until =
ical_compute_rpt_until(vevent.start,
vevent.rpt);
}
switch (vevent_type) {
case APPOINTMENT:
ical_store_apoint(vevent.mesg, vevent.note,
vevent.start, vevent.dur,
vevent.rpt, &vevent.exc,
vevent.has_alarm);
(*noapoints)++;
break;
case EVENT:
ical_store_event(vevent.mesg, vevent.note,
vevent.start, vevent.end,
vevent.rpt, &vevent.exc);
(*noevents)++;
break;
case UNDEFINED:
ical_log(log, ICAL_VEVENT, ITEMLINE,
_("item could not be identified."));
goto cleanup;
break;
}
return;
}
if (starts_with_ci(buf, "DTSTART")) {
p = strchr(buf, ':');
if (!p) {
ical_log(log, ICAL_VEVENT, ITEMLINE,
_("event start time malformed."));
goto cleanup;
}
vevent.start = ical_datetime2long(++p, &vevent_type);
if (!vevent.start) {
ical_log(log, ICAL_VEVENT, ITEMLINE,
_("could not retrieve event start time."));
goto cleanup;
}
} else if (starts_with_ci(buf, "DTEND")) {
p = strchr(buf, ':');
if (!p) {
ical_log(log, ICAL_VEVENT, ITEMLINE,
_("event end time malformed."));
goto cleanup;
}
vevent.end = ical_datetime2long(++p, &vevent_type);
if (!vevent.end) {
ical_log(log, ICAL_VEVENT, ITEMLINE,
_("could not retrieve event end time."));
goto cleanup;
}
} else if (starts_with_ci(buf, "DURATION")) {
vevent.dur = ical_dur2long(buf);
if (vevent.dur <= 0) {
ical_log(log, ICAL_VEVENT, ITEMLINE,
_("item duration malformed."));
goto cleanup;
}
} else if (starts_with_ci(buf, "RRULE")) {
vevent.rpt = ical_read_rrule(log, buf, noskipped,
ITEMLINE);
} else if (starts_with_ci(buf, "EXDATE")) {
ical_read_exdate(&vevent.exc, log, buf, noskipped,
ITEMLINE);
} else if (starts_with_ci(buf, "SUMMARY")) {
vevent.mesg = ical_read_summary(buf);
} else if (starts_with_ci(buf, "BEGIN:VALARM")) {
skip_alarm = vevent.has_alarm = 1;
} else if (starts_with_ci(buf, "DESCRIPTION")) {
vevent.note = ical_read_note(buf, noskipped,
ICAL_VEVENT, ITEMLINE, log);
} }
} }
ical_log(log, ICAL_VEVENT, ITEMLINE, ical_log(log, ICAL_VEVENT, ITEMLINE,
_("The ical file seems to be malformed. " _("The ical file seems to be malformed. "
"The end of item was not found.")); "The end of item was not found."));
@ -1084,51 +1028,46 @@ ical_read_todo(FILE * fdi, FILE * log, unsigned *notodos,
skip_alarm = 0; skip_alarm = 0;
continue; continue;
} }
if (starts_with_ci(buf, "END:VTODO")) { if (starts_with_ci(buf, "END:VTODO")) {
if (!vtodo.has_priority) if (!vtodo.has_priority)
vtodo.priority = LOWEST; vtodo.priority = LOWEST;
if (vtodo.mesg) { if (!vtodo.mesg) {
ical_store_todo(vtodo.priority, vtodo.mesg,
vtodo.note);
(*notodos)++;
} else {
ical_log(log, ICAL_VTODO, ITEMLINE, ical_log(log, ICAL_VTODO, ITEMLINE,
_("could not retrieve item summary.")); _("could not retrieve item summary."));
goto cleanup; goto cleanup;
} }
return;
} else {
int tmpint;
if (starts_with_ci(buf, "PRIORITY:")) { ical_store_todo(vtodo.priority, vtodo.mesg,
sscanf(buf, "%d", &tmpint); vtodo.note);
if (tmpint <= 9 && tmpint >= 1) { (*notodos)++;
vtodo.priority = tmpint; return;
vtodo.has_priority = 1; }
} else {
ical_log(log, ICAL_VTODO, ITEMLINE, if (starts_with_ci(buf, "PRIORITY:")) {
_("item priority is not acceptable " sscanf(buf, "%d", &vtodo.priority);
"(must be between 1 and 9).")); if (vtodo.priority >= 1 && vtodo.priority <= 9) {
vtodo.priority = LOWEST; vtodo.has_priority = 1;
} } else {
} else if (starts_with_ci(buf, "SUMMARY")) { ical_log(log, ICAL_VTODO, ITEMLINE,
vtodo.mesg = ical_read_summary(buf); _("item priority is not acceptable "
} else if (starts_with_ci(buf, "BEGIN:VALARM")) { "(must be between 1 and 9)."));
skip_alarm = 1;
} else if (starts_with_ci(buf, "DESCRIPTION")) {
vtodo.note =
ical_read_note(buf, noskipped,
ICAL_VTODO, ITEMLINE,
log);
} }
} else if (starts_with_ci(buf, "SUMMARY")) {
vtodo.mesg = ical_read_summary(buf);
} else if (starts_with_ci(buf, "BEGIN:VALARM")) {
skip_alarm = 1;
} else if (starts_with_ci(buf, "DESCRIPTION")) {
vtodo.note = ical_read_note(buf, noskipped, ICAL_VTODO,
ITEMLINE, log);
} }
} }
ical_log(log, ICAL_VTODO, ITEMLINE, ical_log(log, ICAL_VTODO, ITEMLINE,
_("The ical file seems to be malformed. " _("The ical file seems to be malformed. "
"The end of item was not found.")); "The end of item was not found."));
cleanup: cleanup:
if (vtodo.note) if (vtodo.note)
mem_free(vtodo.note); mem_free(vtodo.note);
if (vtodo.mesg) if (vtodo.mesg)