]> Pileus Git - lackey/blob - cal/ical.c
Working iCalendar parsing
[lackey] / cal / ical.c
1 /*
2  * Copyright (C) 2012 Andy Spencer <andy753421@gmail.com>
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <libical/ical.h>
21
22 #include "util.h"
23 #include "date.h"
24 #include "event.h"
25
26 /* Local types */
27 typedef struct {
28         icalcomponent *comp;
29         struct icaltimetype start;
30         struct icaltimetype end;
31 } ical_inst;
32
33 /* Helper functions */
34 static int ical_compare(const void *_a, const void *_b)
35 {
36         const ical_inst *a = _a;
37         const ical_inst *b = _b;
38         int scomp = icaltime_compare(a->start, b->start);
39         int ecomp = icaltime_compare(a->end,   b->end);
40         return scomp != 0 ? scomp :
41                ecomp != 0 ? ecomp : 0 ;
42 }
43
44 static date_t to_date(struct icaltimetype time)
45 {
46         return (date_t){
47                 .year  = time.year,
48                 .month = time.month-1,
49                 .day   = time.day-1,
50                 .hour  = time.hour,
51                 .min   = time.minute,
52         };
53 }
54
55 static event_t *to_event(ical_inst *inst)
56 {
57         event_t *event = calloc(1, sizeof(event_t));
58         event->name  = icalcomponent_get_summary(inst->comp);
59         event->desc  = icalcomponent_get_description(inst->comp);
60         event->start = to_date(inst->start);
61         event->end   = to_date(inst->end);
62         return event;
63 }
64
65 static event_t *to_list(icalarray *array)
66 {
67         event_t  list = {};
68         event_t *tail = &list;
69         for (int i = 0; i < array->num_elements; i++) {
70                  ical_inst *inst = icalarray_element_at(array, i);
71                  tail->next = to_event(inst);
72                  tail = tail->next;
73         }
74         return list.next;
75 }
76
77 static void print_list(event_t *start)
78 {
79         for (event_t *cur = start; cur; cur = cur->next)
80                 printf("%04d-%02d-%02d %02d:%02d - %s\n",
81                         cur->start.year, cur->start.month, cur->start.day,
82                         cur->start.hour, cur->start.min,
83                         cur->name ?: cur->desc ?: "[no summary]");
84 }
85
86 static void add_events(icalarray *array, icalcomponent *comp,
87                 icaltimetype start, icaltimetype end)
88 {
89         icalcomponent_kind kind = icalcomponent_isa(comp);
90
91         if (kind == ICAL_VEVENT_COMPONENT ||
92             kind == ICAL_VTODO_COMPONENT) {
93                 /* Get recurrence data */
94                 struct icaltimetype cstart, cend; // Component times
95                 struct icaltimetype istart, iend; // Instance times
96                 struct icaldurationtype length;   // Duration
97
98                 icalproperty             *rrule;
99                 struct icalrecurrencetype recur;
100                 icalrecur_iterator       *iter;
101
102                 cstart = icalcomponent_get_dtstart(comp);
103                 cend   = icalcomponent_get_dtend(comp);
104                 length = icaltime_subtract(cend, cstart);
105
106                 rrule  = icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY);
107                 recur  = icalproperty_get_rrule(rrule);
108                 iter   = icalrecur_iterator_new(recur, cstart);
109
110                 /* Add recurrences */
111                 while (1) {
112                         istart = icalrecur_iterator_next(iter);
113                         if (icaltime_is_null_time(istart))
114                                 break;    // no more instances
115                         iend   = icaltime_add(istart, length);
116                         if (icaltime_compare(iend, start) <= 0)
117                                 continue; // instance ends before start time
118                         if (icaltime_compare(istart, end) >= 0)
119                                 break;    // instance begins after stop time
120                         icalarray_append(array, &(ical_inst){
121                                 .comp  = comp,
122                                 .start = istart,
123                                 .end   = iend,
124                         });
125                 }
126         }
127
128         /* Add children */
129         icalcomponent_kind find = ICAL_ANY_COMPONENT;
130         icalcomponent *child = icalcomponent_get_first_component(comp, find);
131         while (child) {
132                 add_events(array, child, start, end);
133                 child = icalcomponent_get_next_component(comp, find);
134         }
135 }
136
137 /* Event functions */
138 event_t *ical_get(cal_t *cal, year_t year, month_t month, day_t day, int days)
139 {
140         /* Load ical */
141         FILE *file = fopen("data/all.ics", "r");
142         icalparser *parser = icalparser_new();
143         icalparser_set_gen_data(parser, file);
144         icalcomponent *ical = icalparser_parse(parser, (void*)fgets);
145
146         /* Add events */
147         icalarray *array = icalarray_new(sizeof(ical_inst), 1);
148         icaltimetype start = {.year = 2000};
149         icaltimetype end   = {.year = 2020};
150         add_events(array, ical, start, end);
151         icalarray_sort(array, ical_compare);
152         return to_list(array);
153
154         /* Todo, memory management */
155 }
156
157 /* Test functions */
158 void ical_printr(icalcomponent *comp, int depth)
159 {
160         /* Print component */
161         icalcomponent_kind kind = icalcomponent_isa(comp);
162         printf("%*s", depth, "");
163         printf("%s",  icalcomponent_kind_to_string(kind));
164         if (kind == ICAL_VEVENT_COMPONENT ||
165             kind == ICAL_VTODO_COMPONENT)
166                 printf(" - %s", icalcomponent_get_summary(comp) ?: "[no summary]");
167         printf("\n");
168
169         /* Print children */
170         icalcomponent_kind find = ICAL_ANY_COMPONENT;
171         icalcomponent *child = icalcomponent_get_first_component(comp, find);
172         while (child) {
173                 ical_printr(child, depth+2);
174                 child = icalcomponent_get_next_component(comp, find);
175         }
176 }
177
178 void ical_test(void)
179 {
180         /* Load ical */
181         FILE *file = fopen("data/all.ics", "r");
182         icalparser *parser = icalparser_new();
183         icalparser_set_gen_data(parser, file);
184         icalcomponent *ical = icalparser_parse(parser, (void*)fgets);
185
186         /* Test print */
187         ical_printr(ical, 0);
188
189         /* Test Add */
190         icalarray *array = icalarray_new(sizeof(ical_inst), 1);
191         icaltimetype start = {.year = 2000};
192         icaltimetype end   = {.year = 2020};
193         add_events(array, ical, start, end);
194         icalarray_sort(array, ical_compare);
195         event_t *events = to_list(array);
196         print_list(events);
197
198         /* Cleanup */
199         icalparser_free(parser);
200 }