1 /* SPDX-License-Identifier: GPL-2.0 */ 2 /* 3 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> 4 */ 5 %option nostdinit noyywrap never-interactive full ecs 6 %option 8bit nodefault yylineno 7 %x ASSIGN_VAL HELP STRING 8 %{ 9 10 #include <assert.h> 11 #include <limits.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 16 #include <xalloc.h> 17 #include "lkc.h" 18 #include "preprocess.h" 19 20 #include "parser.tab.h" 21 22 #define YY_DECL static int yylex1(void) 23 24 #define START_STRSIZE 16 25 26 /* The Kconfig file currently being parsed. */ 27 const char *cur_filename; 28 29 /* 30 * The line number of the current statement. This does not match yylineno. 31 * yylineno is used by the lexer, while cur_lineno is used by the parser. 32 */ 33 int cur_lineno; 34 35 static int prev_prev_token = T_EOL; 36 static int prev_token = T_EOL; 37 static char *text; 38 static int text_size, text_asize; 39 40 struct buffer { 41 struct buffer *parent; 42 YY_BUFFER_STATE state; 43 int yylineno; 44 const char *filename; 45 int source_lineno; 46 }; 47 48 static struct buffer *current_buf; 49 50 static int last_ts, first_ts; 51 52 static char *expand_token(const char *in, size_t n); 53 static void append_expanded_string(const char *in); 54 static void zconf_endhelp(void); 55 static void zconf_endfile(void); 56 57 static void new_string(void) 58 { 59 text = xmalloc(START_STRSIZE); 60 text_asize = START_STRSIZE; 61 text_size = 0; 62 *text = 0; 63 } 64 65 static void append_string(const char *str, int size) 66 { 67 int new_size = text_size + size + 1; 68 if (new_size > text_asize) { 69 new_size += START_STRSIZE - 1; 70 new_size &= -START_STRSIZE; 71 text = xrealloc(text, new_size); 72 text_asize = new_size; 73 } 74 memcpy(text + text_size, str, size); 75 text_size += size; 76 text[text_size] = 0; 77 } 78 79 static void alloc_string(const char *str, int size) 80 { 81 text = xmalloc(size + 1); 82 memcpy(text, str, size); 83 text[size] = 0; 84 } 85 86 static void warn_ignored_character(char chr) 87 { 88 fprintf(stderr, 89 "%s:%d:warning: ignoring unsupported character '%c'\n", 90 cur_filename, yylineno, chr); 91 } 92 %} 93 94 n [A-Za-z0-9_-] 95 96 %% 97 char open_quote = 0; 98 99 #.* /* ignore comment */ 100 [ \t]* /* whitespaces */ 101 \\\n /* escaped new line */ 102 \n return T_EOL; 103 "bool" return T_BOOL; 104 "choice" return T_CHOICE; 105 "comment" return T_COMMENT; 106 "config" return T_CONFIG; 107 "def_bool" return T_DEF_BOOL; 108 "def_tristate" return T_DEF_TRISTATE; 109 "default" return T_DEFAULT; 110 "depends" return T_DEPENDS; 111 "endchoice" return T_ENDCHOICE; 112 "endif" return T_ENDIF; 113 "endmenu" return T_ENDMENU; 114 "help" return T_HELP; 115 "hex" return T_HEX; 116 "if" return T_IF; 117 "imply" return T_IMPLY; 118 "int" return T_INT; 119 "mainmenu" return T_MAINMENU; 120 "menu" return T_MENU; 121 "menuconfig" return T_MENUCONFIG; 122 "modules" return T_MODULES; 123 "on" return T_ON; 124 "prompt" return T_PROMPT; 125 "range" return T_RANGE; 126 "select" return T_SELECT; 127 "source" return T_SOURCE; 128 "string" return T_STRING; 129 "transitional" return T_TRANSITIONAL; 130 "tristate" return T_TRISTATE; 131 "visible" return T_VISIBLE; 132 "||" return T_OR; 133 "&&" return T_AND; 134 "=" return T_EQUAL; 135 "!=" return T_UNEQUAL; 136 "<" return T_LESS; 137 "<=" return T_LESS_EQUAL; 138 ">" return T_GREATER; 139 ">=" return T_GREATER_EQUAL; 140 "!" return T_NOT; 141 "(" return T_OPEN_PAREN; 142 ")" return T_CLOSE_PAREN; 143 ":=" return T_COLON_EQUAL; 144 "+=" return T_PLUS_EQUAL; 145 \"|\' { 146 open_quote = yytext[0]; 147 new_string(); 148 BEGIN(STRING); 149 } 150 {n}+ { 151 alloc_string(yytext, yyleng); 152 yylval.string = text; 153 return T_WORD; 154 } 155 ({n}|$)+ { 156 /* this token includes at least one '$' */ 157 yylval.string = expand_token(yytext, yyleng); 158 if (strlen(yylval.string)) 159 return T_WORD; 160 free(yylval.string); 161 } 162 . warn_ignored_character(*yytext); 163 164 <ASSIGN_VAL>{ 165 [^[:blank:]\n]+.* { 166 alloc_string(yytext, yyleng); 167 yylval.string = text; 168 return T_ASSIGN_VAL; 169 } 170 \n { BEGIN(INITIAL); return T_EOL; } 171 . 172 } 173 174 <STRING>{ 175 "$".* append_expanded_string(yytext); 176 [^$'"\\\n]+ { 177 append_string(yytext, yyleng); 178 } 179 \\.? { 180 append_string(yytext + 1, yyleng - 1); 181 } 182 \'|\" { 183 if (open_quote == yytext[0]) { 184 BEGIN(INITIAL); 185 yylval.string = text; 186 return T_WORD_QUOTE; 187 } else 188 append_string(yytext, 1); 189 } 190 \n { 191 fprintf(stderr, 192 "%s:%d:warning: multi-line strings not supported\n", 193 cur_filename, cur_lineno); 194 unput('\n'); 195 BEGIN(INITIAL); 196 yylval.string = text; 197 return T_WORD_QUOTE; 198 } 199 <<EOF>> { 200 BEGIN(INITIAL); 201 yylval.string = text; 202 return T_WORD_QUOTE; 203 } 204 } 205 206 <HELP>{ 207 [ \t]+ { 208 int ts, i; 209 210 ts = 0; 211 for (i = 0; i < yyleng; i++) { 212 if (yytext[i] == '\t') 213 ts = (ts & ~7) + 8; 214 else 215 ts++; 216 } 217 last_ts = ts; 218 if (first_ts) { 219 if (ts < first_ts) { 220 zconf_endhelp(); 221 return T_HELPTEXT; 222 } 223 ts -= first_ts; 224 while (ts > 8) { 225 append_string(" ", 8); 226 ts -= 8; 227 } 228 append_string(" ", ts); 229 } 230 } 231 [ \t]*\n/[^ \t\n] { 232 zconf_endhelp(); 233 return T_HELPTEXT; 234 } 235 [ \t]*\n { 236 append_string("\n", 1); 237 } 238 [^ \t\n].* { 239 while (yyleng) { 240 if ((yytext[yyleng-1] != ' ') && (yytext[yyleng-1] != '\t')) 241 break; 242 yyleng--; 243 } 244 append_string(yytext, yyleng); 245 if (!first_ts) 246 first_ts = last_ts; 247 } 248 <<EOF>> { 249 zconf_endhelp(); 250 return T_HELPTEXT; 251 } 252 } 253 254 <<EOF>> { 255 BEGIN(INITIAL); 256 257 if (prev_token != T_EOL && prev_token != T_HELPTEXT) 258 fprintf(stderr, "%s:%d:warning: no new line at end of file\n", 259 cur_filename, yylineno); 260 261 if (current_buf) { 262 zconf_endfile(); 263 return T_EOL; 264 } 265 fclose(yyin); 266 yyterminate(); 267 } 268 269 %% 270 271 /* second stage lexer */ 272 int yylex(void) 273 { 274 int token; 275 276 repeat: 277 token = yylex1(); 278 279 if (prev_token == T_EOL || prev_token == T_HELPTEXT) { 280 if (token == T_EOL) 281 /* Do not pass unneeded T_EOL to the parser. */ 282 goto repeat; 283 else 284 /* 285 * For the parser, update lineno at the first token 286 * of each statement. Generally, \n is a statement 287 * terminator in Kconfig, but it is not always true 288 * because \n could be escaped by a backslash. 289 */ 290 cur_lineno = yylineno; 291 } 292 293 if (prev_prev_token == T_EOL && prev_token == T_WORD && 294 (token == T_EQUAL || token == T_COLON_EQUAL || token == T_PLUS_EQUAL)) 295 BEGIN(ASSIGN_VAL); 296 297 prev_prev_token = prev_token; 298 prev_token = token; 299 300 return token; 301 } 302 303 static char *expand_token(const char *in, size_t n) 304 { 305 char *out; 306 int c; 307 char c2; 308 const char *rest, *end; 309 310 new_string(); 311 append_string(in, n); 312 313 /* 314 * get the whole line because we do not know the end of token. 315 * input() returns 0 (not EOF!) when it reachs the end of file. 316 */ 317 while ((c = input()) != 0) { 318 if (c == '\n') { 319 unput(c); 320 break; 321 } 322 c2 = c; 323 append_string(&c2, 1); 324 } 325 326 rest = text; 327 out = expand_one_token(&rest); 328 329 /* push back unused characters to the input stream */ 330 end = rest + strlen(rest); 331 while (end > rest) 332 unput(*--end); 333 334 free(text); 335 336 return out; 337 } 338 339 static void append_expanded_string(const char *str) 340 { 341 const char *end; 342 char *res; 343 344 str++; 345 346 res = expand_dollar(&str); 347 348 /* push back unused characters to the input stream */ 349 end = str + strlen(str); 350 while (end > str) 351 unput(*--end); 352 353 append_string(res, strlen(res)); 354 355 free(res); 356 } 357 358 void zconf_starthelp(void) 359 { 360 new_string(); 361 last_ts = first_ts = 0; 362 BEGIN(HELP); 363 } 364 365 static void zconf_endhelp(void) 366 { 367 yylval.string = text; 368 BEGIN(INITIAL); 369 } 370 371 372 /* 373 * Try to open specified file with following names: 374 * ./name 375 * $(srctree)/name 376 * The latter is used when srctree is separate from objtree 377 * when compiling the kernel. 378 * Return NULL if file is not found. 379 */ 380 FILE *zconf_fopen(const char *name) 381 { 382 char *env, fullname[PATH_MAX+1]; 383 FILE *f; 384 385 f = fopen(name, "r"); 386 if (!f && name != NULL && name[0] != '/') { 387 env = getenv(SRCTREE); 388 if (env) { 389 snprintf(fullname, sizeof(fullname), 390 "%s/%s", env, name); 391 f = fopen(fullname, "r"); 392 } 393 } 394 return f; 395 } 396 397 void zconf_initscan(const char *name) 398 { 399 yyin = zconf_fopen(name); 400 if (!yyin) { 401 fprintf(stderr, "can't find file %s\n", name); 402 exit(1); 403 } 404 405 cur_filename = file_lookup(name); 406 yylineno = 1; 407 } 408 409 void zconf_nextfile(const char *name) 410 { 411 struct buffer *buf = xmalloc(sizeof(*buf)); 412 bool recur_include = false; 413 414 buf->state = YY_CURRENT_BUFFER; 415 buf->yylineno = yylineno; 416 buf->filename = cur_filename; 417 buf->source_lineno = cur_lineno; 418 buf->parent = current_buf; 419 current_buf = buf; 420 yyin = zconf_fopen(name); 421 if (!yyin) { 422 fprintf(stderr, "%s:%d: can't open file \"%s\"\n", 423 cur_filename, cur_lineno, name); 424 exit(1); 425 } 426 yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); 427 428 for (buf = current_buf; buf; buf = buf->parent) { 429 if (!strcmp(buf->filename, name)) 430 recur_include = true; 431 } 432 433 if (recur_include) { 434 fprintf(stderr, 435 "Recursive inclusion detected.\n" 436 "Inclusion path:\n" 437 " current file : %s\n", name); 438 439 for (buf = current_buf; buf; buf = buf->parent) 440 fprintf(stderr, " included from: %s:%d\n", 441 buf->filename, buf->source_lineno); 442 exit(1); 443 } 444 445 yylineno = 1; 446 cur_filename = file_lookup(name); 447 } 448 449 static void zconf_endfile(void) 450 { 451 struct buffer *tmp; 452 453 fclose(yyin); 454 yy_delete_buffer(YY_CURRENT_BUFFER); 455 yy_switch_to_buffer(current_buf->state); 456 yylineno = current_buf->yylineno; 457 cur_filename = current_buf->filename; 458 tmp = current_buf; 459 current_buf = current_buf->parent; 460 free(tmp); 461 } 462