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 
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 
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 
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 
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