Introduction
Introduction Statistics Contact Development Disclaimer Help
ical.c - ics2txt - convert icalendar .ics file to plain text
git clone git://bitreich.org/ics2txt git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws…
Log
Files
Refs
Tags
README
---
ical.c (7209B)
---
1 #include "ical.h"
2 #include <assert.h>
3 #include <ctype.h>
4 #include <errno.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <strings.h>
9 #include "util.h"
10 #include "base64.h"
11
12 char *ical_block_name[ICAL_BLOCK_OTHER + 1] = {
13 [ICAL_BLOCK_VEVENT] = "VEVENT",
14 [ICAL_BLOCK_VTODO] = "VTODO",
15 [ICAL_BLOCK_VJOURNAL] = "VJOURNAL",
16 [ICAL_BLOCK_VFREEBUSY] = "VFREEBUSY",
17 [ICAL_BLOCK_VALARM] = "VALARM",
18 [ICAL_BLOCK_OTHER] = NULL,
19 };
20
21 /* valuel helpers: common utilities to call within the p->fn()
22 * callbacks as well as in the code below */
23
24 int
25 ical_err(IcalParser *p, char *msg)
26 {
27 p->errmsg = msg;
28 return -1;
29 }
30
31 int
32 ical_get_level(IcalParser *p)
33 {
34 return p->current - p->stack;
35 }
36
37 int
38 ical_get_value(IcalParser *p, char *s, size_t *len)
39 {
40 *len = strlen(s);
41 if (p->base64)
42 if (base64_decode(s, len, s, len) < 0)
43 return ical_err(p, "invalid base64 data");
44 return 0;
45 }
46
47 int
48 ical_get_time(IcalParser *p, char *s, time_t *t)
49 {
50 struct tm tm = {0};
51 char const *tzid;
52
53 tzid = (p->tzid) ? p->tzid :
54 (p->current && p->current->tzid[0] != '\0') ? p->current->tz…
55 "";
56
57 #define N(i, x) ((s[i] - '0') * x)
58
59 /* date */
60 for (int i = 0; i < 8; i++)
61 if (!isdigit(s[i]))
62 return ical_err(p, "invalid date format");
63 tm.tm_year = N(0,1000) + N(1,100) + N(2,10) + N(3,1) - 1900;
64 tm.tm_mon = N(4,10) + N(5,1) - 1;
65 tm.tm_mday = N(6,10) + N(7,1);
66 s += 8;
67
68 if (*s == 'T') {
69 /* time */
70 s++;
71 for (int i = 0; i < 6; i++)
72 if (!isdigit(s[i]))
73 return ical_err(p, "invalid time format"…
74 tm.tm_hour = N(0,10) + N(1,1);
75 tm.tm_min = N(2,10) + N(3,1);
76 tm.tm_sec = N(4,10) + N(5,1);
77 if (s[6] == 'Z')
78 tzid = "UTC";
79 }
80
81 #undef N
82
83 if ((*t = tztime(&tm, tzid)) == (time_t)-1)
84 return ical_err(p, "could not convert time");
85
86 return 0;
87 }
88
89 /* hooks: called just before user functions to do extra work such as
90 * processing time zones definition or prepare base64 decoding, and
91 * permit to only have parsing code left to parsing functions */
92
93 static int
94 hook_field_name(IcalParser *p, char *name)
95 {
96 (void)p; (void)name;
97 return 0;
98 }
99
100 static int
101 hook_param_name(IcalParser *p, char *name)
102 {
103 (void)p; (void)name;
104 return 0;
105 }
106
107 static int
108 hook_param_value(IcalParser *p, char *name, char *value)
109 {
110 if (strcasecmp(name, "ENCODING") == 0)
111 p->base64 = (strcasecmp(value, "BASE64") == 0);
112
113 if (strcasecmp(name, "TZID") == 0)
114 p->tzid = value;
115
116 return 0;
117 }
118
119 static int
120 hook_field_value(IcalParser *p, char *name, char *value)
121 {
122 if (strcasecmp(name, "TZID") == 0)
123 if (strlcpy(p->current->tzid, value, sizeof p->current->…
124 sizeof p->current->tzid)
125 return ical_err(p, "TZID: name too large");
126
127 p->tzid = NULL;
128
129 return 0;
130 }
131
132 static int
133 hook_block_begin(IcalParser *p, char *name)
134 {
135 p->current++;
136 memset(p->current, 0, sizeof(*p->current));
137 if (ical_get_level(p) >= ICAL_STACK_SIZE)
138 return ical_err(p, "max recurion reached");
139 if (strlcpy(p->current->name, name, sizeof p->current->name) >=
140 sizeof p->current->name)
141 return ical_err(p, "value too large");
142
143 for (int i = 0; ical_block_name[i] != NULL; i++) {
144 if (strcasecmp(ical_block_name[i], name) == 0) {
145 if (p->blocktype != ICAL_BLOCK_OTHER)
146 return ical_err(p, "BEGIN:V* in BEGIN:V*…
147 p->blocktype = i;
148 }
149 }
150
151 return 0;
152 }
153
154 static int
155 hook_block_end_before(IcalParser *p, char *name)
156 {
157 if (p->current == p->stack)
158 return ical_err(p, "more END: than BEGIN:");
159 if (strcasecmp(p->current->name, name) != 0)
160 return ical_err(p, "mismatching BEGIN: and END:");
161 if (p->current <= p->stack)
162 return ical_err(p, "more END: than BEGIN:");
163 return 0;
164 }
165
166 static int
167 hook_block_end_after(IcalParser *p, char *name)
168 {
169 p->current--;
170 if (ical_block_name[p->blocktype] != NULL &&
171 strcasecmp(ical_block_name[p->blocktype], name) == 0)
172 p->blocktype = ICAL_BLOCK_OTHER;
173 return 0;
174 }
175
176 /* parsers: in charge of reading from `fp`, splitting text into
177 * fields, and call hooks and user functions. */
178
179 #define CALL(p, fn, ...) ((p)->fn ? (p)->fn((p), __VA_ARGS__) : 0)
180
181 static int
182 ical_parse_value(IcalParser *p, char **sp, char *name)
183 {
184 int err;
185 char *s, c, *val;
186
187 s = *sp;
188 if (*s == '"') {
189 val = ++s;
190 while (!iscntrl(*s) && *s != '"')
191 s++;
192 if (*s != '"')
193 return ical_err(p, "missing '\"'");
194 *s++ = '\0';
195 } else {
196 val = s;
197 while (!iscntrl(*s) && !strchr(",;:'\"", *s))
198 s++;
199 }
200 c = *s, *s = '\0';
201 if ((err = hook_param_value(p, name, val)) != 0 ||
202 (err = CALL(p, fn_param_value, name, val)) != 0)
203 return err;
204 *s = c;
205 *sp = s;
206 return 0;
207 }
208
209 static int
210 ical_parse_param(IcalParser *p, char **sp)
211 {
212 int err;
213 char *s, *name;
214
215 s = *sp;
216 do {
217 for (name = s; isalnum(*s) || *s == '-'; s++);
218 if (s == name || (*s != '='))
219 return ical_err(p, "invalid parameter name");
220 *s++ = '\0';
221 if ((err = hook_param_name(p, name)) != 0 ||
222 (err = CALL(p, fn_param_name, name)) != 0)
223 return err;
224 do {
225 if ((err = ical_parse_value(p, &s, name)) != 0)
226 return err;
227 } while (*s == ',' && s++);
228 } while (*s == ';' && s++);
229 *sp = s;
230 return 0;
231 }
232
233 static int
234 ical_parse_contentline(IcalParser *p, char *s)
235 {
236 int err;
237 char c, *name, *sep;
238
239 if (*s == '\0')
240 return 0;
241
242 for (name = s; isalnum(*s) || *s == '-'; s++);
243 if (s == name || (*s != ';' && *s != ':'))
244 return ical_err(p, "invalid property name");
245 c = *s, *s = '\0';
246 if (strcasecmp(name, "BEGIN") != 0 && strcasecmp(name, "END") !=…
247 if ((err = hook_field_name(p, name)) != 0 ||
248 (err = CALL(p, fn_field_name, name)) != 0)
249 return err;
250 *s = c;
251 sep = s;
252
253 p->base64 = 0;
254 while (*s == ';') {
255 s++;
256 if ((err = ical_parse_param(p, &s)) != 0)
257 return err;
258 }
259
260 if (*s != ':')
261 return ical_err(p, "expected ':' delimiter");
262 s++;
263
264 *sep = '\0';
265 if (strcasecmp(name, "BEGIN") == 0) {
266 if ((err = hook_block_begin(p, s)) != 0 ||
267 (err = CALL(p, fn_block_begin, s)) != 0)
268 return err;
269 } else if (strcasecmp(name, "END") == 0) {
270 if ((err = hook_block_end_before(p, s)) != 0 ||
271 (err = CALL(p, fn_block_end, s)) != 0 ||
272 (err = hook_block_end_after(p, s)) != 0)
273 return err;
274 } else {
275 if ((err = hook_field_value(p, name, s)) != 0 ||
276 (err = CALL(p, fn_field_value, name, s)) != 0)
277 return err;
278 }
279 return 0;
280 }
281
282 static ssize_t
283 ical_getline(char **contentline, char **line, size_t *sz, FILE *fp)
284 {
285 size_t num = 0;
286 int c;
287
288 if ((*contentline = realloc(*contentline, 1)) == NULL)
289 return -1;
290 **contentline = '\0';
291
292 do {
293 if (getline(line, sz, fp) <= 0)
294 goto end;
295 num++;
296 strchomp(*line);
297
298 if (strappend(contentline, *line) == NULL)
299 return -1;
300 if ((c = fgetc(fp)) == EOF)
301 goto end;
302 } while (c == ' ');
303 ungetc(c, fp);
304 assert(!ferror(fp));
305 end:
306 return ferror(fp) ? -1 : num;
307 }
308
309 int
310 ical_parse(IcalParser *p, FILE *fp)
311 {
312 char *line = NULL, *contentline = NULL;
313 size_t sz = 0;
314 ssize_t l;
315 int err;
316
317 p->current = p->stack;
318 p->linenum = 0;
319 p->blocktype = ICAL_BLOCK_OTHER;
320
321 do {
322 if ((l = ical_getline(&contentline, &line, &sz, fp)) < 0…
323 err = ical_err(p, "readling line");
324 break;
325 }
326 p->linenum += l;
327 } while (l > 0 && (err = ical_parse_contentline(p, conten…
328
329 free(contentline);
330
331 if (err == 0 && p->current != p->stack)
332 return ical_err(p, "more BEGIN: than END:");
333
334 return err;
335 }
You are viewing proxied material from bitreich.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.