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