settings: Add reference feature
[strongswan.git] / src / libstrongswan / settings / settings_parser.y
1 %{
2 /*
3  * Copyright (C) 2014-2018 Tobias Brunner
4  * HSR 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 bool settings_parser_load_string(parser_helper_t *ctx, const char *content);
43
44 /**
45  * Forward declarations
46  */
47 static void settings_parser_error(parser_helper_t *ctx, const char *s);
48 static section_t *push_section(parser_helper_t *ctx, char *name);
49 static section_t *pop_section(parser_helper_t *ctx);
50 static void add_section(parser_helper_t *ctx, section_t *section);
51 static void add_setting(parser_helper_t *ctx, kv_t *kv);
52 static void add_references(parser_helper_t *ctx, array_t *references);
53
54 /**
55  * Make sure to call lexer with the proper context
56  */
57 #undef yylex
58 static int yylex(YYSTYPE *lvalp, parser_helper_t *ctx)
59 {
60         return settings_parser_lex(lvalp, ctx->scanner);
61 }
62
63 %}
64 %debug
65
66 /* generate verbose error messages */
67 %error-verbose
68 /* generate a reentrant parser */
69 %define api.pure
70 /* prefix function/variable declarations */
71 %name-prefix "settings_parser_"
72
73 /* interact properly with the reentrant lexer */
74 %lex-param {parser_helper_t *ctx}
75 %parse-param {parser_helper_t *ctx}
76
77 /* types for terminal symbols... (can't use the typedef'd types) */
78 %union {
79         char *s;
80         struct section_t *sec;
81         struct kv_t *kv;
82         array_t *refs;
83 }
84 %token <s> NAME STRING
85 %token REFS ":"
86 %token NEWLINE STRING_ERROR
87
88 /* ...and other symbols */
89 %type <s> value valuepart
90 %type <sec> section_start section
91 %type <kv> setting
92 %type <refs> references
93
94 /* properly destroy string tokens that are strdup()ed on error */
95 %destructor { free($$); } NAME STRING value valuepart
96 /* properly destroy parse results on error */
97 %destructor { pop_section(ctx); settings_section_destroy($$, NULL); } section_start section
98 %destructor { settings_kv_destroy($$, NULL); } setting
99 %destructor { array_destroy_function($$, (void*)free, NULL); } references
100
101 /* there are two shift/reduce conflicts because of the "NAME = NAME" and
102  * "NAME {" ambiguity, and the "NAME =" rule) */
103 %expect 2
104
105 %%
106
107 /**
108  * strongswan.conf grammar rules
109  */
110 statements:
111         /* empty */
112         | statements NEWLINE
113         | statements statement
114         ;
115
116 statement:
117         section
118         {
119                 add_section(ctx, $section);
120         }
121         | setting
122         {
123                 add_setting(ctx, $setting);
124         }
125         ;
126
127 section:
128         section_start statements '}'
129         {
130                 pop_section(ctx);
131                 $$ = $section_start;
132         }
133         ;
134
135 section_start:
136         NAME '{'
137         {
138                 $$ = push_section(ctx, $NAME);
139         }
140         |
141         NAME ":" references '{'
142         {
143                 $$ = push_section(ctx, $NAME);
144                 add_references(ctx, $references);
145                 array_destroy($references);
146         }
147         ;
148
149 references:
150         NAME
151         {
152                 $$ = array_create(0, 0);
153                 array_insert($$, ARRAY_TAIL, $1);
154         }
155         | references ',' NAME
156         {
157                 array_insert($1, ARRAY_TAIL, $3);
158                 $$ = $1;
159         }
160         ;
161
162 setting:
163         NAME '=' value
164         {
165                 $$ = settings_kv_create($NAME, $value);
166         }
167         |
168         NAME '='
169         {
170                 $$ = settings_kv_create($NAME, NULL);
171         }
172         ;
173
174 value:
175         valuepart
176         | value valuepart
177         {       /* just put a single space between them, use strings for more */
178                 if (asprintf(&$$, "%s %s", $1, $2) < 0)
179                 {
180                         free($1);
181                         free($2);
182                         YYERROR;
183                 }
184                 free($1);
185                 free($2);
186         }
187         ;
188
189 valuepart:
190         NAME
191         | STRING
192         ;
193
194 %%
195
196 /**
197  * Referenced by the generated parser
198  */
199 static void settings_parser_error(parser_helper_t *ctx, const char *s)
200 {
201         char *text = settings_parser_get_text(ctx->scanner);
202         int len = settings_parser_get_leng(ctx->scanner);
203
204         if (len && text[len-1] == '\n')
205         {       /* cut off newline at the end to avoid muti-line log messages */
206                 len--;
207         }
208         PARSER_DBG1(ctx, "%s [%.*s]", s, len, text);
209 }
210
211 /**
212  * Create a section and push it to the stack (the name is adopted), returns
213  * the created section
214  */
215 static section_t *push_section(parser_helper_t *ctx, char *name)
216 {
217         array_t *sections = (array_t*)ctx->context;
218         section_t *section;
219
220         section = settings_section_create(name);
221         array_insert(sections, ARRAY_TAIL, section);
222         return section;
223 }
224
225 /**
226  * Removes the top section of the stack and returns it
227  */
228 static section_t *pop_section(parser_helper_t *ctx)
229 {
230         array_t *sections = (array_t*)ctx->context;
231         section_t *section;
232
233         array_remove(sections, ARRAY_TAIL, &section);
234         return section;
235 }
236
237 /**
238  * Adds the given section to the section on top of the stack
239  */
240 static void add_section(parser_helper_t *ctx, section_t *section)
241 {
242         array_t *sections = (array_t*)ctx->context;
243         section_t *parent;
244
245         array_get(sections, ARRAY_TAIL, &parent);
246         settings_section_add(parent, section, NULL);
247 }
248
249 /**
250  * Adds the given key/value pair to the section on top of the stack
251  */
252 static void add_setting(parser_helper_t *ctx, kv_t *kv)
253 {
254         array_t *sections = (array_t*)ctx->context;
255         section_t *section;
256
257         array_get(sections, ARRAY_TAIL, &section);
258         settings_kv_add(section, kv, NULL);
259 }
260
261 /**
262  * Adds the given references to the section on top of the stack
263  */
264 static void add_references(parser_helper_t *ctx, array_t *references)
265 {
266         array_t *sections = (array_t*)ctx->context;
267         section_t *section;
268         enumerator_t *refs;
269         char *ref;
270
271         array_get(sections, ARRAY_TAIL, &section);
272
273         refs = array_create_enumerator(references);
274         while (refs->enumerate(refs, &ref))
275         {
276                 settings_reference_add(section, ref, FALSE);
277                 array_remove_at(references, refs);
278         }
279         refs->destroy(refs);
280 }
281
282 /**
283  * Parse the given file and add all sections and key/value pairs to the
284  * given section.
285  */
286 bool settings_parser_parse_file(section_t *root, char *name)
287 {
288         parser_helper_t *helper;
289         array_t *sections = NULL;
290         bool success = FALSE;
291
292         array_insert_create(&sections, ARRAY_TAIL, root);
293         helper = parser_helper_create(sections);
294         helper->get_lineno = settings_parser_get_lineno;
295         if (settings_parser_lex_init_extra(helper, &helper->scanner) != 0)
296         {
297                 helper->destroy(helper);
298                 array_destroy(sections);
299                 return FALSE;
300         }
301         helper->file_include(helper, name);
302         if (!settings_parser_open_next_file(helper))
303         {
304                 if (lib->conf && streq(name, lib->conf))
305                 {
306                         DBG2(DBG_CFG, "failed to open config file '%s'", name);
307                 }
308                 else
309                 {
310                         DBG1(DBG_CFG, "failed to open config file '%s'", name);
311                 }
312         }
313         else
314         {
315                 if (getenv("DEBUG_SETTINGS_PARSER"))
316                 {
317                         yydebug = 1;
318                         settings_parser_set_debug(1, helper->scanner);
319                 }
320                 success = yyparse(helper) == 0;
321                 if (!success)
322                 {
323                         DBG1(DBG_CFG, "invalid config file '%s'", name);
324                 }
325         }
326         array_destroy(sections);
327         settings_parser_lex_destroy(helper->scanner);
328         helper->destroy(helper);
329         return success;
330 }
331
332 /**
333  * Parse the given string and add all sections and key/value pairs to the
334  * given section.
335  */
336 bool settings_parser_parse_string(section_t *root, char *settings)
337 {
338         parser_helper_t *helper;
339         array_t *sections = NULL;
340         bool success = FALSE;
341
342         array_insert_create(&sections, ARRAY_TAIL, root);
343         helper = parser_helper_create(sections);
344         helper->get_lineno = settings_parser_get_lineno;
345         if (settings_parser_lex_init_extra(helper, &helper->scanner) != 0)
346         {
347                 helper->destroy(helper);
348                 array_destroy(sections);
349                 return FALSE;
350         }
351         settings_parser_load_string(helper, settings);
352         if (getenv("DEBUG_SETTINGS_PARSER"))
353         {
354                 yydebug = 1;
355                 settings_parser_set_debug(1, helper->scanner);
356         }
357         success = yyparse(helper) == 0;
358         if (!success)
359         {
360                 DBG1(DBG_CFG, "failed to parse settings '%s'", settings);
361         }
362         array_destroy(sections);
363         settings_parser_lex_destroy(helper->scanner);
364         helper->destroy(helper);
365         return success;
366 }