build: scripts/config - update to kconfig-v5.14
[openwrt/staging/nbd.git] / scripts / config / lexer.l
1 /* SPDX-License-Identifier: GPL-2.0-only */
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 #include <glob.h>
16 #include <libgen.h>
17
18 #include "lkc.h"
19 #include "parser.tab.h"
20
21 #define YY_DECL static int yylex1(void)
22
23 #define START_STRSIZE 16
24
25 static struct {
26 struct file *file;
27 int lineno;
28 } current_pos;
29
30 static int prev_prev_token = T_EOL;
31 static int prev_token = T_EOL;
32 static char *text;
33 static int text_size, text_asize;
34
35 struct buffer {
36 struct buffer *parent;
37 YY_BUFFER_STATE state;
38 };
39
40 static struct buffer *current_buf;
41
42 static int last_ts, first_ts;
43
44 static char *expand_token(const char *in, size_t n);
45 static void append_expanded_string(const char *in);
46 static void zconf_endhelp(void);
47 static void zconf_endfile(void);
48
49 static void new_string(void)
50 {
51 text = xmalloc(START_STRSIZE);
52 text_asize = START_STRSIZE;
53 text_size = 0;
54 *text = 0;
55 }
56
57 static void append_string(const char *str, int size)
58 {
59 int new_size = text_size + size + 1;
60 if (new_size > text_asize) {
61 new_size += START_STRSIZE - 1;
62 new_size &= -START_STRSIZE;
63 text = xrealloc(text, new_size);
64 text_asize = new_size;
65 }
66 memcpy(text + text_size, str, size);
67 text_size += size;
68 text[text_size] = 0;
69 }
70
71 static void alloc_string(const char *str, int size)
72 {
73 text = xmalloc(size + 1);
74 memcpy(text, str, size);
75 text[size] = 0;
76 }
77
78 static void warn_ignored_character(char chr)
79 {
80 fprintf(stderr,
81 "%s:%d:warning: ignoring unsupported character '%c'\n",
82 current_file->name, yylineno, chr);
83 }
84 %}
85
86 n [A-Za-z0-9_-]
87
88 %%
89 int str = 0;
90 int ts, i;
91
92 #.* /* ignore comment */
93 [ \t]* /* whitespaces */
94 \\\n /* escaped new line */
95 \n return T_EOL;
96 "bool" return T_BOOL;
97 "choice" return T_CHOICE;
98 "comment" return T_COMMENT;
99 "config" return T_CONFIG;
100 "def_bool" return T_DEF_BOOL;
101 "def_tristate" return T_DEF_TRISTATE;
102 "default" return T_DEFAULT;
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 "optional" return T_OPTIONAL;
118 "prompt" return T_PROMPT;
119 "range" return T_RANGE;
120 "reset" return T_RESET;
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 static 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 void zconf_nextfile(const char *name)
445 {
446 glob_t gl;
447 int err;
448 int i;
449 char path[PATH_MAX], *p;
450
451 err = glob(name, GLOB_ERR | GLOB_MARK, NULL, &gl);
452
453 /* ignore wildcard patterns that return no result */
454 if (err == GLOB_NOMATCH && strchr(name, '*')) {
455 err = 0;
456 gl.gl_pathc = 0;
457 }
458
459 if (err == GLOB_NOMATCH) {
460 p = strdup(current_file->name);
461 if (p) {
462 snprintf(path, sizeof(path), "%s/%s", dirname(p), name);
463 err = glob(path, GLOB_ERR | GLOB_MARK, NULL, &gl);
464 free(p);
465 }
466 }
467
468 if (err) {
469 const char *reason = "unknown error";
470
471 switch (err) {
472 case GLOB_NOSPACE:
473 reason = "out of memory";
474 break;
475 case GLOB_ABORTED:
476 reason = "read error";
477 break;
478 case GLOB_NOMATCH:
479 reason = "No files found";
480 break;
481 default:
482 break;
483 }
484
485 printf("%s:%d: glob failed: %s \"%s\"\n", zconf_curname(), zconf_lineno(),
486 reason, name);
487
488 exit(1);
489 }
490
491 for (i = 0; i < gl.gl_pathc; i++)
492 __zconf_nextfile(gl.gl_pathv[i]);
493 }
494
495 static void zconf_endfile(void)
496 {
497 struct buffer *parent;
498
499 current_file = current_file->parent;
500 if (current_file)
501 yylineno = current_file->lineno;
502
503 parent = current_buf->parent;
504 if (parent) {
505 fclose(yyin);
506 yy_delete_buffer(YY_CURRENT_BUFFER);
507 yy_switch_to_buffer(parent->state);
508 }
509 free(current_buf);
510 current_buf = parent;
511 }
512
513 int zconf_lineno(void)
514 {
515 return current_pos.lineno;
516 }
517
518 const char *zconf_curname(void)
519 {
520 return current_pos.file ? current_pos.file->name : "<none>";
521 }