starter: Add starter group and fix formatting of conf_parser_section_t enum
[strongswan.git] / src / starter / parser / parser.y
1 %{
2 /*
3  * Copyright (C) 2013-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 <utils/parser_helper.h>
21 #include <settings/settings_types.h>
22 #include <parser/conf_parser.h>
23
24 #include "parser.h"
25
26 #define YYDEBUG 1
27
28 /**
29  * Defined by the lexer
30  */
31 int conf_parser_lex(YYSTYPE *lvalp, void *scanner);
32 int conf_parser_lex_init_extra(parser_helper_t *extra, void *scanner);
33 int conf_parser_lex_destroy(void *scanner);
34 int conf_parser_set_in(FILE *in, void *scanner);
35 void conf_parser_set_debug(int debug, void *scanner);
36 char *conf_parser_get_text(void *scanner);
37 int conf_parser_get_leng(void *scanner);
38 int conf_parser_get_lineno(void *scanner);
39 /* Custom functions in lexer */
40 bool conf_parser_open_next_file(parser_helper_t *ctx);
41
42 /**
43  * Forward declaration
44  */
45 static void conf_parser_error(parser_helper_t *ctx, const char *s);
46
47 /**
48  * Make sure to call lexer with the proper context
49  */
50 #undef yylex
51 static int yylex(YYSTYPE *lvalp, parser_helper_t *ctx)
52 {
53         return conf_parser_lex(lvalp, ctx->scanner);
54 }
55
56 %}
57 %debug
58
59 /* generate verbose error messages */
60 %error-verbose
61 /* generate a reentrant parser */
62 %define api.pure
63 /* prefix function/variable declarations */
64 %name-prefix "conf_parser_"
65
66 /* interact properly with the reentrant lexer */
67 %lex-param {parser_helper_t *ctx}
68 %parse-param {parser_helper_t *ctx}
69
70 /* types for terminal symbols... */
71 %union {
72         char *s;
73         conf_parser_section_t t;
74 }
75 %token <s> STRING
76 %token EQ SPACES NEWLINE CONFIG_SETUP CONN CA
77
78 /* ...and other symbols */
79 %type <t> section_type
80 %type <s> section_name value
81
82 /* make the equal sign left associative */
83 %left EQ
84
85 /* properly destroy STRING tokens, which are strdup()ed, on errors */
86 %destructor { free($$); } STRING section_name value
87
88 /* there are two shift/reduce conflicts because we allow empty lines (and lines
89  * with spaces) within settings and anywhere else (i.e. in the beginning) */
90 //%expect 2
91
92 %%
93
94 /**
95  * ipsec.conf grammar rules
96  */
97 statements:
98         /* empty */
99         | statements NEWLINE
100         | statements statement
101         ;
102
103 statement:
104         section
105         | SPACES setting
106         ;
107
108 section:
109         section_type section_name
110         {
111                 if ($1 != CONF_PARSER_CONFIG_SETUP && (!$2 || !strlen($2)))
112                 {
113                         PARSER_DBG1(ctx, "section name missing");
114                         free($2);
115                         YYERROR;
116                 }
117                 conf_parser_t *parser = (conf_parser_t*)ctx->context;
118                 parser->add_section(parser, $1, $2);
119         }
120         ;
121
122 section_type:
123         CONFIG_SETUP
124         {
125                 $$ = CONF_PARSER_CONFIG_SETUP;
126         }
127         |
128         CONN
129         {
130                 $$ = CONF_PARSER_CONN;
131         }
132         |
133         CA
134         {
135                 $$ = CONF_PARSER_CA;
136         }
137         ;
138
139 section_name:
140         /* empty */
141         {
142                 $$ = NULL;
143         }
144         | STRING
145         {
146                 $$ = $1;
147         }
148         ;
149
150 setting:
151         /* empty */
152         |
153         STRING EQ value
154         {
155                 if (!strlen($1))
156                 {
157                         PARSER_DBG1(ctx, "setting name can't be empty");
158                         free($1);
159                         free($3);
160                         YYERROR;
161                 }
162                 conf_parser_t *parser = (conf_parser_t*)ctx->context;
163                 parser->add_setting(parser, $1, $value);
164         }
165         |
166         STRING EQ
167         {
168                 if (!strlen($1))
169                 {
170                         PARSER_DBG1(ctx, "setting name can't be empty");
171                         free($1);
172                         YYERROR;
173                 }
174                 conf_parser_t *parser = (conf_parser_t*)ctx->context;
175                 parser->add_setting(parser, $1, NULL);
176         }
177         |
178         STRING
179         {
180                 PARSER_DBG1(ctx, "missing value for setting '%s'", $1);
181                 free($1);
182                 YYERROR;
183         }
184         ;
185
186 value:
187         STRING
188         | value STRING
189         {       /* just put a single space between them, use strings for more */
190                 if (asprintf(&$$, "%s %s", $1, $2) < 0)
191                 {
192                         free($1);
193                         free($2);
194                         YYERROR;
195                 }
196                 free($1);
197                 free($2);
198         }
199         ;
200
201 %%
202
203 /**
204  * Referenced by the generated parser
205  */
206 static void conf_parser_error(parser_helper_t *ctx, const char *s)
207 {
208         char *text = conf_parser_get_text(ctx->scanner);
209         int len = conf_parser_get_leng(ctx->scanner);
210
211         if (len && text[len-1] == '\n')
212         {       /* cut off newline at the end to avoid muti-line log messages */
213                 len--;
214         }
215         PARSER_DBG1(ctx, "%s [%.*s]", s, (int)len, text);
216 }
217
218 /**
219  * Parse the given file
220  */
221 bool conf_parser_parse_file(conf_parser_t *this, char *name)
222 {
223         parser_helper_t *helper;
224         bool success = FALSE;
225
226         helper = parser_helper_create(this);
227         helper->get_lineno = conf_parser_get_lineno;
228         if (conf_parser_lex_init_extra(helper, &helper->scanner) != 0)
229         {
230                 helper->destroy(helper);
231                 return FALSE;
232         }
233         helper->file_include(helper, name);
234         if (!conf_parser_open_next_file(helper))
235         {
236                 DBG1(DBG_CFG, "failed to open config file '%s'", name);
237         }
238         else
239         {
240                 if (getenv("DEBUG_CONF_PARSER"))
241                 {
242                         yydebug = 1;
243                         conf_parser_set_debug(1, helper->scanner);
244                 }
245                 success = yyparse(helper) == 0;
246                 if (!success)
247                 {
248                         DBG1(DBG_CFG, "invalid config file '%s'", name);
249                 }
250         }
251         conf_parser_lex_destroy(helper->scanner);
252         helper->destroy(helper);
253         return success;
254 }