build: scripts/config - update to kconfig-v5.6
[oweals/openwrt.git] / scripts / config / lexer.l
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 #include <unistd.h>
16 #include <glob.h>
17 #include <libgen.h>
18
19 #include "lkc.h"
20 #include "parser.tab.h"
21
22 #define YY_DECL         static int yylex1(void)
23
24 #define START_STRSIZE   16
25
26 static struct {
27         struct file *file;
28         int lineno;
29 } current_pos;
30
31 static int prev_prev_token = T_EOL;
32 static int prev_token = T_EOL;
33 static char *text;
34 static int text_size, text_asize;
35
36 struct buffer {
37         struct buffer *parent;
38         YY_BUFFER_STATE state;
39 };
40
41 struct buffer *current_buf;
42
43 static int last_ts, first_ts;
44
45 static char *expand_token(const char *in, size_t n);
46 static void append_expanded_string(const char *in);
47 static void zconf_endhelp(void);
48 static void zconf_endfile(void);
49
50 static void new_string(void)
51 {
52         text = xmalloc(START_STRSIZE);
53         text_asize = START_STRSIZE;
54         text_size = 0;
55         *text = 0;
56 }
57
58 static void append_string(const char *str, int size)
59 {
60         int new_size = text_size + size + 1;
61         if (new_size > text_asize) {
62                 new_size += START_STRSIZE - 1;
63                 new_size &= -START_STRSIZE;
64                 text = xrealloc(text, new_size);
65                 text_asize = new_size;
66         }
67         memcpy(text + text_size, str, size);
68         text_size += size;
69         text[text_size] = 0;
70 }
71
72 static void alloc_string(const char *str, int size)
73 {
74         text = xmalloc(size + 1);
75         memcpy(text, str, size);
76         text[size] = 0;
77 }
78
79 static void warn_ignored_character(char chr)
80 {
81         fprintf(stderr,
82                 "%s:%d:warning: ignoring unsupported character '%c'\n",
83                 current_file->name, yylineno, chr);
84 }
85 %}
86
87 n       [A-Za-z0-9_-]
88
89 %%
90         int str = 0;
91         int ts, i;
92
93 #.*                     /* ignore comment */
94 [ \t]*                  /* whitespaces */
95 \\\n                    /* escaped new line */
96 \n                      return T_EOL;
97 "allnoconfig_y"         return T_ALLNOCONFIG_Y;
98 "bool"                  return T_BOOL;
99 "choice"                return T_CHOICE;
100 "comment"               return T_COMMENT;
101 "config"                return T_CONFIG;
102 "def_bool"              return T_DEF_BOOL;
103 "def_tristate"          return T_DEF_TRISTATE;
104 "default"               return T_DEFAULT;
105 "defconfig_list"        return T_DEFCONFIG_LIST;
106 "depends"               return T_DEPENDS;
107 "endchoice"             return T_ENDCHOICE;
108 "endif"                 return T_ENDIF;
109 "endmenu"               return T_ENDMENU;
110 "help"|"---help---"     return T_HELP;
111 "hex"                   return T_HEX;
112 "if"                    return T_IF;
113 "imply"                 return T_IMPLY;
114 "int"                   return T_INT;
115 "mainmenu"              return T_MAINMENU;
116 "menu"                  return T_MENU;
117 "menuconfig"            return T_MENUCONFIG;
118 "modules"               return T_MODULES;
119 "on"                    return T_ON;
120 "option"                return T_OPTION;
121 "optional"              return T_OPTIONAL;
122 "prompt"                return T_PROMPT;
123 "range"                 return T_RANGE;
124 "reset"                 return T_RESET;
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                                 str = 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 (str == 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                         zconf_curname(), zconf_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                 ts = 0;
207                 for (i = 0; i < yyleng; i++) {
208                         if (yytext[i] == '\t')
209                                 ts = (ts & ~7) + 8;
210                         else
211                                 ts++;
212                 }
213                 last_ts = ts;
214                 if (first_ts) {
215                         if (ts < first_ts) {
216                                 zconf_endhelp();
217                                 return T_HELPTEXT;
218                         }
219                         ts -= first_ts;
220                         while (ts > 8) {
221                                 append_string("        ", 8);
222                                 ts -= 8;
223                         }
224                         append_string("        ", ts);
225                 }
226         }
227         [ \t]*\n/[^ \t\n] {
228                 zconf_endhelp();
229                 return T_HELPTEXT;
230         }
231         [ \t]*\n        {
232                 append_string("\n", 1);
233         }
234         [^ \t\n].* {
235                 while (yyleng) {
236                         if ((yytext[yyleng-1] != ' ') && (yytext[yyleng-1] != '\t'))
237                                 break;
238                         yyleng--;
239                 }
240                 append_string(yytext, yyleng);
241                 if (!first_ts)
242                         first_ts = last_ts;
243         }
244         <<EOF>> {
245                 zconf_endhelp();
246                 return T_HELPTEXT;
247         }
248 }
249
250 <<EOF>> {
251         BEGIN(INITIAL);
252
253         if (prev_token != T_EOL && prev_token != T_HELPTEXT)
254                 fprintf(stderr, "%s:%d:warning: no new line at end of file\n",
255                         current_file->name, yylineno);
256
257         if (current_file) {
258                 zconf_endfile();
259                 return T_EOL;
260         }
261         fclose(yyin);
262         yyterminate();
263 }
264
265 %%
266
267 /* second stage lexer */
268 int yylex(void)
269 {
270         int token;
271
272 repeat:
273         token = yylex1();
274
275         if (prev_token == T_EOL || prev_token == T_HELPTEXT) {
276                 if (token == T_EOL) {
277                         /* Do not pass unneeded T_EOL to the parser. */
278                         goto repeat;
279                 } else {
280                         /*
281                          * For the parser, update file/lineno at the first token
282                          * of each statement. Generally, \n is a statement
283                          * terminator in Kconfig, but it is not always true
284                          * because \n could be escaped by a backslash.
285                          */
286                         current_pos.file = current_file;
287                         current_pos.lineno = yylineno;
288                 }
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         /* get the whole line because we do not know the end of token. */
312         while ((c = input()) != EOF) {
313                 if (c == '\n') {
314                         unput(c);
315                         break;
316                 }
317                 c2 = c;
318                 append_string(&c2, 1);
319         }
320
321         rest = text;
322         out = expand_one_token(&rest);
323
324         /* push back unused characters to the input stream */
325         end = rest + strlen(rest);
326         while (end > rest)
327                 unput(*--end);
328
329         free(text);
330
331         return out;
332 }
333
334 static void append_expanded_string(const char *str)
335 {
336         const char *end;
337         char *res;
338
339         str++;
340
341         res = expand_dollar(&str);
342
343         /* push back unused characters to the input stream */
344         end = str + strlen(str);
345         while (end > str)
346                 unput(*--end);
347
348         append_string(res, strlen(res));
349
350         free(res);
351 }
352
353 void zconf_starthelp(void)
354 {
355         new_string();
356         last_ts = first_ts = 0;
357         BEGIN(HELP);
358 }
359
360 static void zconf_endhelp(void)
361 {
362         yylval.string = text;
363         BEGIN(INITIAL);
364 }
365
366
367 /*
368  * Try to open specified file with following names:
369  * ./name
370  * $(srctree)/name
371  * The latter is used when srctree is separate from objtree
372  * when compiling the kernel.
373  * Return NULL if file is not found.
374  */
375 FILE *zconf_fopen(const char *name)
376 {
377         char *env, fullname[PATH_MAX+1];
378         FILE *f;
379
380         f = fopen(name, "r");
381         if (!f && name != NULL && name[0] != '/') {
382                 env = getenv(SRCTREE);
383                 if (env) {
384                         snprintf(fullname, sizeof(fullname),
385                                  "%s/%s", env, name);
386                         f = fopen(fullname, "r");
387                 }
388         }
389         return f;
390 }
391
392 void zconf_initscan(const char *name)
393 {
394         yyin = zconf_fopen(name);
395         if (!yyin) {
396                 fprintf(stderr, "can't find file %s\n", name);
397                 exit(1);
398         }
399
400         current_buf = xmalloc(sizeof(*current_buf));
401         memset(current_buf, 0, sizeof(*current_buf));
402
403         current_file = file_lookup(name);
404         yylineno = 1;
405 }
406
407 static void __zconf_nextfile(const char *name)
408 {
409         struct file *iter;
410         struct file *file = file_lookup(name);
411         struct buffer *buf = xmalloc(sizeof(*buf));
412         memset(buf, 0, sizeof(*buf));
413
414         current_buf->state = YY_CURRENT_BUFFER;
415         yyin = zconf_fopen(file->name);
416         if (!yyin) {
417                 fprintf(stderr, "%s:%d: can't open file \"%s\"\n",
418                         zconf_curname(), zconf_lineno(), file->name);
419                 exit(1);
420         }
421         yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
422         buf->parent = current_buf;
423         current_buf = buf;
424
425         current_file->lineno = yylineno;
426         file->parent = current_file;
427
428         for (iter = current_file; iter; iter = iter->parent) {
429                 if (!strcmp(iter->name, file->name)) {
430                         fprintf(stderr,
431                                 "Recursive inclusion detected.\n"
432                                 "Inclusion path:\n"
433                                 "  current file : %s\n", file->name);
434                         iter = file;
435                         do {
436                                 iter = iter->parent;
437                                 fprintf(stderr, "  included from: %s:%d\n",
438                                         iter->name, iter->lineno - 1);
439                         } while (strcmp(iter->name, file->name));
440                         exit(1);
441                 }
442         }
443
444         yylineno = 1;
445         current_file = file;
446 }
447
448 void zconf_nextfile(const char *name)
449 {
450         glob_t gl;
451         int err;
452         int i;
453         char path[PATH_MAX], *p;
454
455         err = glob(name, GLOB_ERR | GLOB_MARK, NULL, &gl);
456
457         /* ignore wildcard patterns that return no result */
458         if (err == GLOB_NOMATCH && strchr(name, '*')) {
459                 err = 0;
460                 gl.gl_pathc = 0;
461         }
462
463         if (err == GLOB_NOMATCH) {
464                 p = strdup(current_file->name);
465                 if (p) {
466                         snprintf(path, sizeof(path), "%s/%s", dirname(p), name);
467                         err = glob(path, GLOB_ERR | GLOB_MARK, NULL, &gl);
468                         free(p);
469                 }
470         }
471
472         if (err) {
473                 const char *reason = "unknown error";
474
475                 switch (err) {
476                 case GLOB_NOSPACE:
477                         reason = "out of memory";
478                         break;
479                 case GLOB_ABORTED:
480                         reason = "read error";
481                         break;
482                 case GLOB_NOMATCH:
483                         reason = "No files found";
484                         break;
485                 default:
486                         break;
487                 }
488
489                 printf("%s:%d: glob failed: %s \"%s\"\n", zconf_curname(), zconf_lineno(),
490                         reason, name);
491
492                 exit(1);
493         }
494
495         for (i = 0; i < gl.gl_pathc; i++)
496                 __zconf_nextfile(gl.gl_pathv[i]);
497 }
498
499 static void zconf_endfile(void)
500 {
501         struct buffer *parent;
502
503         current_file = current_file->parent;
504         if (current_file)
505                 yylineno = current_file->lineno;
506
507         parent = current_buf->parent;
508         if (parent) {
509                 fclose(yyin);
510                 yy_delete_buffer(YY_CURRENT_BUFFER);
511                 yy_switch_to_buffer(parent->state);
512         }
513         free(current_buf);
514         current_buf = parent;
515 }
516
517 int zconf_lineno(void)
518 {
519         return current_pos.lineno;
520 }
521
522 const char *zconf_curname(void)
523 {
524         return current_pos.file ? current_pos.file->name : "<none>";
525 }