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