Beautify diagnostic messages.
[linux-2.6/linux-mips.git] / scripts / tkparse.c
blobabf5ad8b080dc649d13655e6bb89654e3a86c404
1 /*
2 * tkparse.c
4 * Eric Youngdale was the original author of xconfig.
5 * Michael Elizabeth Chastain (mec@shout.net) is the current maintainer.
7 * Parse a config.in file and translate it to a wish script.
8 * This task has three parts:
10 * tkparse.c tokenize the input
11 * tkcond.c transform 'if ...' statements
12 * tkgen.c generate output
14 * Change History
16 * 7 January 1999, Michael Elizabeth Chastain, <mec@shout.net>
17 * - Teach dep_tristate about a few literals, such as:
18 * dep_tristate 'foo' CONFIG_FOO m
19 * Also have it print an error message and exit on some parse failures.
21 * 14 January 1999, Michael Elizabeth Chastain, <mec@shout.net>
22 * - Don't fclose stdin. Thanks to Tony Hoyle for nailing this one.
24 * 14 January 1999, Michael Elizabeth Chastain, <mec@shout.net>
25 * - Steam-clean this file. I tested this by generating kconfig.tk for
26 * every architecture and comparing it character-for-character against
27 * the output of the old tkparse.
29 * 23 January 1999, Michael Elizabeth Chastain, <mec@shout.net>
30 * - Remove bug-compatible code.
32 * 07 July 1999, Andrzej M. Krzysztofowicz, <ankry@mif.pg.gda.pl>
33 * - Submenus implemented,
34 * - plenty of option updating/displaying fixes,
35 * - dep_bool, define_hex, define_int, define_string, define_tristate and
36 * undef implemented,
37 * - dep_tristate fixed to support multiple dependencies,
38 * - handling of variables with an empty value implemented,
39 * - value checking for int and hex fields,
40 * - more checking during condition parsing; choice variables are treated as
41 * all others now,
43 * TO DO:
44 * - xconfig is at the end of its life cycle. Contact <mec@shout.net> if
45 * you are interested in working on the replacement.
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
52 #include "tkparse.h"
54 static struct kconfig * config_list = NULL;
55 static struct kconfig * config_last = NULL;
56 static const char * current_file = "<unknown file>";
57 static int lineno = 0;
59 static void do_source( const char * );
61 #undef strcmp
62 int my_strcmp( const char * s1, const char * s2 ) { return strcmp( s1, s2 ); }
63 #define strcmp my_strcmp
66 * Report a syntax error.
68 static void syntax_error( const char * msg )
70 fprintf( stderr, "%s: %d: %s\n", current_file, lineno, msg );
71 exit( 1 );
77 * Find index of a specyfic variable in the symbol table.
78 * Create a new entry if it does not exist yet.
80 #define VARTABLE_SIZE 2048
81 struct variable vartable[VARTABLE_SIZE];
82 int max_varnum = 0;
84 int get_varnum( char * name )
86 int i;
88 for ( i = 1; i <= max_varnum; i++ )
89 if ( strcmp( vartable[i].name, name ) == 0 )
90 return i;
91 if (max_varnum > VARTABLE_SIZE-1)
92 syntax_error( "Too many variables defined." );
93 vartable[++max_varnum].name = malloc( strlen( name )+1 );
94 strcpy( vartable[max_varnum].name, name );
95 return max_varnum;
101 * Get a string.
103 static const char * get_string( const char * pnt, char ** label )
105 const char * word;
107 word = pnt;
108 for ( ; ; )
110 if ( *pnt == '\0' || *pnt == ' ' || *pnt == '\t' )
111 break;
112 pnt++;
115 *label = malloc( pnt - word + 1 );
116 memcpy( *label, word, pnt - word );
117 (*label)[pnt - word] = '\0';
119 if ( *pnt != '\0' )
120 pnt++;
121 return pnt;
127 * Get a quoted string.
128 * Insert a '\' before any characters that need quoting.
130 static const char * get_qstring( const char * pnt, char ** label )
132 char quote_char;
133 char newlabel [2048];
134 char * pnt1;
136 /* advance to the open quote */
137 for ( ; ; )
139 if ( *pnt == '\0' )
140 return pnt;
141 quote_char = *pnt++;
142 if ( quote_char == '"' || quote_char == '\'' )
143 break;
146 /* copy into an intermediate buffer */
147 pnt1 = newlabel;
148 for ( ; ; )
150 if ( *pnt == '\0' )
151 syntax_error( "unterminated quoted string" );
152 if ( *pnt == quote_char && pnt[-1] != '\\' )
153 break;
155 /* copy the character, quoting if needed */
156 if ( *pnt == '"' || *pnt == '\'' || *pnt == '[' || *pnt == ']' )
157 *pnt1++ = '\\';
158 *pnt1++ = *pnt++;
161 /* copy the label into a permanent location */
162 *pnt1++ = '\0';
163 *label = (char *) malloc( pnt1 - newlabel );
164 memcpy( *label, newlabel, pnt1 - newlabel );
166 /* skip over last quote and next whitespace */
167 pnt++;
168 while ( *pnt == ' ' || *pnt == '\t' )
169 pnt++;
170 return pnt;
176 * Get a quoted or unquoted string. It is recognized by the first
177 * non-white character. '"' and '"' are not allowed inside the string.
179 static const char * get_qnqstring( const char * pnt, char ** label )
181 char quote_char;
183 while ( *pnt == ' ' || *pnt == '\t' )
184 pnt++;
186 if ( *pnt == '\0' )
187 return pnt;
188 quote_char = *pnt;
189 if ( quote_char == '"' || quote_char == '\'' )
190 return get_qstring( pnt, label );
191 else
192 return get_string( pnt, label );
198 * Tokenize an 'if' statement condition.
200 static struct condition * tokenize_if( const char * pnt )
202 struct condition * list;
203 struct condition * last;
204 struct condition * prev;
206 /* eat the open bracket */
207 while ( *pnt == ' ' || *pnt == '\t' )
208 pnt++;
209 if ( *pnt != '[' )
210 syntax_error( "bad 'if' condition" );
211 pnt++;
213 list = last = NULL;
214 for ( ; ; )
216 struct condition * cond;
218 /* advance to the next token */
219 while ( *pnt == ' ' || *pnt == '\t' )
220 pnt++;
221 if ( *pnt == '\0' )
222 syntax_error( "unterminated 'if' condition" );
223 if ( *pnt == ']' )
224 return list;
226 /* allocate a new token */
227 cond = malloc( sizeof(*cond) );
228 memset( cond, 0, sizeof(*cond) );
229 if ( last == NULL )
230 { list = last = cond; prev = NULL; }
231 else
232 { prev = last; last->next = cond; last = cond; }
234 /* determine the token value */
235 if ( *pnt == '-' && pnt[1] == 'a' )
237 if ( ! prev || ( prev->op != op_variable && prev->op != op_constant ) )
238 syntax_error( "incorrect argument" );
239 cond->op = op_and; pnt += 2; continue;
242 if ( *pnt == '-' && pnt[1] == 'o' )
244 if ( ! prev || ( prev->op != op_variable && prev->op != op_constant ) )
245 syntax_error( "incorrect argument" );
246 cond->op = op_or; pnt += 2; continue;
249 if ( *pnt == '!' && pnt[1] == '=' )
251 if ( ! prev || ( prev->op != op_variable && prev->op != op_constant ) )
252 syntax_error( "incorrect argument" );
253 cond->op = op_neq; pnt += 2; continue;
256 if ( *pnt == '=' )
258 if ( ! prev || ( prev->op != op_variable && prev->op != op_constant ) )
259 syntax_error( "incorrect argument" );
260 cond->op = op_eq; pnt += 1; continue;
263 if ( *pnt == '!' )
265 if ( prev && ( prev->op != op_and && prev->op != op_or
266 && prev->op != op_bang ) )
267 syntax_error( "incorrect argument" );
268 cond->op = op_bang; pnt += 1; continue;
271 if ( *pnt == '"' )
273 const char * word;
275 if ( prev && ( prev->op == op_variable || prev->op == op_constant ) )
276 syntax_error( "incorrect argument" );
277 /* advance to the word */
278 pnt++;
279 if ( *pnt == '$' )
280 { cond->op = op_variable; pnt++; }
281 else
282 { cond->op = op_constant; }
284 /* find the end of the word */
285 word = pnt;
286 for ( ; ; )
288 if ( *pnt == '\0' )
289 syntax_error( "unterminated double quote" );
290 if ( *pnt == '"' )
291 break;
292 pnt++;
295 /* store a copy of this word */
297 char * str = malloc( pnt - word + 1 );
298 memcpy( str, word, pnt - word );
299 str [pnt - word] = '\0';
300 if ( cond->op == op_variable )
302 cond->nameindex = get_varnum( str );
303 free( str );
305 else /* op_constant */
307 cond->str = str;
311 pnt++;
312 continue;
315 /* unknown token */
316 syntax_error( "bad if condition" );
323 * Tokenize a choice list. Choices appear as pairs of strings;
324 * note that I am parsing *inside* the double quotes. Ugh.
326 static const char * tokenize_choices( struct kconfig * cfg_choose,
327 const char * pnt )
329 for ( ; ; )
331 struct kconfig * cfg;
332 char * buffer = malloc( 64 );
334 /* skip whitespace */
335 while ( *pnt == ' ' || *pnt == '\t' )
336 pnt++;
337 if ( *pnt == '\0' )
338 return pnt;
340 /* allocate a new kconfig line */
341 cfg = malloc( sizeof(*cfg) );
342 memset( cfg, 0, sizeof(*cfg) );
343 if ( config_last == NULL )
344 { config_last = config_list = cfg; }
345 else
346 { config_last->next = cfg; config_last = cfg; }
348 /* fill out the line */
349 cfg->token = token_choice_item;
350 cfg->cfg_parent = cfg_choose;
351 pnt = get_string( pnt, &cfg->label );
352 while ( *pnt == ' ' || *pnt == '\t' )
353 pnt++;
354 pnt = get_string( pnt, &buffer );
355 cfg->nameindex = get_varnum( buffer );
358 return pnt;
364 * Tokenize one line.
366 static void tokenize_line( const char * pnt )
368 static struct kconfig * last_menuoption = NULL;
369 enum e_token token;
370 struct kconfig * cfg;
371 struct dependency ** dep_ptr;
372 char * buffer = malloc( 64 );
374 /* skip white space */
375 while ( *pnt == ' ' || *pnt == '\t' )
376 pnt++;
379 * categorize the next token
382 #define match_token(t, s) \
383 if (strncmp(pnt, s, strlen(s)) == 0) { token = t; pnt += strlen(s); break; }
385 token = token_UNKNOWN;
386 switch ( *pnt )
388 default:
389 break;
391 case '#':
392 case '\0':
393 return;
395 case 'b':
396 match_token( token_bool, "bool" );
397 break;
399 case 'c':
400 match_token( token_choice_header, "choice" );
401 match_token( token_comment, "comment" );
402 break;
404 case 'd':
405 match_token( token_define_bool, "define_bool" );
406 match_token( token_define_hex, "define_hex" );
407 match_token( token_define_int, "define_int" );
408 match_token( token_define_string, "define_string" );
409 match_token( token_define_tristate, "define_tristate" );
410 match_token( token_dep_bool, "dep_bool" );
411 match_token( token_dep_mbool, "dep_mbool" );
412 match_token( token_dep_tristate, "dep_tristate" );
413 break;
415 case 'e':
416 match_token( token_else, "else" );
417 match_token( token_endmenu, "endmenu" );
418 break;
420 case 'f':
421 match_token( token_fi, "fi" );
422 break;
424 case 'h':
425 match_token( token_hex, "hex" );
426 break;
428 case 'i':
429 match_token( token_if, "if" );
430 match_token( token_int, "int" );
431 break;
433 case 'm':
434 match_token( token_mainmenu_name, "mainmenu_name" );
435 match_token( token_mainmenu_option, "mainmenu_option" );
436 break;
438 case 's':
439 match_token( token_source, "source" );
440 match_token( token_string, "string" );
441 break;
443 case 't':
444 match_token( token_then, "then" );
445 match_token( token_tristate, "tristate" );
446 break;
448 case 'u':
449 match_token( token_unset, "unset" );
450 break;
453 #undef match_token
455 if ( token == token_source )
457 while ( *pnt == ' ' || *pnt == '\t' )
458 pnt++;
459 do_source( pnt );
460 return;
463 if ( token == token_then )
465 if ( config_last != NULL && config_last->token == token_if )
466 return;
467 syntax_error( "bogus 'then'" );
470 #if 0
471 if ( token == token_unset )
473 fprintf( stderr, "Ignoring 'unset' command\n" );
474 return;
476 #endif
478 if ( token == token_UNKNOWN )
479 syntax_error( "unknown command" );
482 * Allocate an item.
484 cfg = malloc( sizeof(*cfg) );
485 memset( cfg, 0, sizeof(*cfg) );
486 if ( config_last == NULL )
487 { config_last = config_list = cfg; }
488 else
489 { config_last->next = cfg; config_last = cfg; }
492 * Tokenize the arguments.
494 while ( *pnt == ' ' || *pnt == '\t' )
495 pnt++;
497 cfg->token = token;
498 switch ( token )
500 default:
501 syntax_error( "unknown token" );
503 case token_bool:
504 case token_tristate:
505 pnt = get_qstring ( pnt, &cfg->label );
506 pnt = get_string ( pnt, &buffer );
507 cfg->nameindex = get_varnum( buffer );
508 break;
510 case token_choice_header:
512 static int choose_number = 0;
513 char * choice_list;
515 pnt = get_qstring ( pnt, &cfg->label );
516 pnt = get_qstring ( pnt, &choice_list );
517 pnt = get_string ( pnt, &cfg->value );
519 cfg->nameindex = -(choose_number++);
520 tokenize_choices( cfg, choice_list );
521 free( choice_list );
523 break;
525 case token_comment:
526 pnt = get_qstring(pnt, &cfg->label);
527 if ( last_menuoption != NULL )
529 pnt = get_qstring(pnt, &cfg->label);
530 if (cfg->label == NULL)
531 syntax_error( "missing comment text" );
532 last_menuoption->label = cfg->label;
533 last_menuoption = NULL;
535 break;
537 case token_define_bool:
538 case token_define_tristate:
539 pnt = get_string( pnt, &buffer );
540 cfg->nameindex = get_varnum( buffer );
541 while ( *pnt == ' ' || *pnt == '\t' )
542 pnt++;
543 if ( ( pnt[0] == 'Y' || pnt[0] == 'M' || pnt[0] == 'N'
544 || pnt[0] == 'y' || pnt[0] == 'm' || pnt[0] == 'n' )
545 && ( pnt[1] == '\0' || pnt[1] == ' ' || pnt[1] == '\t' ) )
547 if ( *pnt == 'n' || *pnt == 'N' ) cfg->value = strdup( "CONSTANT_N" );
548 else if ( *pnt == 'y' || *pnt == 'Y' ) cfg->value = strdup( "CONSTANT_Y" );
549 else if ( *pnt == 'm' || *pnt == 'M' ) cfg->value = strdup( "CONSTANT_M" );
551 else if ( *pnt == '$' )
553 pnt++;
554 pnt = get_string( pnt, &cfg->value );
556 else
558 syntax_error( "unknown define_bool value" );
560 get_varnum( cfg->value );
561 break;
563 case token_define_hex:
564 case token_define_int:
565 pnt = get_string( pnt, &buffer );
566 cfg->nameindex = get_varnum( buffer );
567 pnt = get_string( pnt, &cfg->value );
568 break;
570 case token_define_string:
571 pnt = get_string( pnt, &buffer );
572 cfg->nameindex = get_varnum( buffer );
573 pnt = get_qnqstring( pnt, &cfg->value );
574 if (cfg->value == NULL)
575 syntax_error( "missing value" );
576 break;
578 case token_dep_bool:
579 case token_dep_mbool:
580 case token_dep_tristate:
581 pnt = get_qstring ( pnt, &cfg->label );
582 pnt = get_string ( pnt, &buffer );
583 cfg->nameindex = get_varnum( buffer );
585 while ( *pnt == ' ' || *pnt == '\t' )
586 pnt++;
588 dep_ptr = &(cfg->depend);
590 do {
591 *dep_ptr = (struct dependency *) malloc( sizeof( struct dependency ) );
592 (*dep_ptr)->next = NULL;
594 if ( ( pnt[0] == 'Y' || pnt[0] == 'M' || pnt[0] == 'N'
595 || pnt[0] == 'y' || pnt[0] == 'm' || pnt[0] == 'n' )
596 && ( pnt[1] == '\0' || pnt[1] == ' ' || pnt[1] == '\t' ) )
598 /* dep_tristate 'foo' CONFIG_FOO m */
599 if ( pnt[0] == 'Y' || pnt[0] == 'y' )
600 (*dep_ptr)->name = strdup( "CONSTANT_Y" );
601 else if ( pnt[0] == 'N' || pnt[0] == 'n' )
602 (*dep_ptr)->name = strdup( "CONSTANT_N" );
603 else
604 (*dep_ptr)->name = strdup( "CONSTANT_M" );
605 pnt++;
606 get_varnum( (*dep_ptr)->name );
608 else if ( *pnt == '$' )
610 pnt++;
611 pnt = get_string( pnt, &(*dep_ptr)->name );
612 get_varnum( (*dep_ptr)->name );
614 else
616 syntax_error( "can't handle dep_bool/dep_mbool/dep_tristate condition" );
618 dep_ptr = &(*dep_ptr)->next;
619 while ( *pnt == ' ' || *pnt == '\t' )
620 pnt++;
621 } while ( *pnt );
624 * Create a conditional for this object's dependencies.
627 char fake_if [1024];
628 struct dependency * dep;
629 struct condition ** cond_ptr;
630 int first = 1;
632 cond_ptr = &(cfg->cond);
633 for ( dep = cfg->depend; dep; dep = dep->next )
635 if ( token == token_dep_tristate
636 && ! strcmp( dep->name, "CONSTANT_M" ) )
638 continue;
640 if ( first )
642 first = 0;
644 else
646 *cond_ptr = malloc( sizeof(struct condition) );
647 memset( *cond_ptr, 0, sizeof(struct condition) );
648 (*cond_ptr)->op = op_and;
649 cond_ptr = &(*cond_ptr)->next;
651 *cond_ptr = malloc( sizeof(struct condition) );
652 memset( *cond_ptr, 0, sizeof(struct condition) );
653 (*cond_ptr)->op = op_lparen;
654 if ( token == token_dep_bool )
655 sprintf( fake_if, "[ \"$%s\" = \"y\" -o \"$%s\" = \"\" ]; then",
656 dep->name, dep->name );
657 else
658 sprintf( fake_if, "[ \"$%s\" = \"y\" -o \"$%s\" = \"m\" -o \"$%s\" = \"\" ]; then",
659 dep->name, dep->name, dep->name );
660 (*cond_ptr)->next = tokenize_if( fake_if );
661 while ( *cond_ptr )
662 cond_ptr = &(*cond_ptr)->next;
663 *cond_ptr = malloc( sizeof(struct condition) );
664 memset( *cond_ptr, 0, sizeof(struct condition) );
665 (*cond_ptr)->op = op_rparen;
666 cond_ptr = &(*cond_ptr)->next;
669 break;
671 case token_else:
672 case token_endmenu:
673 case token_fi:
674 break;
676 case token_hex:
677 case token_int:
678 pnt = get_qstring ( pnt, &cfg->label );
679 pnt = get_string ( pnt, &buffer );
680 cfg->nameindex = get_varnum( buffer );
681 pnt = get_string ( pnt, &cfg->value );
682 break;
684 case token_string:
685 pnt = get_qstring ( pnt, &cfg->label );
686 pnt = get_string ( pnt, &buffer );
687 cfg->nameindex = get_varnum( buffer );
688 pnt = get_qnqstring ( pnt, &cfg->value );
689 if (cfg->value == NULL)
690 syntax_error( "missing initial value" );
691 break;
693 case token_if:
694 cfg->cond = tokenize_if( pnt );
695 break;
697 case token_mainmenu_name:
698 pnt = get_qstring( pnt, &cfg->label );
699 break;
701 case token_mainmenu_option:
702 if ( strncmp( pnt, "next_comment", 12 ) == 0 )
703 last_menuoption = cfg;
704 else
705 pnt = get_qstring( pnt, &cfg->label );
706 break;
708 case token_unset:
709 pnt = get_string( pnt, &buffer );
710 cfg->nameindex = get_varnum( buffer );
711 while ( *pnt == ' ' || *pnt == '\t' )
712 pnt++;
713 while (*pnt)
715 cfg->next = (struct kconfig *) malloc( sizeof(struct kconfig) );
716 memset( cfg->next, 0, sizeof(struct kconfig) );
717 cfg = cfg->next;
718 cfg->token = token_unset;
719 pnt = get_string( pnt, &buffer );
720 cfg->nameindex = get_varnum( buffer );
721 while ( *pnt == ' ' || *pnt == '\t' )
722 pnt++;
724 break;
726 return;
732 * Implement the "source" command.
734 static void do_source( const char * filename )
736 char buffer [2048];
737 FILE * infile;
738 const char * old_file;
739 int old_lineno;
740 int offset;
742 /* open the file */
743 if ( strcmp( filename, "-" ) == 0 )
744 infile = stdin;
745 else
746 infile = fopen( filename, "r" );
748 /* if that failed, try ../filename */
749 if ( infile == NULL )
751 sprintf( buffer, "../%s", filename );
752 infile = fopen( buffer, "r" );
755 if ( infile == NULL )
757 sprintf( buffer, "unable to open %s", filename );
758 syntax_error( buffer );
761 /* push the new file name and line number */
762 old_file = current_file;
763 old_lineno = lineno;
764 current_file = filename;
765 lineno = 0;
767 /* read and process lines */
768 for ( offset = 0; ; )
770 char * pnt;
772 /* read a line */
773 fgets( buffer + offset, sizeof(buffer) - offset, infile );
774 if ( feof( infile ) )
775 break;
776 lineno++;
778 /* strip the trailing return character */
779 pnt = buffer + strlen(buffer) - 1;
780 if ( *pnt == '\n' )
781 *pnt-- = '\0';
783 /* eat \ NL pairs */
784 if ( *pnt == '\\' )
786 offset = pnt - buffer;
787 continue;
790 /* tokenize this line */
791 tokenize_line( buffer );
792 offset = 0;
795 /* that's all, folks */
796 if ( infile != stdin )
797 fclose( infile );
798 current_file = old_file;
799 lineno = old_lineno;
800 return;
806 * Main program.
808 int main( int argc, const char * argv [] )
810 do_source ( "-" );
811 fix_conditionals ( config_list );
812 dump_tk_script ( config_list );
813 return 0;