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