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