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