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