starter: Add support for multi-line strings in ipsec.conf
[strongswan.git] / src / libstrongswan / settings / settings_parser.y
1 %{
2 /*
3  * Copyright (C) 2014 Tobias Brunner
4  * Hochschule fuer Technik Rapperswil
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; either version 2 of the License, or (at your
9  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * for more details.
15  */
16
17 #define _GNU_SOURCE /* for asprintf() */
18 #include <stdio.h>
19
20 #include <library.h>
21 #include <collections/array.h>
22 #include <settings/settings_types.h>
23 #include <utils/parser_helper.h>
24
25 #include "settings_parser.h"
26
27 #define YYDEBUG 1
28
29 /**
30  * Defined by the lexer
31  */
32 int settings_parser_lex(YYSTYPE *lvalp, void *scanner);
33 int settings_parser_lex_init_extra(parser_helper_t *extra, void *scanner);
34 int settings_parser_lex_destroy(void *scanner);
35 int settings_parser_set_in(FILE *in, void *scanner);
36 void settings_parser_set_debug(int debug, void *scanner);
37 char *settings_parser_get_text(void *scanner);
38 int settings_parser_get_leng(void *scanner);
39 int settings_parser_get_lineno(void *scanner);
40 /* Custom functions in lexer */
41 bool settings_parser_open_next_file(parser_helper_t *ctx);
42
43 /**
44  * Forward declarations
45  */
46 static void settings_parser_error(parser_helper_t *ctx, const char *s);
47 static section_t *push_section(parser_helper_t *ctx, char *name);
48 static section_t *pop_section(parser_helper_t *ctx);
49 static void add_section(parser_helper_t *ctx, section_t *section);
50 static void add_setting(parser_helper_t *ctx, kv_t *kv);
51
52 /**
53  * Make sure to call lexer with the proper context
54  */
55 #undef yylex
56 static int yylex(YYSTYPE *lvalp, parser_helper_t *ctx)
57 {
58         return settings_parser_lex(lvalp, ctx->scanner);
59 }
60
61 %}
62 %debug
63
64 /* generate verbose error messages */
65 %error-verbose
66 /* generate a reentrant parser */
67 %define api.pure
68 /* prefix function/variable declarations */
69 %name-prefix "settings_parser_"
70
71 /* interact properly with the reentrant lexer */
72 %lex-param {parser_helper_t *ctx}
73 %parse-param {parser_helper_t *ctx}
74
75 /* types for terminal symbols... (can't use the typedef'd types) */
76 %union {
77         char *s;
78         struct section_t *sec;
79         struct kv_t *kv;
80 }
81 %token <s> NAME STRING
82 %token NEWLINE STRING_ERROR
83
84 /* ...and other symbols */
85 %type <s> value valuepart
86 %type <sec> section_start section
87 %type <kv> setting
88
89 /* properly destroy string tokens that are strdup()ed on error */
90 %destructor { free($$); } NAME STRING value valuepart
91 /* properly destroy parse results on error */
92 %destructor { pop_section(ctx); settings_section_destroy($$, NULL); } section_start section
93 %destructor { settings_kv_destroy($$, NULL); } setting
94
95 /* there are two shift/reduce conflicts because of the "NAME = NAME" and
96  * "NAME {" ambiguity, and the "NAME =" rule) */
97 %expect 2
98
99 %%
100
101 /**
102  * strongswan.conf grammar rules
103  */
104 statements:
105         /* empty */
106         | statements NEWLINE
107         | statements statement
108         ;
109
110 statement:
111         section
112         {
113                 add_section(ctx, $section);
114         }
115         | setting
116         {
117                 add_setting(ctx, $setting);
118         }
119         ;
120
121 section:
122         section_start statements '}'
123         {
124                 pop_section(ctx);
125                 $$ = $section_start;
126         }
127         ;
128
129 section_start:
130         NAME '{'
131         {
132                 $$ = push_section(ctx, $NAME);
133         }
134         |
135         NAME NEWLINE '{'
136         {
137                 $$ = push_section(ctx, $NAME);
138         }
139         ;
140
141 setting:
142         NAME '=' value
143         {
144                 $$ = settings_kv_create($NAME, $value);
145         }
146         |
147         NAME '='
148         {
149                 $$ = settings_kv_create($NAME, NULL);
150         }
151         ;
152
153 value:
154         valuepart
155         | value valuepart
156         {       /* just put a single space between them, use strings for more */
157                 if (asprintf(&$$, "%s %s", $1, $2) < 0)
158                 {
159                         free($1);
160                         free($2);
161                         YYERROR;
162                 }
163                 free($1);
164                 free($2);
165         }
166         ;
167
168 valuepart:
169         NAME
170         | STRING
171         ;
172
173 %%
174
175 /**
176  * Referenced by the generated parser
177  */
178 static void settings_parser_error(parser_helper_t *ctx, const char *s)
179 {
180         char *text = settings_parser_get_text(ctx->scanner);
181         int len = settings_parser_get_leng(ctx->scanner);
182
183         if (len && text[len-1] == '\n')
184         {       /* cut off newline at the end to avoid muti-line log messages */
185                 len--;
186         }
187         PARSER_DBG1(ctx, "%s [%.*s]", s, len, text);
188 }
189
190 /**
191  * Create a section and push it to the stack (the name is adopted), returns
192  * the created section
193  */
194 static section_t *push_section(parser_helper_t *ctx, char *name)
195 {
196         array_t *sections = (array_t*)ctx->context;
197         section_t *section;
198
199         section = settings_section_create(name);
200         array_insert(sections, ARRAY_TAIL, section);
201         return section;
202 }
203
204 /**
205  * Removes the top section of the stack and returns it
206  */
207 static section_t *pop_section(parser_helper_t *ctx)
208 {
209         array_t *sections = (array_t*)ctx->context;
210         section_t *section;
211
212         array_remove(sections, ARRAY_TAIL, &section);
213         return section;
214 }
215
216 /**
217  * Adds the given section to the section on top of the stack
218  */
219 static void add_section(parser_helper_t *ctx, section_t *section)
220 {
221         array_t *sections = (array_t*)ctx->context;
222         section_t *parent;
223
224         array_get(sections, ARRAY_TAIL, &parent);
225         settings_section_add(parent, section, NULL);
226 }
227
228 /**
229  * Adds the given key/value pair to the section on top of the stack
230  */
231 static void add_setting(parser_helper_t *ctx, kv_t *kv)
232 {
233         array_t *sections = (array_t*)ctx->context;
234         section_t *section;
235
236         array_get(sections, ARRAY_TAIL, &section);
237         settings_kv_add(section, kv, NULL);
238 }
239
240 /**
241  * Parse the given file and add all sections and key/value pairs to the
242  * given section.
243  */
244 bool settings_parser_parse_file(section_t *root, char *name)
245 {
246         parser_helper_t *helper;
247         array_t *sections = NULL;
248         bool success = FALSE;
249
250         array_insert_create(&sections, ARRAY_TAIL, root);
251         helper = parser_helper_create(sections);
252         helper->get_lineno = settings_parser_get_lineno;
253         if (settings_parser_lex_init_extra(helper, &helper->scanner) != 0)
254         {
255                 helper->destroy(helper);
256                 array_destroy(sections);
257                 return FALSE;
258         }
259         helper->file_include(helper, name);
260         if (!settings_parser_open_next_file(helper))
261         {
262                 if (lib->conf && streq(name, lib->conf))
263                 {
264                         DBG2(DBG_CFG, "failed to open config file '%s'", name);
265                 }
266                 else
267                 {
268                         DBG1(DBG_CFG, "failed to open config file '%s'", name);
269                 }
270         }
271         else
272         {
273                 if (getenv("DEBUG_SETTINGS_PARSER"))
274                 {
275                         yydebug = 1;
276                         settings_parser_set_debug(1, helper->scanner);
277                 }
278                 success = yyparse(helper) == 0;
279                 if (!success)
280                 {
281                         DBG1(DBG_CFG, "invalid config file '%s'", name);
282                 }
283         }
284         array_destroy(sections);
285         settings_parser_lex_destroy(helper->scanner);
286         helper->destroy(helper);
287         return success;
288 }