settings: Add reference feature
[strongswan.git] / src / libstrongswan / settings / settings_lexer.l
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 #include <utils/parser_helper.h>
18
19 #include "settings_parser.h"
20
21 bool settings_parser_open_next_file(parser_helper_t *ctx);
22
23 static void include_files(parser_helper_t *ctx);
24
25 %}
26 %option debug
27 %option warn
28
29 /* use start conditions stack */
30 %option stack
31
32 /* do not declare unneded functions */
33 %option noinput noyywrap
34
35 /* don't use global variables, and interact properly with bison */
36 %option reentrant bison-bridge
37
38 /* maintain the line number */
39 %option yylineno
40
41 /* don't generate a default rule */
42 %option nodefault
43
44 /* prefix function/variable declarations */
45 %option prefix="settings_parser_"
46 /* don't change the name of the output file otherwise autotools has issues */
47 %option outfile="lex.yy.c"
48
49 /* type of our extra data */
50 %option extra-type="parser_helper_t*"
51
52 /* state used to scan names */
53 %x nam
54 /* state used to scan values */
55 %x val
56 /* state used to scan include file patterns */
57 %x inc
58 /* state used to scan quoted strings */
59 %x str
60
61 /* pattern for section/key names */
62 NAME [^#{}:,="\r\n\t ]
63
64 %%
65
66 [\t ]*#[^\r\n]*                 /* eat comments */
67 [\t\r ]+                                /* eat whitespace */
68 \n|#.*\n                                /* eat newlines and comments at the end of a line */
69
70 "{"                                             |
71 "}"                                             |
72 ","                                             return yytext[0];
73
74 ":"                                             return REFS;
75
76 "="                                             {
77         yy_push_state(val, yyscanner);
78         return yytext[0];
79 }
80
81 "include"[\t ]+/[^=]    {
82         yyextra->string_init(yyextra);
83         yy_push_state(inc, yyscanner);
84 }
85
86 "\""                                    {
87         PARSER_DBG1(yyextra, "unexpected string detected");
88         return STRING_ERROR;
89 }
90
91 {NAME} {
92         yyextra->string_init(yyextra);
93         yyextra->string_add(yyextra, yytext);
94         yy_push_state(nam, yyscanner);
95 }
96
97 <nam>{
98         "::"                            {
99                 yyextra->string_add(yyextra, yytext+1);
100         }
101
102         {NAME}+                         {
103                 yyextra->string_add(yyextra, yytext);
104         }
105
106         <<EOF>>                         |
107         .|[\r\n]                        {
108                 if (*yytext)
109                 {
110                         switch (yytext[0])
111                         {
112                                 case '\n':
113                                         /* put the newline back to fix the line numbers */
114                                         unput('\n');
115                                         yy_set_bol(0);
116                                         break;
117                                 default:
118                                         /* these are parsed outside of this start condition */
119                                         unput(yytext[0]);
120                                         break;
121                         }
122                 }
123                 yy_pop_state(yyscanner);
124                 yylval->s = yyextra->string_get(yyextra);
125                 return NAME;
126         }
127 }
128
129 <val>{
130         \r                                      /* just ignore these */
131         [\t ]+
132         <<EOF>>                         |
133         [#}\n]                          {
134                 if (*yytext)
135                 {
136                         switch (yytext[0])
137                         {
138                                 case '\n':
139                                         /* put the newline back to fix the line numbers */
140                                         unput('\n');
141                                         yy_set_bol(0);
142                                         break;
143                                 case '#':
144                                 case '}':
145                                         /* these are parsed outside of this start condition */
146                                         unput(yytext[0]);
147                                         break;
148                         }
149                 }
150                 yy_pop_state(yyscanner);
151                 return NEWLINE;
152         }
153
154         "\""                            {
155                 yyextra->string_init(yyextra);
156                 yy_push_state(str, yyscanner);
157         }
158
159         /* same as above, but allow more characters */
160         [^#}"\r\n\t ]+          {
161                 yylval->s = strdup(yytext);
162                 return NAME;
163         }
164 }
165
166 <inc>{
167         \r                                      /* just ignore these */
168         /* we allow all characters except #, } and spaces, they can be escaped */
169         <<EOF>>                         |
170         [#}\n\t ]                       {
171                 if (*yytext)
172                 {
173                         switch (yytext[0])
174                         {
175                                 case '\n':
176                                         /* put the newline back to fix the line numbers */
177                                         unput('\n');
178                                         yy_set_bol(0);
179                                         break;
180                                 case '#':
181                                 case '}':
182                                         /* these are parsed outside of this start condition */
183                                         unput(yytext[0]);
184                                         break;
185                         }
186                 }
187                 include_files(yyextra);
188                 yy_pop_state(yyscanner);
189         }
190         "\""                            {       /* string include */
191                 yy_push_state(str, yyscanner);
192         }
193         \\                                      {
194                 yyextra->string_add(yyextra, yytext);
195         }
196         \\["#} ]                        {
197                 yyextra->string_add(yyextra, yytext+1);
198         }
199         [^"\\#}\r\n\t ]+ {
200                 yyextra->string_add(yyextra, yytext);
201         }
202 }
203
204 <str>{
205         \r                                      /* just ignore these */
206         "\""                            |
207         <<EOF>>                         |
208         \\                                      {
209                 if (!streq(yytext, "\""))
210                 {
211                         PARSER_DBG1(yyextra, "unterminated string detected");
212                         return STRING_ERROR;
213                 }
214                 if (yy_top_state(yyscanner) == inc)
215                 {       /* string include */
216                         include_files(yyextra);
217                         yy_pop_state(yyscanner);
218                         yy_pop_state(yyscanner);
219                 }
220                 else
221                 {
222                         yy_pop_state(yyscanner);
223                         yylval->s = yyextra->string_get(yyextra);
224                         return STRING;
225                 }
226         }
227
228         \\n     yyextra->string_add(yyextra, "\n");
229         \\r     yyextra->string_add(yyextra, "\r");
230         \\t     yyextra->string_add(yyextra, "\t");
231         \\\r?\n /* merge lines that end with escaped EOL characters */
232         \\.     yyextra->string_add(yyextra, yytext+1);
233         [^\\\r"]+                       {
234                 yyextra->string_add(yyextra, yytext);
235         }
236 }
237
238 <<EOF>>                                 {
239         settings_parser_pop_buffer_state(yyscanner);
240         if (!settings_parser_open_next_file(yyextra) && !YY_CURRENT_BUFFER)
241         {
242                 yyterminate();
243         }
244 }
245
246 %%
247
248 /**
249  * Open the next file, if any is queued and readable, otherwise returns FALSE.
250  */
251 bool settings_parser_open_next_file(parser_helper_t *ctx)
252 {
253         FILE *file;
254
255         file = ctx->file_next(ctx);
256         if (!file)
257         {
258                 return FALSE;
259         }
260
261         settings_parser_set_in(file, ctx->scanner);
262         settings_parser_push_buffer_state(
263                         settings_parser__create_buffer(file, YY_BUF_SIZE,
264                                                                                    ctx->scanner), ctx->scanner);
265         return TRUE;
266 }
267
268 /**
269  * Assumes that the file pattern to include is currently stored as string on
270  * the helper object.
271  */
272 static void include_files(parser_helper_t *ctx)
273 {
274         char *pattern = ctx->string_get(ctx);
275
276         ctx->file_include(ctx, pattern);
277         free(pattern);
278
279         settings_parser_open_next_file(ctx);
280 }
281
282 /**
283  * Load the given string to be parsed next
284  */
285 void settings_parser_load_string(parser_helper_t *ctx, const char *content)
286 {
287         settings_parser__scan_string(content, ctx->scanner);
288 }