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