tical.c - ics2txt - convert icalendar .ics file to plain text
(HTM) git clone git://bitreich.org/ics2txt git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/ics2txt
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) Tags
(DIR) README
---
tical.c (5753B)
---
1 #include "ical.h"
2
3 #include <assert.h>
4 #include <errno.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <strings.h> /* strcase* */
9
10 #include "util.h"
11
12 enum ical_err ical_errno;
13
14 int
15 ical_getline(char **line, char **ln, size_t *sz, FILE *fp)
16 {
17 int c;
18 void *v;
19
20 if ((v = realloc(*line, 1)) == NULL)
21 return -ICAL_ERR_SYSTEM;
22 *line = v;
23 (*line)[0] = '\0';
24
25 do { top:
26 if (getline(ln, sz, fp) <= 0)
27 return ferror(fp) ? -ICAL_ERR_SYSTEM : 0;
28 strchomp(*ln);
29 if (**ln == '\0')
30 goto top;
31 if (strappend(line, *ln) < 0)
32 return -ICAL_ERR_SYSTEM;
33 if ((c = fgetc(fp)) == EOF)
34 return ferror(fp) ? -ICAL_ERR_SYSTEM : 1;
35 } while (c == ' ');
36
37 ungetc(c, fp);
38 assert(!ferror(fp));
39 return 1;
40 }
41
42 char *
43 ical_strerror(int i)
44 {
45 enum ical_err err = (i > 0) ? i : -i;
46
47 switch (err) {
48 case ICAL_ERR_OK:
49 return "no error";
50 case ICAL_ERR_SYSTEM:
51 return "system error";
52 case ICAL_ERR_END_MISMATCH:
53 return "END: does not match its corresponding BEGIN:";
54 case ICAL_ERR_MISSING_BEGIN:
55 return "unexpected content line before any BEGIN:";
56 case ICAL_ERR_MISSING_COLUMN:
57 return "missing ':' character from line";
58 case ICAL_ERR_MISSING_SEMICOLUMN:
59 return "missing ';' character before ':'";
60 case ICAL_ERR_MISSING_EQUAL:
61 return "missing '=' character in parameter before ':'";
62 case ICAL_ERR_MIN_NESTED:
63 return "too many END: for the number of BEGIN:";
64 case ICAL_ERR_MAX_NESTED:
65 return "maximum nesting level reached";
66 case ICAL_ERR_LENGTH:
67 assert(!"used internally, should not happen");
68 }
69 assert(!"unknown error code");
70 return "not a valid ical error code";
71 }
72
73 struct ical_value *
74 ical_new_value(char const *line)
75 {
76 struct ical_value *new;
77 size_t len;
78
79 len = strlen(line);
80 if ((new = calloc(1, sizeof *new + len + 1)) == NULL)
81 return NULL;
82 memcpy(new->buf, line, len + 1);
83 return new;
84 }
85
86 void
87 ical_free_value(struct ical_value *value)
88 {
89 map_free(&value->param, NULL);
90 free(value);
91 }
92
93 int
94 ical_parse_value(struct ical_value *value)
95 {
96 char *column, *equal, *param, *cp;
97 int e = errno;
98
99 value->name = value->buf;
100
101 if ((column = strchr(value->buf, ':')) == NULL)
102 return -ICAL_ERR_MISSING_COLUMN;
103 *column = '\0';
104 value->value = column + 1;
105
106 if ((cp = strchr(value->buf, ';')) != NULL)
107 *cp++ = '\0';
108 while ((param = strsep(&cp, ";")) != NULL) {
109 if ((equal = strchr(param, '=')) == NULL)
110 return -ICAL_ERR_MISSING_EQUAL;
111 *equal = '\0';
112 if (map_set(&value->param, param, equal + 1) < 0)
113 return -ICAL_ERR_SYSTEM;
114 }
115
116 assert(errno == e);
117 return 0;
118 }
119
120 struct ical_vnode *
121 ical_new_vnode(char const *name)
122 {
123 struct ical_vnode *new;
124 size_t sz;
125
126 if ((new = calloc(1, sizeof *new)) == NULL)
127 return NULL;
128 sz = sizeof new->name;
129 if (strlcpy(new->name, name, sz) >= sz) {
130 errno = EMSGSIZE;
131 goto err;
132 }
133 return new;
134 err:
135 ical_free_vnode(new);
136 return NULL;
137 }
138
139 static void
140 ical_free_value_void(void *v)
141 {
142 ical_free_value(v);
143 }
144
145 static void
146 ical_free_vnode_void(void *v)
147 {
148 ical_free_vnode(v);
149 }
150
151 void
152 ical_free_vnode(struct ical_vnode *node)
153 {
154 if (node == NULL)
155 return;
156 map_free(&node->values, ical_free_value_void);
157 map_free(&node->childs, ical_free_vnode_void);
158 ical_free_vnode(node->next);
159 free(node);
160 }
161
162 int
163 ical_push_nested(struct ical_vcalendar *vcal, struct ical_vnode *new)
164 {
165 struct ical_vnode **node;
166
167 node = vcal->nested;
168 for (int i = 0; *node != NULL; node++, i++) {
169 if (i >= ICAL_NESTED_MAX)
170 return -ICAL_ERR_MAX_NESTED;
171 }
172 node[0] = new;
173 node[1] = NULL;
174 return 0;
175 }
176
177 struct ical_vnode *
178 ical_pop_nested(struct ical_vcalendar *vcal)
179 {
180 struct ical_vnode **node, **prev = vcal->nested, *old;
181
182 for (prev = node = vcal->nested; *node != NULL; node++) {
183 vcal->current = *prev;
184 prev = node;
185 old = *node;
186 }
187 *prev = NULL;
188 if (vcal->nested[0] == NULL)
189 vcal->current = NULL;
190 return old;
191 }
192
193 int
194 ical_begin_vnode(struct ical_vcalendar *vcal, char const *name)
195 {
196 struct ical_vnode *new;
197 int e;
198
199 if ((new = ical_new_vnode(name)) == NULL)
200 return -ICAL_ERR_SYSTEM;
201 if ((e = ical_push_nested(vcal, new)) < 0)
202 goto err;
203 if (vcal->root == NULL) {
204 vcal->root = new;
205 } else {
206 new->next = map_get(&vcal->current->childs, new->name);
207 if (map_set(&vcal->current->childs, new->name, new) < 0) {
208 e = -ICAL_ERR_SYSTEM;
209 goto err;
210 }
211 }
212 vcal->current = new;
213 return 0;
214 err:
215 ical_free_vnode(new);
216 return e;
217 }
218
219 int
220 ical_end_vnode(struct ical_vcalendar *vcal, char const *name)
221 {
222 struct ical_vnode *old;
223
224 if ((old = ical_pop_nested(vcal)) == NULL)
225 return -ICAL_ERR_MIN_NESTED;
226 if (strcasecmp(name, old->name) != 0)
227 return -ICAL_ERR_END_MISMATCH;
228 return 0;
229 }
230
231 int
232 ical_push_value(struct ical_vcalendar *vcal, struct ical_value *new)
233 {
234 if (strcasecmp(new->name, "BEGIN") == 0) {
235 int e = ical_begin_vnode(vcal, new->value);
236 ical_free_value(new);
237 return e;
238 }
239 if (strcasecmp(new->name, "END") == 0) {
240 int e = ical_end_vnode(vcal, new->value);
241 ical_free_value(new);
242 return e;
243 }
244
245 if (vcal->current == NULL)
246 return -ICAL_ERR_MISSING_BEGIN;
247
248 new->next = map_get(&vcal->current->values, new->name);
249 if (map_set(&vcal->current->values, new->name, new) < 0)
250 return -ICAL_ERR_SYSTEM;
251
252 return 0;
253 }
254
255 int
256 ical_read_vcalendar(struct ical_vcalendar *vcal, FILE *fp)
257 {
258 char *line = NULL, *ln = NULL;
259 size_t sz = 0;
260 ssize_t r;
261 int e;
262
263 memset(vcal, 0, sizeof *vcal);
264
265 while ((r = ical_getline(&line, &ln, &sz, fp)) > 0) {
266 struct ical_value *new;
267
268 if ((new = ical_new_value(line)) == NULL) {
269 e = -ICAL_ERR_SYSTEM;
270 goto err;
271 }
272 if ((e = ical_parse_value(new)) < 0)
273 goto err;
274 if ((e = ical_push_value(vcal, new)) < 0)
275 goto err;
276 }
277 e = (r == 0) ? 0 : -ICAL_ERR_SYSTEM;
278 err:
279 free(line);
280 free(ln);
281 return e;
282 }
283
284 void
285 ical_free_vcalendar(struct ical_vcalendar *vcal)
286 {
287 ical_free_vnode(vcal->root);
288 }