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