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