Ok. I didn't make 2.4.0 in 2000. Tough. I tried, but we had some
[davej-history.git] / scripts / tkparse.c
blob241640f23cf37a1d4a28a8e53c33a7a998d68289
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 int default_checked = 0;
330 for ( ; ; )
332 struct kconfig * cfg;
333 char * buffer = malloc( 64 );
335 /* skip whitespace */
336 while ( *pnt == ' ' || *pnt == '\t' )
337 pnt++;
338 if ( *pnt == '\0' )
339 return pnt;
341 /* allocate a new kconfig line */
342 cfg = malloc( sizeof(*cfg) );
343 memset( cfg, 0, sizeof(*cfg) );
344 if ( config_last == NULL )
345 { config_last = config_list = cfg; }
346 else
347 { config_last->next = cfg; config_last = cfg; }
349 /* fill out the line */
350 cfg->token = token_choice_item;
351 cfg->cfg_parent = cfg_choose;
352 pnt = get_string( pnt, &cfg->label );
353 if ( ! default_checked &&
354 ! strncmp( cfg->label, cfg_choose->value, strlen( cfg_choose->value ) ) )
356 default_checked = 1;
357 free( cfg_choose->value );
358 cfg_choose->value = cfg->label;
360 while ( *pnt == ' ' || *pnt == '\t' )
361 pnt++;
362 pnt = get_string( pnt, &buffer );
363 cfg->nameindex = get_varnum( buffer );
365 if ( ! default_checked )
366 syntax_error( "bad 'choice' default value" );
367 return pnt;
373 * Tokenize one line.
375 static void tokenize_line( const char * pnt )
377 static struct kconfig * last_menuoption = NULL;
378 enum e_token token;
379 struct kconfig * cfg;
380 struct dependency ** dep_ptr;
381 char * buffer = malloc( 64 );
383 /* skip white space */
384 while ( *pnt == ' ' || *pnt == '\t' )
385 pnt++;
388 * categorize the next token
391 #define match_token(t, s) \
392 if (strncmp(pnt, s, strlen(s)) == 0) { token = t; pnt += strlen(s); break; }
394 token = token_UNKNOWN;
395 switch ( *pnt )
397 default:
398 break;
400 case '#':
401 case '\0':
402 return;
404 case 'b':
405 match_token( token_bool, "bool" );
406 break;
408 case 'c':
409 match_token( token_choice_header, "choice" );
410 match_token( token_comment, "comment" );
411 break;
413 case 'd':
414 match_token( token_define_bool, "define_bool" );
415 match_token( token_define_hex, "define_hex" );
416 match_token( token_define_int, "define_int" );
417 match_token( token_define_string, "define_string" );
418 match_token( token_define_tristate, "define_tristate" );
419 match_token( token_dep_bool, "dep_bool" );
420 match_token( token_dep_mbool, "dep_mbool" );
421 match_token( token_dep_tristate, "dep_tristate" );
422 break;
424 case 'e':
425 match_token( token_else, "else" );
426 match_token( token_endmenu, "endmenu" );
427 break;
429 case 'f':
430 match_token( token_fi, "fi" );
431 break;
433 case 'h':
434 match_token( token_hex, "hex" );
435 break;
437 case 'i':
438 match_token( token_if, "if" );
439 match_token( token_int, "int" );
440 break;
442 case 'm':
443 match_token( token_mainmenu_name, "mainmenu_name" );
444 match_token( token_mainmenu_option, "mainmenu_option" );
445 break;
447 case 's':
448 match_token( token_source, "source" );
449 match_token( token_string, "string" );
450 break;
452 case 't':
453 match_token( token_then, "then" );
454 match_token( token_tristate, "tristate" );
455 break;
457 case 'u':
458 match_token( token_unset, "unset" );
459 break;
462 #undef match_token
464 if ( token == token_source )
466 while ( *pnt == ' ' || *pnt == '\t' )
467 pnt++;
468 do_source( pnt );
469 return;
472 if ( token == token_then )
474 if ( config_last != NULL && config_last->token == token_if )
475 return;
476 syntax_error( "bogus 'then'" );
479 #if 0
480 if ( token == token_unset )
482 fprintf( stderr, "Ignoring 'unset' command\n" );
483 return;
485 #endif
487 if ( token == token_UNKNOWN )
488 syntax_error( "unknown command" );
491 * Allocate an item.
493 cfg = malloc( sizeof(*cfg) );
494 memset( cfg, 0, sizeof(*cfg) );
495 if ( config_last == NULL )
496 { config_last = config_list = cfg; }
497 else
498 { config_last->next = cfg; config_last = cfg; }
501 * Tokenize the arguments.
503 while ( *pnt == ' ' || *pnt == '\t' )
504 pnt++;
506 cfg->token = token;
507 switch ( token )
509 default:
510 syntax_error( "unknown token" );
512 case token_bool:
513 case token_tristate:
514 pnt = get_qstring ( pnt, &cfg->label );
515 pnt = get_string ( pnt, &buffer );
516 cfg->nameindex = get_varnum( buffer );
517 break;
519 case token_choice_header:
521 static int choose_number = 0;
522 char * choice_list;
524 pnt = get_qstring ( pnt, &cfg->label );
525 pnt = get_qstring ( pnt, &choice_list );
526 pnt = get_string ( pnt, &cfg->value );
527 cfg->nameindex = -(choose_number++);
528 tokenize_choices( cfg, choice_list );
529 free( choice_list );
531 break;
533 case token_comment:
534 pnt = get_qstring(pnt, &cfg->label);
535 if ( last_menuoption != NULL )
537 pnt = get_qstring(pnt, &cfg->label);
538 if (cfg->label == NULL)
539 syntax_error( "missing comment text" );
540 last_menuoption->label = cfg->label;
541 last_menuoption = NULL;
543 break;
545 case token_define_bool:
546 case token_define_tristate:
547 pnt = get_string( pnt, &buffer );
548 cfg->nameindex = get_varnum( buffer );
549 while ( *pnt == ' ' || *pnt == '\t' )
550 pnt++;
551 if ( ( pnt[0] == 'Y' || pnt[0] == 'M' || pnt[0] == 'N'
552 || pnt[0] == 'y' || pnt[0] == 'm' || pnt[0] == 'n' )
553 && ( pnt[1] == '\0' || pnt[1] == ' ' || pnt[1] == '\t' ) )
555 if ( *pnt == 'n' || *pnt == 'N' ) cfg->value = strdup( "CONSTANT_N" );
556 else if ( *pnt == 'y' || *pnt == 'Y' ) cfg->value = strdup( "CONSTANT_Y" );
557 else if ( *pnt == 'm' || *pnt == 'M' ) cfg->value = strdup( "CONSTANT_M" );
559 else if ( *pnt == '$' )
561 pnt++;
562 pnt = get_string( pnt, &cfg->value );
564 else
566 syntax_error( "unknown define_bool value" );
568 get_varnum( cfg->value );
569 break;
571 case token_define_hex:
572 case token_define_int:
573 pnt = get_string( pnt, &buffer );
574 cfg->nameindex = get_varnum( buffer );
575 pnt = get_string( pnt, &cfg->value );
576 break;
578 case token_define_string:
579 pnt = get_string( pnt, &buffer );
580 cfg->nameindex = get_varnum( buffer );
581 pnt = get_qnqstring( pnt, &cfg->value );
582 if (cfg->value == NULL)
583 syntax_error( "missing value" );
584 break;
586 case token_dep_bool:
587 case token_dep_mbool:
588 case token_dep_tristate:
589 pnt = get_qstring ( pnt, &cfg->label );
590 pnt = get_string ( pnt, &buffer );
591 cfg->nameindex = get_varnum( buffer );
593 while ( *pnt == ' ' || *pnt == '\t' )
594 pnt++;
596 dep_ptr = &(cfg->depend);
598 do {
599 *dep_ptr = (struct dependency *) malloc( sizeof( struct dependency ) );
600 (*dep_ptr)->next = NULL;
602 if ( ( pnt[0] == 'Y' || pnt[0] == 'M' || pnt[0] == 'N'
603 || pnt[0] == 'y' || pnt[0] == 'm' || pnt[0] == 'n' )
604 && ( pnt[1] == '\0' || pnt[1] == ' ' || pnt[1] == '\t' ) )
606 /* dep_tristate 'foo' CONFIG_FOO m */
607 if ( pnt[0] == 'Y' || pnt[0] == 'y' )
608 (*dep_ptr)->name = strdup( "CONSTANT_Y" );
609 else if ( pnt[0] == 'N' || pnt[0] == 'n' )
610 (*dep_ptr)->name = strdup( "CONSTANT_N" );
611 else
612 (*dep_ptr)->name = strdup( "CONSTANT_M" );
613 pnt++;
614 get_varnum( (*dep_ptr)->name );
616 else if ( *pnt == '$' )
618 pnt++;
619 pnt = get_string( pnt, &(*dep_ptr)->name );
620 get_varnum( (*dep_ptr)->name );
622 else
624 syntax_error( "can't handle dep_bool/dep_mbool/dep_tristate condition" );
626 dep_ptr = &(*dep_ptr)->next;
627 while ( *pnt == ' ' || *pnt == '\t' )
628 pnt++;
629 } while ( *pnt );
632 * Create a conditional for this object's dependencies.
635 char fake_if [1024];
636 struct dependency * dep;
637 struct condition ** cond_ptr;
638 int first = 1;
640 cond_ptr = &(cfg->cond);
641 for ( dep = cfg->depend; dep; dep = dep->next )
643 if ( token == token_dep_tristate
644 && ! strcmp( dep->name, "CONSTANT_M" ) )
646 continue;
648 if ( first )
650 first = 0;
652 else
654 *cond_ptr = malloc( sizeof(struct condition) );
655 memset( *cond_ptr, 0, sizeof(struct condition) );
656 (*cond_ptr)->op = op_and;
657 cond_ptr = &(*cond_ptr)->next;
659 *cond_ptr = malloc( sizeof(struct condition) );
660 memset( *cond_ptr, 0, sizeof(struct condition) );
661 (*cond_ptr)->op = op_lparen;
662 if ( token == token_dep_bool )
663 sprintf( fake_if, "[ \"$%s\" = \"y\" -o \"$%s\" = \"\" ]; then",
664 dep->name, dep->name );
665 else
666 sprintf( fake_if, "[ \"$%s\" = \"y\" -o \"$%s\" = \"m\" -o \"$%s\" = \"\" ]; then",
667 dep->name, dep->name, dep->name );
668 (*cond_ptr)->next = tokenize_if( fake_if );
669 while ( *cond_ptr )
670 cond_ptr = &(*cond_ptr)->next;
671 *cond_ptr = malloc( sizeof(struct condition) );
672 memset( *cond_ptr, 0, sizeof(struct condition) );
673 (*cond_ptr)->op = op_rparen;
674 cond_ptr = &(*cond_ptr)->next;
677 break;
679 case token_else:
680 case token_endmenu:
681 case token_fi:
682 break;
684 case token_hex:
685 case token_int:
686 pnt = get_qstring ( pnt, &cfg->label );
687 pnt = get_string ( pnt, &buffer );
688 cfg->nameindex = get_varnum( buffer );
689 pnt = get_string ( pnt, &cfg->value );
690 break;
692 case token_string:
693 pnt = get_qstring ( pnt, &cfg->label );
694 pnt = get_string ( pnt, &buffer );
695 cfg->nameindex = get_varnum( buffer );
696 pnt = get_qnqstring ( pnt, &cfg->value );
697 if (cfg->value == NULL)
698 syntax_error( "missing initial value" );
699 break;
701 case token_if:
702 cfg->cond = tokenize_if( pnt );
703 break;
705 case token_mainmenu_name:
706 pnt = get_qstring( pnt, &cfg->label );
707 break;
709 case token_mainmenu_option:
710 if ( strncmp( pnt, "next_comment", 12 ) == 0 )
711 last_menuoption = cfg;
712 else
713 pnt = get_qstring( pnt, &cfg->label );
714 break;
716 case token_unset:
717 pnt = get_string( pnt, &buffer );
718 cfg->nameindex = get_varnum( buffer );
719 while ( *pnt == ' ' || *pnt == '\t' )
720 pnt++;
721 while (*pnt)
723 cfg->next = (struct kconfig *) malloc( sizeof(struct kconfig) );
724 memset( cfg->next, 0, sizeof(struct kconfig) );
725 cfg = cfg->next;
726 cfg->token = token_unset;
727 pnt = get_string( pnt, &buffer );
728 cfg->nameindex = get_varnum( buffer );
729 while ( *pnt == ' ' || *pnt == '\t' )
730 pnt++;
732 break;
734 return;
740 * Implement the "source" command.
742 static void do_source( const char * filename )
744 char buffer [2048];
745 FILE * infile;
746 const char * old_file;
747 int old_lineno;
748 int offset;
750 /* open the file */
751 if ( strcmp( filename, "-" ) == 0 )
752 infile = stdin;
753 else
754 infile = fopen( filename, "r" );
756 /* if that failed, try ../filename */
757 if ( infile == NULL )
759 sprintf( buffer, "../%s", filename );
760 infile = fopen( buffer, "r" );
763 if ( infile == NULL )
765 sprintf( buffer, "unable to open %s", filename );
766 syntax_error( buffer );
769 /* push the new file name and line number */
770 old_file = current_file;
771 old_lineno = lineno;
772 current_file = filename;
773 lineno = 0;
775 /* read and process lines */
776 for ( offset = 0; ; )
778 char * pnt;
780 /* read a line */
781 fgets( buffer + offset, sizeof(buffer) - offset, infile );
782 if ( feof( infile ) )
783 break;
784 lineno++;
786 /* strip the trailing return character */
787 pnt = buffer + strlen(buffer) - 1;
788 if ( *pnt == '\n' )
789 *pnt-- = '\0';
791 /* eat \ NL pairs */
792 if ( *pnt == '\\' )
794 offset = pnt - buffer;
795 continue;
798 /* tokenize this line */
799 tokenize_line( buffer );
800 offset = 0;
803 /* that's all, folks */
804 if ( infile != stdin )
805 fclose( infile );
806 current_file = old_file;
807 lineno = old_lineno;
808 return;
814 * Main program.
816 int main( int argc, const char * argv [] )
818 do_source ( "-" );
819 fix_conditionals ( config_list );
820 dump_tk_script ( config_list );
821 return 0;