]> diplodocus.org Git - nmh/blob - sbr/icalparse.y
sendsbr.c: Move interface to own file.
[nmh] / sbr / icalparse.y
1 /* icalparse.y -- icalendar (RFC 5545) parser
2 *
3 * This code is Copyright (c) 2014, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 %{
9 /*
10 * Use these yy* #defines, instead of the -p command line
11 * option, to allow multiple parsers in a program. yyval
12 * is generated by Solaris yacc and is of type YYSTYPE.
13 * All other yy* symbols are data of a built-in type and
14 * are initialized by yyparse(), so they can be shared
15 * between different parsers.
16 */
17 #define yydebug icaldebug
18 #define yyerror icalerror
19 #define yylex icallex
20 #define yylval icallval
21 #define yyval icalval
22 #define yyparse icalparse
23 #define YYDEBUG 1
24 #define YYERROR_DEBUG
25 #define YYERROR_VERBOSE
26 #define YY_NO_LEAKS
27
28 /*
29 * To quiet compile warnings with Solaris yacc:
30 #ifdef sun
31 # define lint 1
32 # undef YYDEBUG
33 #endif
34 */
35
36 #include "h/mh.h"
37 #include "error.h"
38 #include "h/icalendar.h"
39 #include "h/utils.h"
40
41 static char *append (contentline *, const char *, const size_t);
42 static void new_content_line (contentline **);
43 static void new_vevent (vevent *);
44 static void free_param_names (param_list *);
45 static void free_param_values (value_list *);
46 static int icalerror (const char *);
47 %}
48
49 %token ICAL_NAME ICAL_COLON ICAL_VALUE ICAL_CRLF ICAL_SEMICOLON
50 %token ICAL_PARAM_NAME ICAL_EQUAL ICAL_PARAM_VALUE ICAL_COMMA
51
52 %start contentline_list
53
54 %%
55
56 /* Instead of rigorous definition, cheat based on fact that every
57 icalbody line looks like a contentline. And we don't need to
58 parse values. */
59 contentline_list
60 : contentline
61 | contentline_list contentline
62
63 /* contentline = name *(";" param ) ":" value CRLF */
64 contentline
65 : ICAL_NAME {
66 new_content_line (&vevents.last->contentlines);
67 append (vevents.last->contentlines->last, $1, strlen ($1));
68 vevents.last->contentlines->last->name = $1;
69 } ICAL_COLON {
70 append (vevents.last->contentlines->last, $3, strlen ($3));
71 } ICAL_VALUE {
72 append (vevents.last->contentlines->last, $5, strlen ($5));
73 vevents.last->contentlines->last->value = $5;
74 } ICAL_CRLF {
75 append (vevents.last->contentlines->last, $7, strlen ($7));
76 if (vevents.last->contentlines->cr_before_lf == CR_UNSET) {
77 vevents.last->contentlines->cr_before_lf =
78 $7[0] == '\r' ? CR_BEFORE_LF : LF_ONLY;
79 }
80 /* END:VEVENT doesn't have a param_list so we don't need
81 to check for it below. */
82 if (vevents.last->contentlines->last->name &&
83 vevents.last->contentlines->last->value &&
84 ! strcasecmp (vevents.last->contentlines->last->name, "END") &&
85 ! strcasecmp (vevents.last->contentlines->last->value,
86 "VEVENT")) {
87 new_vevent (&vevents);
88 }
89 }
90 | ICAL_NAME {
91 new_content_line (&vevents.last->contentlines);
92 append (vevents.last->contentlines->last, $1, strlen ($1));
93 vevents.last->contentlines->last->name = $1;
94 } param_list ICAL_COLON {
95 append (vevents.last->contentlines->last, $4, strlen ($4));
96 } ICAL_VALUE {
97 append (vevents.last->contentlines->last, $6, strlen ($6));
98 vevents.last->contentlines->last->value = $6;
99 } ICAL_CRLF {
100 append (vevents.last->contentlines->last, $8, strlen ($8));
101 if (vevents.last->contentlines->cr_before_lf == CR_UNSET) {
102 vevents.last->contentlines->cr_before_lf =
103 $8[0] == '\r' ? CR_BEFORE_LF : LF_ONLY;
104 }
105 }
106
107 param_list
108 : ICAL_SEMICOLON {
109 append (vevents.last->contentlines->last, $1, strlen ($1));
110 } param
111 | param_list ICAL_SEMICOLON {
112 append (vevents.last->contentlines->last, $2, strlen ($2));
113 } param
114
115 /* param = param-name "=" param-value *("," param-value) */
116 param
117 : ICAL_PARAM_NAME {
118 append (vevents.last->contentlines->last, $1, strlen ($1));
119 add_param_name (vevents.last->contentlines->last, $1);
120 } ICAL_EQUAL {
121 append (vevents.last->contentlines->last, $3, strlen ($3));
122 } param_value_list
123
124 param_value_list
125 : ICAL_PARAM_VALUE {
126 append (vevents.last->contentlines->last, $1, strlen ($1));
127 add_param_value (vevents.last->contentlines->last, $1);
128 }
129 | param_value_list ICAL_COMMA {
130 append (vevents.last->contentlines->last, $2, strlen ($2));
131 } ICAL_PARAM_VALUE {
132 append (vevents.last->contentlines->last, $4, strlen ($4));
133 add_param_value (vevents.last->contentlines->last, $4);
134 }
135
136 %%
137
138 /*
139 * Remove the contentline node (by setting its name to NULL).
140 */
141 void
142 remove_contentline (contentline *node)
143 {
144 free (node->name);
145 node->name = NULL;
146 }
147
148 contentline *
149 add_contentline (contentline *node, const char *name)
150 {
151 contentline *new_node;
152
153 NEW0(new_node);
154 new_node->name = mh_xstrdup (name);
155 new_node->next = node->next;
156 node->next = new_node;
157
158 return new_node;
159 }
160
161 /*
162 * Remove the value from a value_list.
163 */
164 void
165 remove_value (value_list *node)
166 {
167 free (node->value);
168 node->value = NULL;
169 }
170
171 /*
172 * Find the contentline with the specified name, and optionally,
173 * the specified value and/or parameter name.
174 */
175 contentline *
176 find_contentline (contentline *contentlines, const char *name,
177 const char *val)
178 {
179 contentline *node;
180
181 for (node = contentlines; node; node = node->next) {
182 /* node->name will be NULL if the line was "deleted". */
183 if (node->name && ! strcasecmp (name, node->name)) {
184 if (!val || !node->value || !strcasecmp(val, node->value))
185 return node;
186 }
187 }
188
189 return NULL;
190 }
191
192 static char *
193 append (contentline *cline, const char *src, const size_t src_len)
194 {
195 if (src_len > 0) {
196 const size_t len = cline->input_line_len + src_len;
197
198 while (len >= cline->input_line_size) {
199 cline->input_line_size = cline->input_line_size == 0
200 ? NMH_BUFSIZ
201 : 2 * cline->input_line_size;
202 cline->input_line =
203 mh_xrealloc (cline->input_line, cline->input_line_size);
204 }
205
206 memcpy (cline->input_line + cline->input_line_len, src, src_len);
207 cline->input_line[len] = '\0';
208 cline->input_line_len = len;
209 }
210
211 return cline->input_line;
212 }
213
214 static void
215 new_content_line (contentline **cline)
216 {
217 contentline *new_node;
218
219 NEW0(new_node);
220 if (*cline) {
221 /* Append the new node to the end of the list. */
222 (*cline)->last->next = new_node;
223 } else {
224 /* First line: save the root node in *cline. */
225 *cline = new_node;
226 }
227
228 /* Only maintain the pointer to the last node in the root node. */
229 (*cline)->last = new_node;
230 }
231
232 static void
233 new_vevent (vevent *event)
234 {
235 vevent *new_node, *node;
236
237 NEW0(new_node);
238
239 /* Append the new node to the end of the list. */
240 for (node = event; node->next; node = node->next) { continue; }
241 event->last = node->next = new_node;
242 }
243
244 void
245 add_param_name (contentline *cline, char *name)
246 {
247 param_list *new_node;
248 param_list *p;
249
250 NEW0(new_node);
251 new_node->param_name = name;
252
253 if (cline->params) {
254 for (p = cline->params; p->next; p = p->next) { continue; }
255 /* The loop terminated at, not after, the last node. */
256 p->next = new_node;
257 } else {
258 cline->params = new_node;
259 }
260 }
261
262 /*
263 * Add a value to the last parameter seen.
264 */
265 void
266 add_param_value (contentline *cline, char *value)
267 {
268 value_list *new_node;
269 param_list *p;
270 value_list *v;
271
272 NEW0(new_node);
273 new_node->value = value;
274
275 if (cline->params) {
276 for (p = cline->params; p->next; p = p->next) { continue; }
277 /* The loop terminated at, not after, the last param_list node. */
278
279 if (p->values) {
280 for (v = p->values; v->next; v = v->next) { continue; }
281 /* The loop terminated at, not after, the last value_list node. */
282 v->next = new_node;
283 } else {
284 p->values = new_node;
285 }
286 } else {
287 /* Never should get here because a param value is always
288 preceded by a param name. */
289 free (new_node);
290 }
291 }
292
293 void
294 free_contentlines (contentline *root)
295 {
296 contentline *i, *next;
297
298 for (i = root; i; i = next) {
299 free (i->name);
300 if (i->params) {
301 free_param_names (i->params);
302 }
303 free (i->value);
304 free (i->input_line);
305 charstring_free (i->unexpected);
306 next = i->next;
307 free (i);
308 }
309 }
310
311 static void
312 free_param_names (param_list *p)
313 {
314 param_list *next;
315
316 for ( ; p; p = next) {
317 free (p->param_name);
318 free_param_values (p->values);
319 next = p->next;
320 free (p);
321 }
322 }
323
324 static void
325 free_param_values (value_list *v)
326 {
327 value_list *next;
328
329 for ( ; v; v = next) {
330 free (v->value);
331 next = v->next;
332 free (v);
333 }
334 }
335
336 static int
337 icalerror (const char *error)
338 {
339 contentline *c;
340 charstring_t context = NULL;
341
342 /* Find last chunk of unexpected text. */
343 for (c = vevents.last->contentlines; c; c = c->next) {
344 if (c->unexpected) {
345 context = c->unexpected;
346 }
347 }
348
349 if (! strcmp ("syntax error, unexpected $end, expecting ICAL_NAME",
350 error)) {
351 /* Empty input: produce no output. */
352 } else {
353 if (context) {
354 inform ("%s after \"%s\"", error, charstring_buffer (context));
355 } else {
356 inform ("%s", error);
357 }
358 parser_status = -1;
359 return -1;
360 }
361
362 return 0; /* The return value isn't used anyway. */
363 }
364
365 /*
366 * In case YYDEBUG is disabled above; mhical refers to icaldebug.
367 */
368 #if ! defined YYDEBUG || ! YYDEBUG
369 int icaldebug;
370 #endif /* ! YYDEBUG */