1 %{
2 /* grecs - Gray's Extensible Configuration System
3    Copyright (C) 2007-2016 Sergey Poznyakoff
4 
5    Grecs is free software; you can redistribute it and/or modify it
6    under the terms of the GNU General Public License as published by the
7    Free Software Foundation; either version 3 of the License, or (at your
8    option) any later version.
9 
10    Grecs is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License along
16    with Grecs. If not, see <http://www.gnu.org/licenses/>. */
17 
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 #include <grecs.h>
22 #include <dhcpd-gram.h>
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <string.h>
26 #include <errno.h>
27 
28 int yylex(void);
29 int yyerror(char const *s);
30 
31 static struct grecs_node *parse_tree;
32 extern int yy_flex_debug;
33 extern int grecs_dhcpd_new_source(const char *name, grecs_locus_t *loc);
34 extern void grecs_dhcpd_close_sources(void);
35 
36 extern void grecs_dhcpd_begin_bool(void);
37 extern void grecs_dhcpd_begin_expr(void);
38 
39 /* NOTE: STRING must be allocated */
40 static grecs_value_t *
make_string_value(char * string)41 make_string_value(char *string)
42 {
43 	grecs_value_t *val;
44 	val = grecs_malloc(sizeof(val[0]));
45 	val->type = GRECS_TYPE_STRING;
46 	val->v.string = string;
47 	return val;
48 }
49 
50 %}
51 
52 %error-verbose
53 %locations
54 
55 %union {
56 	char *string;
57 	grecs_value_t svalue, *pvalue;
58 	struct grecs_list *list;
59 	struct grecs_node *node;
60 	grecs_locus_t locus;
61 	struct { struct grecs_node *head, *tail; } node_list;
62 }
63 
64 %token <string> DHCPD_IF DHCPD_ELSIF DHCPD_EXPR
65 %token DHCPD_ELSE
66 %token <string> DHCPD_STRING DHCPD_IDENT
67 %type <string> string
68 %type <svalue> value
69 %type <pvalue> vallist tag
70 %type <list> vlist strlist
71 %type <node> stmt simple block maybe_stmtlist
72 %type <node> cond elsecond opt_elsecond elsifcond
73 %type <node_list> stmtlist elsifchain opt_elsifchain
74 
75 %%
76 
77 input   : maybe_stmtlist
78           {
79 		  parse_tree = grecs_node_create(grecs_node_root, &@1);
80 		  parse_tree->v.texttab = grecs_text_table();
81 		  grecs_node_bind(parse_tree, $1, 1);
82 	  }
83         ;
84 
85 maybe_stmtlist:
86           /* empty */
87           {
88 		  $$ = NULL;
89 	  }
90         | stmtlist
91 	  {
92 		  $$ = $1.head;
93 	  }
94         ;
95 
96 stmtlist: stmt
97           {
98 		  $$.head = $$.tail = $1;
99 	  }
100         | stmtlist stmt
101 	  {
102 		  if ($2) {
103 			  if (!$1.head)
104 				  $1.head = $1.tail = $2;
105 			  else
106 				  grecs_node_bind($1.tail, $2, 0);
107 		  }
108 		  $$ = $1;
109 	  }
110         ;
111 
112 stmt    : simple
113         | block
114         | cond
115         ;
116 
117 simple  : DHCPD_IDENT vallist ';'
118           {
119 		  if (strcmp($1, "include") == 0 &&
120 		      $2->type == GRECS_TYPE_STRING) {
121 			  grecs_dhcpd_new_source($2->v.string, &@1);
122 			  $$ = NULL;
123 		  } else {
124 			  $$ = grecs_node_create_points(grecs_node_stmt,
125 							@1.beg, @2.end);
126 			  $$->ident = $1;
127 			  $$->idloc = @1;
128 			  $$->v.value = $2;
129 		  }
130 	  }
131         | DHCPD_IDENT '=' { grecs_dhcpd_begin_expr(); } DHCPD_EXPR ';'
132 	  {
133 		  $$ = grecs_node_create_points(grecs_node_stmt,
134 						@1.beg, @5.end);
135 		  $$->ident = $1;
136 		  $$->idloc = @1;
137 		  $$->v.value = make_string_value($4);
138 	  }
139         | string ';'
140 	  {
141 		  $$ = grecs_node_create(grecs_node_stmt, &@1);
142 		  $$->ident = $1;
143 		  $$->idloc = @1;
144 		  $$->v.value = NULL;
145 	  }
146         ;
147 
148 block   : DHCPD_IDENT tag '{' maybe_stmtlist '}' opt_semi
149           {
150 		  $$ = grecs_node_create_points(grecs_node_block,
151 						@1.beg, @5.end);
152 		  $$->ident = $1;
153 		  $$->idloc = @1;
154 		  $$->v.value = $2;
155 		  grecs_node_bind($$, $4, 1);
156 	  }
157         ;
158 
159 opt_semi: /* empty */
160         | ';'
161         ;
162 
163 tag     : /* empty */
164           {
165 		  $$ = NULL;
166 	  }
167         | vallist
168         ;
169 
170 vallist : vlist
171           {
172 		  size_t n;
173 
174 		  if ((n = grecs_list_size($1)) == 1) {
175 			  $$ = grecs_list_index($1, 0);
176 		  } else {
177 			  size_t i;
178 			  struct grecs_list_entry *ep;
179 
180 			  $$ = grecs_malloc(sizeof($$[0]));
181 			  $$->type = GRECS_TYPE_ARRAY;
182 			  $$->locus = @1;
183 			  $$->v.arg.c = n;
184 			  $$->v.arg.v = grecs_calloc(n,
185 						     sizeof($$->v.arg.v[0]));
186 			  for (i = 0, ep = $1->head; ep; i++, ep = ep->next)
187 				  $$->v.arg.v[i] = ep->data;
188 		  }
189 		  $1->free_entry = NULL;
190 		  grecs_list_free($1);
191 	  }
192 	;
193 
194 vlist   : value
195           {
196 		  $$ = grecs_value_list_create();
197 		  grecs_list_append($$, grecs_value_ptr_from_static(&$1));
198 	  }
199         | vlist value
200           {
201 		  grecs_list_append($1, grecs_value_ptr_from_static(&$2));
202 	  }
203         ;
204 
205 value   : string
206           {
207 		  $$.type = GRECS_TYPE_STRING;
208 		  $$.locus = @1;
209 		  $$.v.string = $1;
210 	  }
211         | strlist
212 	  {
213 		  $$.type = GRECS_TYPE_LIST;
214 		  $$.locus = @1;
215 		  $$.v.list = $1;
216 	  }
217         ;
218 
219 string  : DHCPD_STRING
220         | DHCPD_IDENT
221         ;
222 
223 strlist : DHCPD_STRING ',' DHCPD_STRING
224           {
225 		  grecs_value_t val;
226 
227 		  $$ = grecs_value_list_create();
228 
229 		  val.type = GRECS_TYPE_STRING;
230 		  val.locus = @1;
231 		  val.v.string = $1;
232 		  grecs_list_append($$, grecs_value_ptr_from_static(&val));
233 
234 		  val.type = GRECS_TYPE_STRING;
235 		  val.locus = @3;
236 		  val.v.string = $3;
237 		  grecs_list_append($$, grecs_value_ptr_from_static(&val));
238 	  }
239         | strlist ',' DHCPD_STRING
240           {
241 		  grecs_value_t val;
242 
243 		  val.type = GRECS_TYPE_STRING;
244 		  val.locus = @3;
245 		  val.v.string = $3;
246 		  grecs_list_append($1, grecs_value_ptr_from_static(&val));
247 	  }
248         ;
249 
250 cond    : if DHCPD_EXPR '{' maybe_stmtlist '}' opt_elsifchain opt_elsecond
251           {
252 		  $$ = grecs_node_create_points(grecs_node_block,
253 						@1.beg, @7.end);
254 
255 		  grecs_line_begin();
256 		  grecs_line_add("if", 2);
257 
258 		  $$->ident = grecs_line_finish();
259 		  $$->idloc = @1;
260 
261 		  $$->v.value = make_string_value ($2);
262 		  grecs_node_bind($$, $4, 1);
263 
264 		  if ($6.head) {
265 			  grecs_node_bind($6.tail, $7, 0);
266 			  grecs_node_bind($$, $6.head, 0);
267 		  }
268 	  }
269         ;
270 
271 if      : DHCPD_IF
272           {
273 		  grecs_dhcpd_begin_bool();
274 	  }
275         ;
276 
277 elsif   : DHCPD_ELSIF
278           {
279 		  grecs_dhcpd_begin_bool();
280 	  }
281         ;
282 
283 opt_elsifchain: /* empty */
284           {
285 		  $$.head = $$.tail = NULL;
286 	  }
287         | elsifchain
288 	;
289 
290 elsifchain: elsifcond
291           {
292 		  $$.head = $$.tail = $1;
293 	  }
294         | elsifchain elsifcond
295 	  {
296 		  if ($2) {
297 			  if (!$1.head)
298 				  $1.head = $1.tail = $2;
299 			  else
300 				  grecs_node_bind($1.tail, $2, 0);
301 		  }
302 		  $$ = $1;
303 	  }
304         ;
305 
306 elsifcond: elsif DHCPD_EXPR '{' maybe_stmtlist '}'
307           {
308 		  $$ = grecs_node_create_points(grecs_node_block,
309 						@1.beg, @5.end);
310 
311 		  grecs_line_begin();
312 		  grecs_line_add("elsif", 5);
313 
314 		  $$->ident = grecs_line_finish();
315 		  $$->idloc = @1;
316 		  $$->v.value = make_string_value ($2);
317 		  grecs_node_bind($$, $4, 1);
318 	  }
319         ;
320 
321 opt_elsecond: /* empty */
322           {
323 		  $$ = NULL;
324 	  }
325         | elsecond
326 	;
327 
328 elsecond: DHCPD_ELSE '{' maybe_stmtlist '}'
329           {
330 		  $$ = grecs_node_create_points(grecs_node_block,
331 						@1.beg, @4.end);
332 
333 		  grecs_line_begin();
334 		  grecs_line_add("else", 4);
335 
336 		  $$->ident = grecs_line_finish();
337 		  $$->idloc = @1;
338 		  $$->v.value = NULL;
339 		  grecs_node_bind($$, $3, 1);
340 	  }
341         ;
342 
343 %%
344 
345 int
346 yyerror(char const *s)
347 {
348 	grecs_error(&yylloc, 0, "%s", s);
349 	return 0;
350 }
351 
352 struct grecs_node *
353 grecs_dhcpd_parser(const char *name, int traceflags)
354 {
355 	int rc;
356 
357 	if (grecs_dhcpd_new_source(name, NULL))
358 		return NULL;
359 	yy_flex_debug = traceflags & GRECS_TRACE_LEX;
360 	yydebug = traceflags & GRECS_TRACE_GRAM;
361 	parse_tree = NULL;
362 	grecs_line_acc_create();
363 	rc = yyparse();
364 	grecs_dhcpd_close_sources();
365 	if (grecs_error_count)
366 		rc = 1;
367 	grecs_line_acc_free();
368 	if (rc) {
369 		grecs_tree_free(parse_tree);
370 		parse_tree = NULL;
371 	}
372 	return parse_tree;
373 }
374 
375