kconfig: switch to ASSIGN_VAL state in the second lexer
[carl9170fw.git] / config / zconf.l
1 %option nostdinit noyywrap never-interactive full ecs
2 %option 8bit nodefault yylineno
3 %x COMMAND HELP STRING PARAM ASSIGN_VAL
4 %{
5 /*
6  * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
7  * Released under the terms of the GNU GPL v2.0.
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
17 #include "lkc.h"
18
19 #define YY_DECL         static int yylex1(void)
20
21 #define START_STRSIZE   16
22
23 static struct {
24         struct file *file;
25         int lineno;
26 } current_pos;
27
28 static int prev_prev_token = T_EOL;
29 static int prev_token = T_EOL;
30 static char *text;
31 static int text_size, text_asize;
32
33 struct buffer {
34         struct buffer *parent;
35         YY_BUFFER_STATE state;
36 };
37
38 struct buffer *current_buf;
39
40 static int last_ts, first_ts;
41
42 static char *expand_token(const char *in, size_t n);
43 static void append_expanded_string(const char *in);
44 static void zconf_endhelp(void);
45 static void zconf_endfile(void);
46
47 static void new_string(void)
48 {
49         text = xmalloc(START_STRSIZE);
50         text_asize = START_STRSIZE;
51         text_size = 0;
52         *text = 0;
53 }
54
55 static 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
69 static 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
76 static 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
84 n       [A-Za-z0-9_-]
85
86 %%
87         int str = 0;
88         int ts, i;
89
90 [ \t]*#.*\n     |
91 [ \t]*\n        {
92         return T_EOL;
93 }
94 [ \t]*#.*
95 .       {
96         unput(yytext[0]);
97         BEGIN(COMMAND);
98 }
99
100
101 <COMMAND>{
102         {n}+    {
103                 const struct kconf_id *id = kconf_id_lookup(yytext, yyleng);
104                 current_pos.file = current_file;
105                 current_pos.lineno = yylineno;
106                 if (id && id->flags & TF_COMMAND) {
107                         BEGIN(PARAM);
108                         return id->token;
109                 }
110                 alloc_string(yytext, yyleng);
111                 yylval.string = text;
112                 return T_WORD;
113         }
114         ({n}|$)+        {
115                 /* this token includes at least one '$' */
116                 yylval.string = expand_token(yytext, yyleng);
117                 if (strlen(yylval.string))
118                         return T_WORD;
119                 free(yylval.string);
120         }
121         "="     return T_EQUAL;
122         ":="    return T_COLON_EQUAL;
123         "+="    return T_PLUS_EQUAL;
124         [[:blank:]]+
125         .       warn_ignored_character(*yytext);
126         \n      {
127                 BEGIN(INITIAL);
128                 return T_EOL;
129         }
130 }
131
132 <ASSIGN_VAL>{
133         [^[:blank:]\n]+.*       {
134                 alloc_string(yytext, yyleng);
135                 yylval.string = text;
136                 return T_ASSIGN_VAL;
137         }
138         \n      { BEGIN(INITIAL); return T_EOL; }
139         .
140 }
141
142 <PARAM>{
143         "modules"               return T_MODULES;
144         "defconfig_list"        return T_DEFCONFIG_LIST;
145         "allnoconfig_y"         return T_ALLNOCONFIG_Y;
146         "&&"    return T_AND;
147         "||"    return T_OR;
148         "("     return T_OPEN_PAREN;
149         ")"     return T_CLOSE_PAREN;
150         "!"     return T_NOT;
151         "="     return T_EQUAL;
152         "!="    return T_UNEQUAL;
153         "<="    return T_LESS_EQUAL;
154         ">="    return T_GREATER_EQUAL;
155         "<"     return T_LESS;
156         ">"     return T_GREATER;
157         \"|\'   {
158                 str = yytext[0];
159                 new_string();
160                 BEGIN(STRING);
161         }
162         \n      BEGIN(INITIAL); return T_EOL;
163         {n}+    {
164                 const struct kconf_id *id = kconf_id_lookup(yytext, yyleng);
165                 if (id && id->flags & TF_PARAM) {
166                         return id->token;
167                 }
168                 alloc_string(yytext, yyleng);
169                 yylval.string = text;
170                 return T_WORD;
171         }
172         ({n}|$)+        {
173                 /* this token includes at least one '$' */
174                 yylval.string = expand_token(yytext, yyleng);
175                 if (strlen(yylval.string))
176                         return T_WORD;
177                 free(yylval.string);
178         }
179         #.*     /* comment */
180         \\\n    ;
181         [[:blank:]]+
182         .       warn_ignored_character(*yytext);
183 }
184
185 <STRING>{
186         "$".*   append_expanded_string(yytext);
187         [^$'"\\\n]+     {
188                 append_string(yytext, yyleng);
189         }
190         \\.?    {
191                 append_string(yytext + 1, yyleng - 1);
192         }
193         \'|\"   {
194                 if (str == yytext[0]) {
195                         BEGIN(PARAM);
196                         yylval.string = text;
197                         return T_WORD_QUOTE;
198                 } else
199                         append_string(yytext, 1);
200         }
201         \n      {
202                 fprintf(stderr,
203                         "%s:%d:warning: multi-line strings not supported\n",
204                         zconf_curname(), zconf_lineno());
205                 unput('\n');
206                 BEGIN(INITIAL);
207                 yylval.string = text;
208                 return T_WORD_QUOTE;
209         }
210         <<EOF>> {
211                 BEGIN(INITIAL);
212                 yylval.string = text;
213                 return T_WORD_QUOTE;
214         }
215 }
216
217 <HELP>{
218         [ \t]+  {
219                 ts = 0;
220                 for (i = 0; i < yyleng; i++) {
221                         if (yytext[i] == '\t')
222                                 ts = (ts & ~7) + 8;
223                         else
224                                 ts++;
225                 }
226                 last_ts = ts;
227                 if (first_ts) {
228                         if (ts < first_ts) {
229                                 zconf_endhelp();
230                                 return T_HELPTEXT;
231                         }
232                         ts -= first_ts;
233                         while (ts > 8) {
234                                 append_string("        ", 8);
235                                 ts -= 8;
236                         }
237                         append_string("        ", ts);
238                 }
239         }
240         [ \t]*\n/[^ \t\n] {
241                 zconf_endhelp();
242                 return T_HELPTEXT;
243         }
244         [ \t]*\n        {
245                 append_string("\n", 1);
246         }
247         [^ \t\n].* {
248                 while (yyleng) {
249                         if ((yytext[yyleng-1] != ' ') && (yytext[yyleng-1] != '\t'))
250                                 break;
251                         yyleng--;
252                 }
253                 append_string(yytext, yyleng);
254                 if (!first_ts)
255                         first_ts = last_ts;
256         }
257         <<EOF>> {
258                 zconf_endhelp();
259                 return T_HELPTEXT;
260         }
261 }
262
263 <<EOF>> {
264         BEGIN(INITIAL);
265
266         if (prev_token != T_EOL && prev_token != T_HELPTEXT)
267                 fprintf(stderr, "%s:%d:warning: no new line at end of file\n",
268                         current_file->name, yylineno);
269
270         if (current_file) {
271                 zconf_endfile();
272                 return T_EOL;
273         }
274         fclose(yyin);
275         yyterminate();
276 }
277
278 %%
279
280 /* second stage lexer */
281 int yylex(void)
282 {
283         int token;
284
285 repeat:
286         token = yylex1();
287
288         /* Do not pass unneeded T_EOL to the parser. */
289         if ((prev_token == T_EOL || prev_token == T_HELPTEXT) && token == T_EOL)
290                 goto repeat;
291
292         if (prev_prev_token == T_EOL && prev_token == T_WORD &&
293             (token == T_EQUAL || token == T_COLON_EQUAL || token == T_PLUS_EQUAL))
294                 BEGIN(ASSIGN_VAL);
295
296         prev_prev_token = prev_token;
297         prev_token = token;
298
299         return token;
300 }
301
302 static char *expand_token(const char *in, size_t n)
303 {
304         char *out;
305         int c;
306         char c2;
307         const char *rest, *end;
308
309         new_string();
310         append_string(in, n);
311
312         /* get the whole line because we do not know the end of token. */
313         while ((c = input()) != EOF) {
314                 if (c == '\n') {
315                         unput(c);
316                         break;
317                 }
318                 c2 = c;
319                 append_string(&c2, 1);
320         }
321
322         rest = text;
323         out = expand_one_token(&rest);
324
325         /* push back unused characters to the input stream */
326         end = rest + strlen(rest);
327         while (end > rest)
328                 unput(*--end);
329
330         free(text);
331
332         return out;
333 }
334
335 static void append_expanded_string(const char *str)
336 {
337         const char *end;
338         char *res;
339
340         str++;
341
342         res = expand_dollar(&str);
343
344         /* push back unused characters to the input stream */
345         end = str + strlen(str);
346         while (end > str)
347                 unput(*--end);
348
349         append_string(res, strlen(res));
350
351         free(res);
352 }
353
354 void zconf_starthelp(void)
355 {
356         new_string();
357         last_ts = first_ts = 0;
358         BEGIN(HELP);
359 }
360
361 static void zconf_endhelp(void)
362 {
363         yylval.string = text;
364         BEGIN(INITIAL);
365 }
366
367
368 /*
369  * Try to open specified file with following names:
370  * ./name
371  * $(srctree)/name
372  * The latter is used when srctree is separate from objtree
373  * when compiling the firmware.
374  * Return NULL if file is not found.
375  */
376 FILE *zconf_fopen(const char *name)
377 {
378         char *env, fullname[PATH_MAX+1];
379         FILE *f;
380
381         f = fopen(name, "r");
382         if (!f && name != NULL && name[0] != '/') {
383                 env = getenv(SRCTREE);
384                 if (env) {
385                         sprintf(fullname, "%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 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 static void zconf_endfile(void)
449 {
450         struct buffer *parent;
451
452         current_file = current_file->parent;
453         if (current_file)
454                 yylineno = current_file->lineno;
455
456         parent = current_buf->parent;
457         if (parent) {
458                 fclose(yyin);
459                 yy_delete_buffer(YY_CURRENT_BUFFER);
460                 yy_switch_to_buffer(parent->state);
461         }
462         free(current_buf);
463         current_buf = parent;
464 }
465
466 int zconf_lineno(void)
467 {
468         return current_pos.lineno;
469 }
470
471 const char *zconf_curname(void)
472 {
473         return current_pos.file ? current_pos.file->name : "<none>";
474 }