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