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