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. */ 26const 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 */ 32int cur_lineno; 33 34static int prev_prev_token = T_EOL; 35static int prev_token = T_EOL; 36static char *text; 37static int text_size, text_asize; 38 39struct buffer { 40 struct buffer *parent; 41 YY_BUFFER_STATE state; 42 int yylineno; 43 const char *filename; 44 int source_lineno; 45}; 46 47static struct buffer *current_buf; 48 49static int last_ts, first_ts; 50 51static char *expand_token(const char *in, size_t n); 52static void append_expanded_string(const char *in); 53static void zconf_endhelp(void); 54static void zconf_endfile(void); 55 56static void new_string(void) 57{ 58 text = xmalloc(START_STRSIZE); 59 text_asize = START_STRSIZE; 60 text_size = 0; 61 *text = 0; 62} 63 64static 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 78static 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 85static 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 93n [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"optional" return T_OPTIONAL; 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 */ 271int yylex(void) 272{ 273 int token; 274 275repeat: 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 302static 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 338static 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 357void zconf_starthelp(void) 358{ 359 new_string(); 360 last_ts = first_ts = 0; 361 BEGIN(HELP); 362} 363 364static 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 */ 379FILE *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 396void 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 408void 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 448static 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