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