1 /* grecs - Gray's Extensible Configuration System       -*- c -*- */
2 %option noinput
3 %top {
4 #ifdef HAVE_CONFIG_H
5 # include <config.h>
6 #endif
7 }
8 %{
9 /* grecs - Gray's Extensible Configuration System
10    Copyright (C) 2007-2016 Sergey Poznyakoff
11 
12    Grecs is free software; you can redistribute it and/or modify it
13    under the terms of the GNU General Public License as published by the
14    Free Software Foundation; either version 3 of the License, or (at your
15    option) any later version.
16 
17    Grecs is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21 
22    You should have received a copy of the GNU General Public License along
23    with Grecs. If not, see <http://www.gnu.org/licenses/>. */
24 
25 #include <grecs.h>
26 #include <dhcpd-gram.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <ctype.h>
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <sys/stat.h>
33 
34 #include <wordsplit.h>
35 
36 #define YY_USER_ACTION do {						\
37 		if (YYSTATE == 0) {					\
38 			yylloc.beg = grecs_current_locus_point;		\
39 			yylloc.beg.col++;				\
40 		}							\
41   		grecs_current_locus_point.col += yyleng;		\
42  		yylloc.end = grecs_current_locus_point;			\
43    	} while (0);
44 
45 #define ISWS(c) ((c)==' '||(c)=='\t')
46 %}
47 
48 %x COMMENT STR BOOL EXPR
49 
50 OWS [ \t\f]*
51 WS [ \t\f][ \t\f]*
52 ID [a-zA-Z_][a-zA-Z_0-9-]*
53 P [1-9][0-9]*
54 
55 %%
56          /* Line directive */
57 ^[ \t]*#[ \t]*{P}[ \t]+\".*\".*\n { grecs_parse_line_directive_cpp(yytext,
58 						    &yylloc,
59 						    &grecs_current_locus_point,
60 						    NULL); }
61 ^[ \t]*#[ \t]*line[ \t].*\n       { grecs_parse_line_directive(yytext,
62 						    &yylloc,
63 						    &grecs_current_locus_point,
64 						    NULL); }
65          /* End-of-line comments */
66 #.*\n     { grecs_locus_point_advance_line(grecs_current_locus_point); }
67 #.*     /* end-of-file comment */;
68 "//".*\n  { grecs_locus_point_advance_line(grecs_current_locus_point); }
69 "//".*    /* end-of-file comment */;
70 "if"      return DHCPD_IF;
71 "elsif"   return DHCPD_ELSIF;
72 "else"    return DHCPD_ELSE;
73 
74 <BOOL>[^\{\n]*\n {
75 	  char *p;
76 	  size_t len;
77 	  for (p = yytext, len = yyleng - 1; ISWS(*p) && len > 0; p++, len--)
78                   ;
79 	  if (len) {
80 		  grecs_line_add(p, len);
81 		  grecs_line_add(" ", 1);
82 	  }
83 	  grecs_locus_point_advance_line(grecs_current_locus_point);
84 }
85 <BOOL>[^\{\n]*\{ {
86 	  char *p;
87 	  size_t len;
88 
89 	  unput('{');
90 	  for (p = yytext, len = yyleng - 1; ISWS(*p) && len > 0; p++, len--)
91                   ;
92 	  for (; len > 0 && ISWS(p[len-1]); len--)
93 	      ;
94 	  grecs_line_add(p, len);
95           BEGIN(INITIAL);
96           yylval.string = grecs_line_finish();
97           return DHCPD_EXPR;
98 }
99 
100 <EXPR>[^;\n]*\n {
101 	  char *p;
102 	  size_t len;
103 	  for (p = yytext, len = yyleng - 1; ISWS(*p) && len > 0; p++, len--)
104                   ;
105 	  if (len) {
106 		  grecs_line_add(p, len);
107 		  grecs_line_add(" ", 1);
108 	  }
109 	  grecs_locus_point_advance_line(grecs_current_locus_point);
110 }
111 <EXPR>[^;\n]*; {
112 	  char *p;
113 	  size_t len;
114 
115 	  unput(';');
116 	  for (p = yytext, len = yyleng - 1; ISWS(*p) && len > 0; p++, len--)
117                   ;
118 	  for (; len > 0 && ISWS(p[len-1]); len--)
119 	      ;
120 	  grecs_line_add(p, len);
121           BEGIN(INITIAL);
122           yylval.string = grecs_line_finish();
123           return DHCPD_EXPR;
124 }
125         /* Identifiers */
126 {ID}      { grecs_line_begin();
127 	    grecs_line_add(yytext, yyleng);
128 	    yylval.string = grecs_line_finish();
129 	    return DHCPD_IDENT;
130           }
131          /* Strings */
132 [a-zA-Z0-9_\.\*/:@-]([a-zA-Z0-9_\./:@-][a-zA-Z0-9_\.\*/:@-]*)? {
133 	                   grecs_line_begin();
134 	                   grecs_line_add(yytext, yyleng);
135                            yylval.string = grecs_line_finish();
136                            return DHCPD_STRING; }
137          /* Quoted strings */
138 \"[^\\"\n]*\"         { grecs_line_begin();
139                         grecs_line_add(yytext + 1, yyleng - 2);
140                         yylval.string = grecs_line_finish();
141                         return DHCPD_STRING; }
142 \"[^\\"\n]*\\. |
143 \"[^\\"\n]*\\\n        { BEGIN(STR);
144                          grecs_line_begin();
145 		         grecs_line_acc_grow_unescape_last(yytext + 1,
146                                                            yyleng - 1,
147                                                            &yylloc); }
148 \"[^\\"\n]*\n          { BEGIN(STR);
149                          grecs_line_begin();
150 		         grecs_line_acc_grow(yytext + 1, yyleng - 1); }
151 <STR>[^\\"\n]*\\. |
152 <STR>\"[^\\"\n]*\\\n  { grecs_line_acc_grow_unescape_last(yytext, yyleng,
153                                                           &yylloc); }
154 <STR>[^\\"\n]*\n |
155 <STR>\"[^\\"\n]*\n    { grecs_line_acc_grow(yytext, yyleng); }
156 <STR>[^\\"\n]*\"      { BEGIN(INITIAL);
157                         if (yyleng > 1)
158 				grecs_line_add(yytext, yyleng - 1);
159                         yylval.string = grecs_line_finish();
160 		        return DHCPD_STRING; }
161 {WS}     ;
162          /* Other tokens */
163 \n       { grecs_locus_point_advance_line(grecs_current_locus_point); }
164 [,;{}()!=] return yytext[0];
165 .        { if (isascii(yytext[0]) && isprint(yytext[0]))
166 		grecs_error(&yylloc, 0,
167 			     _("stray character %c"), yytext[0]);
168            else
169                 grecs_error(&yylloc, 0, _("stray character \\%03o"),
170 		   	    (unsigned char) yytext[0]); }
171 %%
172 
173 void
174 grecs_dhcpd_begin_bool(void)
175 {
176         BEGIN(BOOL);
177 }
178 
179 void
180 grecs_dhcpd_begin_expr(void)
181 {
182         BEGIN(EXPR);
183 }
184 
185 struct dhcpd_input_context {
186 	ino_t i_node;
187 	dev_t i_dev;
188         struct grecs_locus_point point;
189 	grecs_locus_t locus;		/* Current input location */
190 	YY_BUFFER_STATE state;
191 	FILE *input;
192 };
193 
194 static struct grecs_list *input_stack;
195 static ino_t i_node;
196 static dev_t i_dev;
197 
198 static void
199 free_context(void *ptr)
200 {
201 	free(ptr);
202 }
203 
204 static int
205 cmp_context(const void *a, const void *b)
206 {
207 	struct dhcpd_input_context const *ac = a;
208 	struct dhcpd_input_context const *bc = b;
209 
210 	return !(ac->i_node == bc->i_node && ac->i_dev == bc->i_dev);
211 }
212 
213 static int
214 _push_context(const char *name, ino_t i_node, dev_t i_dev, grecs_locus_t *loc)
215 {
216 	struct dhcpd_input_context ctx, *pctx;
217 
218 	if (!input_stack) {
219 		input_stack = grecs_list_create();
220 		input_stack->free_entry = free_context;
221 		input_stack->cmp = cmp_context;
222 	} else {
223 		ctx.i_dev = i_dev;
224 		ctx.i_node = i_node;
225 		pctx = grecs_list_locate(input_stack, &ctx);
226 		if (pctx) {
227 			grecs_error(&yylloc, 0,
228 				    _("%s has already been included"), name);
229 			grecs_error(&pctx->locus, 0,
230 			    _("this is where the previous inclusion occurred"));
231 			return 1;
232 		}
233 
234 		pctx = grecs_malloc(sizeof(*pctx));
235 		pctx->i_node = i_node;
236 		pctx->i_dev = i_dev;
237                 if (loc)
238                         pctx->locus = *loc;
239                 else
240                         memset(&pctx->locus, 0, sizeof(pctx->locus)); /* FIXME */
241 		pctx->point = grecs_current_locus_point;
242 		pctx->state = YY_CURRENT_BUFFER;
243 		pctx->input = yyin;
244 		grecs_list_push(input_stack, pctx);
245 	}
246 	return 0;
247 }
248 
249 static int
250 _pop_context()
251 {
252 	struct dhcpd_input_context *pctx;
253 
254 	if (!yyin)
255 		return 1;
256         if (grecs_preprocessor)
257                 pclose(yyin);
258         else
259 	        fclose(yyin);
260 	pctx = grecs_list_pop(input_stack);
261 	if (!pctx) {
262 		yyin = NULL;
263 		return 1;
264 	}
265 	i_node = pctx->i_node;
266 	i_dev = pctx->i_dev;
267 	grecs_current_locus_point = pctx->point;
268 	yyin = pctx->input;
269 	yy_delete_buffer(YY_CURRENT_BUFFER);
270 	yy_switch_to_buffer(pctx->state);
271 	grecs_free(pctx);
272 	return 0;
273 }
274 
275 int
276 yywrap()
277 {
278 	return _pop_context();
279 }
280 
281 int
282 grecs_dhcpd_new_source(const char *name, grecs_locus_t *loc)
283 {
284 	struct stat st;
285 	FILE *fp;
286 
287 	if (access(name, F_OK)) {
288 		int ec = errno;
289 		char *tmp = grecs_find_include_file(name, 0);
290 		if (!tmp) {
291 			grecs_error(loc, ec, _("cannot open `%s'"), name);
292 			return 1;
293 		}
294 		name = grecs_install_text(tmp);
295 		free(tmp);
296 	}
297 
298 	fp = fopen(name, "r");
299 	if (!fp) {
300 		grecs_error(loc, errno, _("cannot open `%s'"), name);
301 		return 1;
302  	}
303 	if (fstat(fileno(fp), &st)) {
304 		grecs_error(loc, errno, _("can't state %s"), name);
305 		fclose(fp);
306 		return 1;
307 	}
308         if (grecs_preprocessor) {
309                 char *cmd = NULL;
310                 size_t size = 0;
311 
312 		fclose(fp);
313                 if (grecs_asprintf(&cmd, &size, "%s \"%s\"",
314                                    grecs_preprocessor, name))
315                         grecs_alloc_die();
316 
317                 fp = popen(cmd, "r");
318                 if (!fp) {
319 		        grecs_error(loc, errno, _("cannot open `%s'"), cmd);
320                         grecs_free(cmd);
321                         return 1;
322                 }
323                 grecs_free(cmd);
324         }
325 
326 	if (_push_context(name, st.st_ino, st.st_dev, loc)) {
327 		return 1;
328 	}
329 	i_node = st.st_ino;
330 	i_dev = st.st_dev;
331 	yyin = fp;
332 	yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
333 	grecs_current_locus_point.file = grecs_install_text(name);
334 	grecs_current_locus_point.line = 1;
335 	grecs_current_locus_point.col = 0;
336 	return 0;
337 }
338 
339 void
340 grecs_dhcpd_close_sources()
341 {
342 	while (!_pop_context())
343 		;
344 	grecs_list_free(input_stack);
345 	input_stack = NULL;
346 }
347