2 * icalparse.y -- icalendar (RFC 5545) parser
4 * This code is Copyright (c) 2014, by the authors of nmh. See the
5 * COPYRIGHT file in the root directory of the nmh distribution for
6 * complete copyright information.
11 * Use these yy* #defines, instead of the -p command line
12 * option, to allow multiple parsers in a program. yyval
13 * is generated by Solaris yacc and is of type YYSTYPE.
14 * All other yy* symbols are data of a built-in type and
15 * are initialized by yyparse(), so they can be shared
16 * between different parsers.
18 #define yydebug icaldebug
19 #define yyerror icalerror
21 #define yylval icallval
23 #define yyparse icalparse
26 #define YYERROR_VERBOSE
30 * To quiet compile warnings with Solaris yacc:
38 #include "h/icalendar.h"
41 static char *append (contentline *, const char *, const size_t);
42 static void new_input_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 *);
49 %token ICAL_NAME ICAL_COLON ICAL_VALUE ICAL_CRLF ICAL_SEMICOLON
50 %token ICAL_PARAM_NAME ICAL_EQUAL ICAL_PARAM_VALUE ICAL_COMMA
52 %start contentline_list
56 /* Instead of rigorous definition, cheat based on fact that every
57 icalbody line looks like a contentline. And we don't need to
61 | contentline_list contentline
63 /* contentline = name *(";" param ) ":" value CRLF */
66 new_input_line (&vevents.last->contentlines);
67 append (vevents.last->contentlines->last, $1, strlen ($1));
68 vevents.last->contentlines->last->name = $1;
70 append (vevents.last->contentlines->last, $3, strlen ($3));
72 append (vevents.last->contentlines->last, $5, strlen ($5));
73 vevents.last->contentlines->last->value = $5;
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;
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,
87 new_vevent (&vevents);
91 new_input_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));
97 append (vevents.last->contentlines->last, $6, strlen ($6));
98 vevents.last->contentlines->last->value = $6;
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;
109 append (vevents.last->contentlines->last, $1, strlen ($1));
111 | param_list ICAL_SEMICOLON {
112 append (vevents.last->contentlines->last, $2, strlen ($2));
115 /* param = param-name "=" param-value *("," param-value) */
118 append (vevents.last->contentlines->last, $1, strlen ($1));
119 add_param_name (vevents.last->contentlines->last, $1);
121 append (vevents.last->contentlines->last, $3, strlen ($3));
126 append (vevents.last->contentlines->last, $1, strlen ($1));
127 add_param_value (vevents.last->contentlines->last, $1);
129 | param_value_list ICAL_COMMA {
130 append (vevents.last->contentlines->last, $2, strlen ($2));
132 append (vevents.last->contentlines->last, $4, strlen ($4));
133 add_param_value (vevents.last->contentlines->last, $4);
139 * Remove the contentline node (by setting its name to NULL).
142 remove_contentline (contentline *node) {
148 add_contentline (contentline *node, const char *name) {
149 contentline *new_node = mh_xcalloc (1, sizeof (contentline));
151 new_node->name = strdup (name);
152 new_node->next = node->next;
153 node->next = new_node;
159 * Remove the value from a value_list.
162 remove_value (value_list *node) {
168 * Find the contentline with the specified name, and optionally,
169 * the specified value and/or parameter name.
172 find_contentline (contentline *contentlines, const char *name,
176 for (node = contentlines; node; node = node->next) {
177 /* node->name will be NULL if the line was "deleted". */
178 if (node->name && ! strcasecmp (name, node->name)) {
179 if (val && node->value) {
180 if (! strcasecmp (val, node->value)) {
193 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
199 ? (BUFSIZ>=8192 ? BUFSIZ : 8192)
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_input_line (contentline **cline) {
215 contentline *new_node = mh_xcalloc (1, sizeof (contentline));
218 /* Append the new node to the end of the list. */
219 (*cline)->last->next = new_node;
221 /* First line: save the root node in *cline. */
225 /* Only maintain the pointer to the last node in the root node. */
226 (*cline)->last = new_node;
230 new_vevent (vevent *event) {
231 vevent *new_node = mh_xcalloc (1, sizeof (vevent)), *node;
233 /* Append the new node to the end of the list. */
234 for (node = event; node->next; node = node->next) { continue; }
235 event->last = node->next = new_node;
239 add_param_name (contentline *cline, char *name) {
240 param_list *new_node = mh_xcalloc (1, sizeof (param_list));
243 new_node->param_name = name;
246 for (p = cline->params; p->next; p = p->next) { continue; }
247 /* The loop terminated at, not after, the last node. */
250 cline->params = new_node;
255 * Add a value to the last parameter seen.
258 add_param_value (contentline *cline, char *value) {
259 value_list *new_node = mh_xcalloc (1, sizeof (value_list));
263 new_node->value = value;
266 for (p = cline->params; p->next; p = p->next) { continue; }
267 /* The loop terminated at, not after, the last param_list node. */
270 for (v = p->values; v->next; v = v->next) { continue; }
271 /* The loop terminated at, not after, the last value_list node. */
274 p->values = new_node;
277 /* Never should get here because a param value is always
278 preceded by a param name. */
284 free_contentlines (contentline *root) {
285 contentline *i, *next;
287 for (i = root; i; i = next) {
290 free_param_names (i->params);
293 free (i->input_line);
300 free_param_names (param_list *p) {
303 for ( ; p; p = next) {
304 free (p->param_name);
305 free_param_values (p->values);
312 free_param_values (value_list *v) {
315 for ( ; v; v = next) {
323 icalerror (const char *error) {
324 if (! strcmp ("syntax error, unexpected $end, expecting ICAL_NAME",
326 /* Empty input: produce no output. */
328 adios (NULL, "%s", error);
331 return 0; /* The return value isn't used anyway. */
335 * In case YYDEBUG is disabled above; mhical refers to icaldebug.
337 #if ! defined YYDEBUG || ! YYDEBUG
339 #endif /* ! YYDEBUG */