1 /* icalparse.y -- icalendar (RFC 5545) parser
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.
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.
17 #define yydebug icaldebug
18 #define yyerror icalerror
20 #define yylval icallval
22 #define yyparse icalparse
25 #define YYERROR_VERBOSE
29 * To quiet compile warnings with Solaris yacc:
37 #include "h/icalendar.h"
40 static char *append (contentline *, const char *, const size_t);
41 static void new_content_line (contentline **);
42 static void new_vevent (vevent *);
43 static void free_param_names (param_list *);
44 static void free_param_values (value_list *);
45 static int icalerror (const char *);
48 %token ICAL_NAME ICAL_COLON ICAL_VALUE ICAL_CRLF ICAL_SEMICOLON
49 %token ICAL_PARAM_NAME ICAL_EQUAL ICAL_PARAM_VALUE ICAL_COMMA
51 %start contentline_list
55 /* Instead of rigorous definition, cheat based on fact that every
56 icalbody line looks like a contentline. And we don't need to
60 | contentline_list contentline
62 /* contentline = name *(";" param ) ":" value CRLF */
65 new_content_line (&vevents.last->contentlines);
66 append (vevents.last->contentlines->last, $1, strlen ($1));
67 vevents.last->contentlines->last->name = $1;
69 append (vevents.last->contentlines->last, $3, strlen ($3));
71 append (vevents.last->contentlines->last, $5, strlen ($5));
72 vevents.last->contentlines->last->value = $5;
74 append (vevents.last->contentlines->last, $7, strlen ($7));
75 if (vevents.last->contentlines->cr_before_lf == CR_UNSET) {
76 vevents.last->contentlines->cr_before_lf =
77 $7[0] == '\r' ? CR_BEFORE_LF : LF_ONLY;
79 /* END:VEVENT doesn't have a param_list so we don't need
80 to check for it below. */
81 if (vevents.last->contentlines->last->name &&
82 vevents.last->contentlines->last->value &&
83 ! strcasecmp (vevents.last->contentlines->last->name, "END") &&
84 ! strcasecmp (vevents.last->contentlines->last->value,
86 new_vevent (&vevents);
90 new_content_line (&vevents.last->contentlines);
91 append (vevents.last->contentlines->last, $1, strlen ($1));
92 vevents.last->contentlines->last->name = $1;
93 } param_list ICAL_COLON {
94 append (vevents.last->contentlines->last, $4, strlen ($4));
96 append (vevents.last->contentlines->last, $6, strlen ($6));
97 vevents.last->contentlines->last->value = $6;
99 append (vevents.last->contentlines->last, $8, strlen ($8));
100 if (vevents.last->contentlines->cr_before_lf == CR_UNSET) {
101 vevents.last->contentlines->cr_before_lf =
102 $8[0] == '\r' ? CR_BEFORE_LF : LF_ONLY;
108 append (vevents.last->contentlines->last, $1, strlen ($1));
110 | param_list ICAL_SEMICOLON {
111 append (vevents.last->contentlines->last, $2, strlen ($2));
114 /* param = param-name "=" param-value *("," param-value) */
117 append (vevents.last->contentlines->last, $1, strlen ($1));
118 add_param_name (vevents.last->contentlines->last, $1);
120 append (vevents.last->contentlines->last, $3, strlen ($3));
125 append (vevents.last->contentlines->last, $1, strlen ($1));
126 add_param_value (vevents.last->contentlines->last, $1);
128 | param_value_list ICAL_COMMA {
129 append (vevents.last->contentlines->last, $2, strlen ($2));
131 append (vevents.last->contentlines->last, $4, strlen ($4));
132 add_param_value (vevents.last->contentlines->last, $4);
138 * Remove the contentline node (by setting its name to NULL).
141 remove_contentline (contentline *node)
148 add_contentline (contentline *node, const char *name)
150 contentline *new_node;
153 new_node->name = mh_xstrdup (name);
154 new_node->next = node->next;
155 node->next = new_node;
161 * Remove the value from a value_list.
164 remove_value (value_list *node)
171 * Find the contentline with the specified name, and optionally,
172 * the specified value and/or parameter name.
175 find_contentline (contentline *contentlines, const char *name,
180 for (node = contentlines; node; node = node->next) {
181 /* node->name will be NULL if the line was "deleted". */
182 if (node->name && ! strcasecmp (name, node->name)) {
183 if (!val || !node->value || !strcasecmp(val, node->value))
192 append (contentline *cline, const char *src, const size_t src_len)
195 const size_t len = cline->input_line_len + src_len;
197 while (len >= cline->input_line_size) {
198 cline->input_line_size = cline->input_line_size == 0
200 : 2 * cline->input_line_size;
202 mh_xrealloc (cline->input_line, cline->input_line_size);
205 memcpy (cline->input_line + cline->input_line_len, src, src_len);
206 cline->input_line[len] = '\0';
207 cline->input_line_len = len;
210 return cline->input_line;
214 new_content_line (contentline **cline)
216 contentline *new_node;
220 /* Append the new node to the end of the list. */
221 (*cline)->last->next = new_node;
223 /* First line: save the root node in *cline. */
227 /* Only maintain the pointer to the last node in the root node. */
228 (*cline)->last = new_node;
232 new_vevent (vevent *event)
234 vevent *new_node, *node;
238 /* Append the new node to the end of the list. */
239 for (node = event; node->next; node = node->next) { continue; }
240 event->last = node->next = new_node;
244 add_param_name (contentline *cline, char *name)
246 param_list *new_node;
250 new_node->param_name = name;
253 for (p = cline->params; p->next; p = p->next) { continue; }
254 /* The loop terminated at, not after, the last node. */
257 cline->params = new_node;
262 * Add a value to the last parameter seen.
265 add_param_value (contentline *cline, char *value)
267 value_list *new_node;
272 new_node->value = value;
275 for (p = cline->params; p->next; p = p->next) { continue; }
276 /* The loop terminated at, not after, the last param_list node. */
279 for (v = p->values; v->next; v = v->next) { continue; }
280 /* The loop terminated at, not after, the last value_list node. */
283 p->values = new_node;
286 /* Never should get here because a param value is always
287 preceded by a param name. */
293 free_contentlines (contentline *root)
295 contentline *i, *next;
297 for (i = root; i; i = next) {
300 free_param_names (i->params);
303 free (i->input_line);
304 charstring_free (i->unexpected);
311 free_param_names (param_list *p)
315 for ( ; p; p = next) {
316 free (p->param_name);
317 free_param_values (p->values);
324 free_param_values (value_list *v)
328 for ( ; v; v = next) {
336 icalerror (const char *error)
339 charstring_t context = NULL;
341 /* Find last chunk of unexpected text. */
342 for (c = vevents.last->contentlines; c; c = c->next) {
344 context = c->unexpected;
348 if (! strcmp ("syntax error, unexpected $end, expecting ICAL_NAME",
350 /* Empty input: produce no output. */
353 inform ("%s after \"%s\"", error, charstring_buffer (context));
355 inform ("%s", error);
361 return 0; /* The return value isn't used anyway. */
365 * In case YYDEBUG is disabled above; mhical refers to icaldebug.
367 #if ! defined YYDEBUG || ! YYDEBUG
369 #endif /* ! YYDEBUG */