+static icaltimetype to_itime(date_t time)
+{
+ return (struct icaltimetype){
+ .year = time.year,
+ .month = time.month + 1,
+ .day = time.day + 1,
+ .hour = time.hour,
+ .minute = time.min
+ };
+}
+
+static void add_recur(cal_t *cal,
+ icalarray *array, icalcomponent *comp,
+ icaltimetype start, icaltimetype end,
+ icalcomponent_kind which)
+{
+ icalcomponent_kind kind = icalcomponent_isa(comp);
+
+ if (kind == which) {
+ /* Get recurrence data */
+ struct icaltimetype cstart, cend; // Component times
+ struct icaltimetype istart, iend; // Instance times
+ struct icaldurationtype length; // Duration
+
+ icalproperty *rrule;
+ struct icalrecurrencetype recur;
+ icalrecur_iterator *iter;
+
+ cstart = icalcomponent_get_dtstart(comp);
+ cend = icalcomponent_get_dtend(comp);
+ length = icaltime_subtract(cend, cstart);
+
+ /* Full day event */
+ if (icaltime_is_null_time(cstart) ||
+ which == ICAL_VTODO_COMPONENT) {
+ icalarray_append(array, &(ical_inst){
+ .cal = cal,
+ .comp = comp,
+ .start = cstart,
+ .end = cend,
+ });
+ }
+
+ /* Add all recurrences */
+ rrule = icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY);
+ while (rrule) {
+ recur = icalproperty_get_rrule(rrule);
+ iter = icalrecur_iterator_new(recur, cstart);
+
+ /* Add recurrence for this rrule */
+ while (1) {
+ istart = iend = icalrecur_iterator_next(iter);
+ if (icaltime_is_null_time(istart))
+ break; // no more instances
+ if (!icaltime_is_null_time(cend))
+ iend = icaltime_add(iend, length);
+
+ if (icaltime_compare(iend, start) <= 0)
+ continue; // instance ends before start time
+ if (icaltime_compare(istart, end) >= 0)
+ break; // instance begins after stop time
+
+ icalarray_append(array, &(ical_inst){
+ .cal = cal,
+ .comp = comp,
+ .start = istart,
+ .end = iend,
+ });
+ }
+
+ icalrecur_iterator_free(iter);
+ rrule = icalcomponent_get_next_property(comp, ICAL_RRULE_PROPERTY);
+ }
+ }
+
+ /* Add children */
+ icalcomponent_kind find = ICAL_ANY_COMPONENT;
+ icalcomponent *child = icalcomponent_get_first_component(comp, find);
+ while (child) {
+ add_recur(cal, array, child, start, end, which);
+ child = icalcomponent_get_next_component(comp, find);
+ }
+}
+
+static void read_icals(void)
+{
+ for (ical_t *cal = calendars; cal; cal = cal->next) {
+ if (cal->comp == NULL && cal->location) {
+ wordexp_t wexp;
+ wordexp(cal->location, &wexp, WRDE_NOCMD);
+ icalparser *parser = icalparser_new();
+ for (int i = 0; i < wexp.we_wordc; i++) {
+ FILE *file = fopen(wexp.we_wordv[i], "r");
+ if (!file)
+ continue;
+ icalparser_set_gen_data(parser, file);
+ }
+ cal->comp = icalparser_parse(parser, (void*)fgets);
+ icalparser_free(parser);
+ wordfree(&wexp);
+ }
+ }
+}
+
+/* Event functions */