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