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