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