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