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