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_hex" return T_DEF_HEX; 109 "def_int" return T_DEF_INT; 110 "def_string" return T_DEF_STRING; 111 "def_tristate" return T_DEF_TRISTATE; 112 "default" return T_DEFAULT; 113 "depends" return T_DEPENDS; 114 "endchoice" return T_ENDCHOICE; 115 "endif" return T_ENDIF; 116 "endmenu" return T_ENDMENU; 117 "help" return T_HELP; 118 "hex" return T_HEX; 119 "if" return T_IF; 120 "imply" return T_IMPLY; 121 "int" return T_INT; 122 "mainmenu" return T_MAINMENU; 123 "menu" return T_MENU; 124 "menuconfig" return T_MENUCONFIG; 125 "modules" return T_MODULES; 126 "on" return T_ON; 127 "prompt" return T_PROMPT; 128 "range" return T_RANGE; 129 "select" return T_SELECT; 130 "source" return T_SOURCE; 131 "string" return T_STRING; 132 "transitional" return T_TRANSITIONAL; 133 "tristate" return T_TRISTATE; 134 "visible" return T_VISIBLE; 135 "||" return T_OR; 136 "&&" return T_AND; 137 "=" return T_EQUAL; 138 "!=" return T_UNEQUAL; 139 "<" return T_LESS; 140 "<=" return T_LESS_EQUAL; 141 ">" return T_GREATER; 142 ">=" return T_GREATER_EQUAL; 143 "!" return T_NOT; 144 "(" return T_OPEN_PAREN; 145 ")" return T_CLOSE_PAREN; 146 ":=" return T_COLON_EQUAL; 147 "+=" return T_PLUS_EQUAL; 148 \"|\' { 149 open_quote = yytext[0]; 150 new_string(); 151 BEGIN(STRING); 152 } 153 {n}+ { 154 alloc_string(yytext, yyleng); 155 yylval.string = text; 156 return T_WORD; 157 } 158 ({n}|$)+ { 159 /* this token includes at least one '$' */ 160 yylval.string = expand_token(yytext, yyleng); 161 if (strlen(yylval.string)) 162 return T_WORD; 163 free(yylval.string); 164 } 165 . warn_ignored_character(*yytext); 166 167 <ASSIGN_VAL>{ 168 [^[:blank:]\n]+.* { 169 alloc_string(yytext, yyleng); 170 yylval.string = text; 171 return T_ASSIGN_VAL; 172 } 173 \n { BEGIN(INITIAL); return T_EOL; } 174 . 175 } 176 177 <STRING>{ 178 "$".* append_expanded_string(yytext); 179 [^$'"\\\n]+ { 180 append_string(yytext, yyleng); 181 } 182 \\.? { 183 append_string(yytext + 1, yyleng - 1); 184 } 185 \'|\" { 186 if (open_quote == yytext[0]) { 187 BEGIN(INITIAL); 188 yylval.string = text; 189 return T_WORD_QUOTE; 190 } else 191 append_string(yytext, 1); 192 } 193 \n { 194 fprintf(stderr, 195 "%s:%d:warning: multi-line strings not supported\n", 196 cur_filename, cur_lineno); 197 unput('\n'); 198 BEGIN(INITIAL); 199 yylval.string = text; 200 return T_WORD_QUOTE; 201 } 202 <<EOF>> { 203 BEGIN(INITIAL); 204 yylval.string = text; 205 return T_WORD_QUOTE; 206 } 207 } 208 209 <HELP>{ 210 [ \t]+ { 211 int ts, i; 212 213 ts = 0; 214 for (i = 0; i < yyleng; i++) { 215 if (yytext[i] == '\t') 216 ts = (ts & ~7) + 8; 217 else 218 ts++; 219 } 220 last_ts = ts; 221 if (first_ts) { 222 if (ts < first_ts) { 223 zconf_endhelp(); 224 return T_HELPTEXT; 225 } 226 ts -= first_ts; 227 while (ts > 8) { 228 append_string(" ", 8); 229 ts -= 8; 230 } 231 append_string(" ", ts); 232 } 233 } 234 [ \t]*\n/[^ \t\n] { 235 zconf_endhelp(); 236 return T_HELPTEXT; 237 } 238 [ \t]*\n { 239 append_string("\n", 1); 240 } 241 [^ \t\n].* { 242 while (yyleng) { 243 if ((yytext[yyleng-1] != ' ') && (yytext[yyleng-1] != '\t')) 244 break; 245 yyleng--; 246 } 247 append_string(yytext, yyleng); 248 if (!first_ts) 249 first_ts = last_ts; 250 } 251 <<EOF>> { 252 zconf_endhelp(); 253 return T_HELPTEXT; 254 } 255 } 256 257 <<EOF>> { 258 BEGIN(INITIAL); 259 260 if (prev_token != T_EOL && prev_token != T_HELPTEXT) 261 fprintf(stderr, "%s:%d:warning: no new line at end of file\n", 262 cur_filename, yylineno); 263 264 if (current_buf) { 265 zconf_endfile(); 266 return T_EOL; 267 } 268 fclose(yyin); 269 yyterminate(); 270 } 271 272 %% 273 274 /* second stage lexer */ 275 int yylex(void) 276 { 277 int token; 278 279 repeat: 280 token = yylex1(); 281 282 if (prev_token == T_EOL || prev_token == T_HELPTEXT) { 283 if (token == T_EOL) 284 /* Do not pass unneeded T_EOL to the parser. */ 285 goto repeat; 286 else 287 /* 288 * For the parser, update lineno at the first token 289 * of each statement. Generally, \n is a statement 290 * terminator in Kconfig, but it is not always true 291 * because \n could be escaped by a backslash. 292 */ 293 cur_lineno = yylineno; 294 } 295 296 if (prev_prev_token == T_EOL && prev_token == T_WORD && 297 (token == T_EQUAL || token == T_COLON_EQUAL || token == T_PLUS_EQUAL)) 298 BEGIN(ASSIGN_VAL); 299 300 prev_prev_token = prev_token; 301 prev_token = token; 302 303 return token; 304 } 305 306 static char *expand_token(const char *in, size_t n) 307 { 308 char *out; 309 int c; 310 char c2; 311 const char *rest, *end; 312 313 new_string(); 314 append_string(in, n); 315 316 /* 317 * get the whole line because we do not know the end of token. 318 * input() returns 0 (not EOF!) when it reachs the end of file. 319 */ 320 while ((c = input()) != 0) { 321 if (c == '\n') { 322 unput(c); 323 break; 324 } 325 c2 = c; 326 append_string(&c2, 1); 327 } 328 329 rest = text; 330 out = expand_one_token(&rest); 331 332 /* push back unused characters to the input stream */ 333 end = rest + strlen(rest); 334 while (end > rest) 335 unput(*--end); 336 337 free(text); 338 339 return out; 340 } 341 342 static void append_expanded_string(const char *str) 343 { 344 const char *end; 345 char *res; 346 347 str++; 348 349 res = expand_dollar(&str); 350 351 /* push back unused characters to the input stream */ 352 end = str + strlen(str); 353 while (end > str) 354 unput(*--end); 355 356 append_string(res, strlen(res)); 357 358 free(res); 359 } 360 361 void zconf_starthelp(void) 362 { 363 new_string(); 364 last_ts = first_ts = 0; 365 BEGIN(HELP); 366 } 367 368 static void zconf_endhelp(void) 369 { 370 yylval.string = text; 371 BEGIN(INITIAL); 372 } 373 374 375 /* 376 * Try to open specified file with following names: 377 * ./name 378 * $(srctree)/name 379 * The latter is used when srctree is separate from objtree 380 * when compiling the kernel. 381 * Return NULL if file is not found. 382 */ 383 FILE *zconf_fopen(const char *name) 384 { 385 char *env, fullname[PATH_MAX+1]; 386 FILE *f; 387 388 f = fopen(name, "r"); 389 if (!f && name != NULL && name[0] != '/') { 390 env = getenv(SRCTREE); 391 if (env) { 392 snprintf(fullname, sizeof(fullname), 393 "%s/%s", env, name); 394 f = fopen(fullname, "r"); 395 } 396 } 397 return f; 398 } 399 400 void zconf_initscan(const char *name) 401 { 402 yyin = zconf_fopen(name); 403 if (!yyin) { 404 fprintf(stderr, "can't find file %s\n", name); 405 exit(1); 406 } 407 408 cur_filename = file_lookup(name, NULL, 0); 409 yylineno = 1; 410 } 411 412 void zconf_nextfile(const char *name) 413 { 414 struct buffer *buf = xmalloc(sizeof(*buf)); 415 bool recur_include = false; 416 417 buf->state = YY_CURRENT_BUFFER; 418 buf->yylineno = yylineno; 419 buf->filename = cur_filename; 420 buf->source_lineno = cur_lineno; 421 buf->parent = current_buf; 422 current_buf = buf; 423 yyin = zconf_fopen(name); 424 if (!yyin) { 425 fprintf(stderr, "%s:%d: can't open file \"%s\"\n", 426 cur_filename, cur_lineno, name); 427 exit(1); 428 } 429 yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); 430 431 for (buf = current_buf; buf; buf = buf->parent) { 432 if (!strcmp(buf->filename, name)) 433 recur_include = true; 434 } 435 436 if (recur_include) { 437 fprintf(stderr, 438 "Recursive inclusion detected.\n" 439 "Inclusion path:\n" 440 " current file : %s\n", name); 441 442 for (buf = current_buf; buf; buf = buf->parent) 443 fprintf(stderr, " included from: %s:%d\n", 444 buf->filename, buf->source_lineno); 445 exit(1); 446 } 447 448 yylineno = 1; 449 cur_filename = file_lookup(name, cur_filename, cur_lineno); 450 } 451 452 static void zconf_endfile(void) 453 { 454 struct buffer *tmp; 455 456 fclose(yyin); 457 yy_delete_buffer(YY_CURRENT_BUFFER); 458 yy_switch_to_buffer(current_buf->state); 459 yylineno = current_buf->yylineno; 460 cur_filename = current_buf->filename; 461 tmp = current_buf; 462 current_buf = current_buf->parent; 463 free(tmp); 464 } 465