• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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