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