1/* 2 * tkparse.c 3 * 4 * Eric Youngdale was the original author of xconfig. 5 * Michael Elizabeth Chastain (mec@shout.net) is the current maintainer. 6 * 7 * Parse a config.in file and translate it to a wish script. 8 * This task has three parts: 9 * 10 * tkparse.c tokenize the input 11 * tkcond.c transform 'if ...' statements 12 * tkgen.c generate output 13 * 14 * Change History 15 * 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. 20 * 21 * 14 January 1999, Michael Elizabeth Chastain, <mec@shout.net> 22 * - Don't fclose stdin. Thanks to Tony Hoyle for nailing this one. 23 * 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. 28 * 29 * 23 January 1999, Michael Elizabeth Chastain, <mec@shout.net> 30 * - Remove bug-compatible code. 31 * 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, 42 * 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. 46 */ 47 48#include <stdio.h> 49#include <stdlib.h> 50#include <string.h> 51 52#include "tkparse.h" 53 54static struct kconfig * config_list = NULL; 55static struct kconfig * config_last = NULL; 56static const char * current_file = "<unknown file>"; 57static int lineno = 0; 58 59static void do_source( const char * ); 60 61#undef strcmp 62int my_strcmp( const char * s1, const char * s2 ) { return strcmp( s1, s2 ); } 63#define strcmp my_strcmp 64 65/* 66 * Report a syntax error. 67 */ 68static void syntax_error( const char * msg ) 69{ 70 fprintf( stderr, "%s: %d: %s\n", current_file, lineno, msg ); 71 exit( 1 ); 72} 73 74 75 76/* 77 * Find index of a specific variable in the symbol table. 78 * Create a new entry if it does not exist yet. 79 */ 80struct variable *vartable; 81int max_varnum = 0; 82static int vartable_size = 0; 83 84int get_varnum( char * name ) 85{ 86 int i; 87 88 for ( i = 1; i <= max_varnum; i++ ) 89 if ( strcmp( vartable[i].name, name ) == 0 ) 90 return i; 91 while (max_varnum+1 >= vartable_size) { 92 vartable = realloc(vartable, (vartable_size += 1000)*sizeof(*vartable)); 93 if (!vartable) { 94 fprintf(stderr, "tkparse realloc vartable failed\n"); 95 exit(1); 96 } 97 } 98 vartable[++max_varnum].name = malloc( strlen( name )+1 ); 99 strcpy( vartable[max_varnum].name, name ); 100 return max_varnum; 101} 102 103 104 105/* 106 * Get a string. 107 */ 108static const char * get_string( const char * pnt, char ** label ) 109{ 110 const char * word; 111 112 word = pnt; 113 for ( ; ; ) 114 { 115 if ( *pnt == '\0' || *pnt == ' ' || *pnt == '\t' ) 116 break; 117 pnt++; 118 } 119 120 *label = malloc( pnt - word + 1 ); 121 memcpy( *label, word, pnt - word ); 122 (*label)[pnt - word] = '\0'; 123 124 if ( *pnt != '\0' ) 125 pnt++; 126 return pnt; 127} 128 129 130 131/* 132 * Get a quoted string. 133 * Insert a '\' before any characters that need quoting. 134 */ 135static const char * get_qstring( const char * pnt, char ** label ) 136{ 137 char quote_char; 138 char newlabel [2048]; 139 char * pnt1; 140 141 /* advance to the open quote */ 142 for ( ; ; ) 143 { 144 if ( *pnt == '\0' ) 145 return pnt; 146 quote_char = *pnt++; 147 if ( quote_char == '"' || quote_char == '\'' ) 148 break; 149 } 150 151 /* copy into an intermediate buffer */ 152 pnt1 = newlabel; 153 for ( ; ; ) 154 { 155 if ( *pnt == '\0' ) 156 syntax_error( "unterminated quoted string" ); 157 if ( *pnt == quote_char && pnt[-1] != '\\' ) 158 break; 159 160 /* copy the character, quoting if needed */ 161 if ( *pnt == '"' || *pnt == '\'' || *pnt == '[' || *pnt == ']' ) 162 *pnt1++ = '\\'; 163 *pnt1++ = *pnt++; 164 } 165 166 /* copy the label into a permanent location */ 167 *pnt1++ = '\0'; 168 *label = (char *) malloc( pnt1 - newlabel ); 169 memcpy( *label, newlabel, pnt1 - newlabel ); 170 171 /* skip over last quote and next whitespace */ 172 pnt++; 173 while ( *pnt == ' ' || *pnt == '\t' ) 174 pnt++; 175 return pnt; 176} 177 178 179 180/* 181 * Get a quoted or unquoted string. It is recognized by the first 182 * non-white character. '"' and '"' are not allowed inside the string. 183 */ 184static const char * get_qnqstring( const char * pnt, char ** label ) 185{ 186 char quote_char; 187 188 while ( *pnt == ' ' || *pnt == '\t' ) 189 pnt++; 190 191 if ( *pnt == '\0' ) 192 return pnt; 193 quote_char = *pnt; 194 if ( quote_char == '"' || quote_char == '\'' ) 195 return get_qstring( pnt, label ); 196 else 197 return get_string( pnt, label ); 198} 199 200 201 202/* 203 * Tokenize an 'if' statement condition. 204 */ 205static struct condition * tokenize_if( const char * pnt ) 206{ 207 struct condition * list; 208 struct condition * last; 209 struct condition * prev; 210 211 /* eat the open bracket */ 212 while ( *pnt == ' ' || *pnt == '\t' ) 213 pnt++; 214 if ( *pnt != '[' ) 215 syntax_error( "bad 'if' condition" ); 216 pnt++; 217 218 list = last = NULL; 219 for ( ; ; ) 220 { 221 struct condition * cond; 222 223 /* advance to the next token */ 224 while ( *pnt == ' ' || *pnt == '\t' ) 225 pnt++; 226 if ( *pnt == '\0' ) 227 syntax_error( "unterminated 'if' condition" ); 228 if ( *pnt == ']' ) 229 return list; 230 231 /* allocate a new token */ 232 cond = malloc( sizeof(*cond) ); 233 memset( cond, 0, sizeof(*cond) ); 234 if ( last == NULL ) 235 { list = last = cond; prev = NULL; } 236 else 237 { prev = last; last->next = cond; last = cond; } 238 239 /* determine the token value */ 240 if ( *pnt == '-' && pnt[1] == 'a' ) 241 { 242 if ( ! prev || ( prev->op != op_variable && prev->op != op_constant ) ) 243 syntax_error( "incorrect argument" ); 244 cond->op = op_and; pnt += 2; continue; 245 } 246 247 if ( *pnt == '-' && pnt[1] == 'o' ) 248 { 249 if ( ! prev || ( prev->op != op_variable && prev->op != op_constant ) ) 250 syntax_error( "incorrect argument" ); 251 cond->op = op_or; pnt += 2; continue; 252 } 253 254 if ( *pnt == '!' && pnt[1] == '=' ) 255 { 256 if ( ! prev || ( prev->op != op_variable && prev->op != op_constant ) ) 257 syntax_error( "incorrect argument" ); 258 cond->op = op_neq; pnt += 2; continue; 259 } 260 261 if ( *pnt == '=' ) 262 { 263 if ( ! prev || ( prev->op != op_variable && prev->op != op_constant ) ) 264 syntax_error( "incorrect argument" ); 265 cond->op = op_eq; pnt += 1; continue; 266 } 267 268 if ( *pnt == '!' ) 269 { 270 if ( prev && ( prev->op != op_and && prev->op != op_or 271 && prev->op != op_bang ) ) 272 syntax_error( "incorrect argument" ); 273 cond->op = op_bang; pnt += 1; continue; 274 } 275 276 if ( *pnt == '"' ) 277 { 278 const char * word; 279 280 if ( prev && ( prev->op == op_variable || prev->op == op_constant ) ) 281 syntax_error( "incorrect argument" ); 282 /* advance to the word */ 283 pnt++; 284 if ( *pnt == '$' ) 285 { cond->op = op_variable; pnt++; } 286 else 287 { cond->op = op_constant; } 288 289 /* find the end of the word */ 290 word = pnt; 291 for ( ; ; ) 292 { 293 if ( *pnt == '\0' ) 294 syntax_error( "unterminated double quote" ); 295 if ( *pnt == '"' ) 296 break; 297 pnt++; 298 } 299 300 /* store a copy of this word */ 301 { 302 char * str = malloc( pnt - word + 1 ); 303 memcpy( str, word, pnt - word ); 304 str [pnt - word] = '\0'; 305 if ( cond->op == op_variable ) 306 { 307 cond->nameindex = get_varnum( str ); 308 free( str ); 309 } 310 else /* op_constant */ 311 { 312 cond->str = str; 313 } 314 } 315 316 pnt++; 317 continue; 318 } 319 320 /* unknown token */ 321 syntax_error( "bad if condition" ); 322 } 323} 324 325 326 327/* 328 * Tokenize a choice list. Choices appear as pairs of strings; 329 * note that I am parsing *inside* the double quotes. Ugh. 330 */ 331static const char * tokenize_choices( struct kconfig * cfg_choose, 332 const char * pnt ) 333{ 334 int default_checked = 0; 335 for ( ; ; ) 336 { 337 struct kconfig * cfg; 338 char * buffer = malloc( 64 ); 339 340 /* skip whitespace */ 341 while ( *pnt == ' ' || *pnt == '\t' ) 342 pnt++; 343 if ( *pnt == '\0' ) 344 return pnt; 345 346 /* allocate a new kconfig line */ 347 cfg = malloc( sizeof(*cfg) ); 348 memset( cfg, 0, sizeof(*cfg) ); 349 if ( config_last == NULL ) 350 { config_last = config_list = cfg; } 351 else 352 { config_last->next = cfg; config_last = cfg; } 353 354 /* fill out the line */ 355 cfg->token = token_choice_item; 356 cfg->cfg_parent = cfg_choose; 357 pnt = get_string( pnt, &cfg->label ); 358 if ( ! default_checked && 359 ! strncmp( cfg->label, cfg_choose->value, strlen( cfg_choose->value ) ) ) 360 { 361 default_checked = 1; 362 free( cfg_choose->value ); 363 cfg_choose->value = cfg->label; 364 } 365 while ( *pnt == ' ' || *pnt == '\t' ) 366 pnt++; 367 pnt = get_string( pnt, &buffer ); 368 cfg->nameindex = get_varnum( buffer ); 369 } 370 if ( ! default_checked ) 371 syntax_error( "bad 'choice' default value" ); 372 return pnt; 373} 374 375 376 377/* 378 * Tokenize one line. 379 */ 380static void tokenize_line( const char * pnt ) 381{ 382 static struct kconfig * last_menuoption = NULL; 383 enum e_token token; 384 struct kconfig * cfg; 385 struct dependency ** dep_ptr; 386 char * buffer = malloc( 64 ); 387 388 /* skip white space */ 389 while ( *pnt == ' ' || *pnt == '\t' ) 390 pnt++; 391 392 /* 393 * categorize the next token 394 */ 395 396#define match_token(t, s) \ 397 if (strncmp(pnt, s, strlen(s)) == 0) { token = t; pnt += strlen(s); break; } 398 399 token = token_UNKNOWN; 400 switch ( *pnt ) 401 { 402 default: 403 break; 404 405 case '#': 406 case '\0': 407 return; 408 409 case 'b': 410 match_token( token_bool, "bool" ); 411 break; 412 413 case 'c': 414 match_token( token_choice_header, "choice" ); 415 match_token( token_comment, "comment" ); 416 break; 417 418 case 'd': 419 match_token( token_define_bool, "define_bool" ); 420 match_token( token_define_hex, "define_hex" ); 421 match_token( token_define_int, "define_int" ); 422 match_token( token_define_string, "define_string" ); 423 match_token( token_define_tristate, "define_tristate" ); 424 match_token( token_dep_bool, "dep_bool" ); 425 match_token( token_dep_mbool, "dep_mbool" ); 426 match_token( token_dep_tristate, "dep_tristate" ); 427 break; 428 429 case 'e': 430 match_token( token_else, "else" ); 431 match_token( token_endmenu, "endmenu" ); 432 break; 433 434 case 'f': 435 match_token( token_fi, "fi" ); 436 break; 437 438 case 'h': 439 match_token( token_hex, "hex" ); 440 break; 441 442 case 'i': 443 match_token( token_if, "if" ); 444 match_token( token_int, "int" ); 445 break; 446 447 case 'm': 448 match_token( token_mainmenu_name, "mainmenu_name" ); 449 match_token( token_mainmenu_option, "mainmenu_option" ); 450 break; 451 452 case 's': 453 match_token( token_source, "source" ); 454 match_token( token_string, "string" ); 455 break; 456 457 case 't': 458 match_token( token_then, "then" ); 459 match_token( token_tristate, "tristate" ); 460 break; 461 462 case 'u': 463 match_token( token_unset, "unset" ); 464 break; 465 } 466 467#undef match_token 468 469 if ( token == token_source ) 470 { 471 while ( *pnt == ' ' || *pnt == '\t' ) 472 pnt++; 473 do_source( pnt ); 474 return; 475 } 476 477 if ( token == token_then ) 478 { 479 if ( config_last != NULL && config_last->token == token_if ) 480 return; 481 syntax_error( "bogus 'then'" ); 482 } 483 484 485 if ( token == token_UNKNOWN ) 486 syntax_error( "unknown command" ); 487 488 /* 489 * Allocate an item. 490 */ 491 cfg = malloc( sizeof(*cfg) ); 492 memset( cfg, 0, sizeof(*cfg) ); 493 if ( config_last == NULL ) 494 { config_last = config_list = cfg; } 495 else 496 { config_last->next = cfg; config_last = cfg; } 497 498 /* 499 * Tokenize the arguments. 500 */ 501 while ( *pnt == ' ' || *pnt == '\t' ) 502 pnt++; 503 504 cfg->token = token; 505 switch ( token ) 506 { 507 default: 508 syntax_error( "unknown token" ); 509 510 case token_bool: 511 case token_tristate: 512 pnt = get_qstring ( pnt, &cfg->label ); 513 pnt = get_string ( pnt, &buffer ); 514 cfg->nameindex = get_varnum( buffer ); 515 break; 516 517 case token_choice_header: 518 { 519 static int choose_number = 0; 520 char * choice_list; 521 522 pnt = get_qstring ( pnt, &cfg->label ); 523 pnt = get_qstring ( pnt, &choice_list ); 524 pnt = get_string ( pnt, &cfg->value ); 525 cfg->nameindex = -(choose_number++); 526 tokenize_choices( cfg, choice_list ); 527 free( choice_list ); 528 } 529 break; 530 531 case token_comment: 532 pnt = get_qstring(pnt, &cfg->label); 533 if ( last_menuoption != NULL ) 534 { 535 pnt = get_qstring(pnt, &cfg->label); 536 if (cfg->label == NULL) 537 syntax_error( "missing comment text" ); 538 last_menuoption->label = cfg->label; 539 last_menuoption = NULL; 540 } 541 break; 542 543 case token_define_bool: 544 case token_define_tristate: 545 pnt = get_string( pnt, &buffer ); 546 cfg->nameindex = get_varnum( buffer ); 547 while ( *pnt == ' ' || *pnt == '\t' ) 548 pnt++; 549 if ( ( pnt[0] == 'Y' || pnt[0] == 'M' || pnt[0] == 'N' 550 || pnt[0] == 'y' || pnt[0] == 'm' || pnt[0] == 'n' ) 551 && ( pnt[1] == '\0' || pnt[1] == ' ' || pnt[1] == '\t' ) ) 552 { 553 if ( *pnt == 'n' || *pnt == 'N' ) cfg->value = strdup( "CONSTANT_N" ); 554 else if ( *pnt == 'y' || *pnt == 'Y' ) cfg->value = strdup( "CONSTANT_Y" ); 555 else if ( *pnt == 'm' || *pnt == 'M' ) cfg->value = strdup( "CONSTANT_M" ); 556 } 557 else if ( *pnt == '$' ) 558 { 559 pnt++; 560 pnt = get_string( pnt, &cfg->value ); 561 } 562 else 563 { 564 syntax_error( "unknown define_bool value" ); 565 } 566 get_varnum( cfg->value ); 567 break; 568 569 case token_define_hex: 570 case token_define_int: 571 pnt = get_string( pnt, &buffer ); 572 cfg->nameindex = get_varnum( buffer ); 573 pnt = get_string( pnt, &cfg->value ); 574 break; 575 576 case token_define_string: 577 pnt = get_string( pnt, &buffer ); 578 cfg->nameindex = get_varnum( buffer ); 579 pnt = get_qnqstring( pnt, &cfg->value ); 580 if (cfg->value == NULL) 581 syntax_error( "missing value" ); 582 break; 583 584 case token_dep_bool: 585 case token_dep_mbool: 586 case token_dep_tristate: 587 pnt = get_qstring ( pnt, &cfg->label ); 588 pnt = get_string ( pnt, &buffer ); 589 cfg->nameindex = get_varnum( buffer ); 590 591 while ( *pnt == ' ' || *pnt == '\t' ) 592 pnt++; 593 594 dep_ptr = &(cfg->depend); 595 596 do { 597 *dep_ptr = (struct dependency *) malloc( sizeof( struct dependency ) ); 598 (*dep_ptr)->next = NULL; 599 600 if ( ( pnt[0] == 'Y' || pnt[0] == 'M' || pnt[0] == 'N' 601 || pnt[0] == 'y' || pnt[0] == 'm' || pnt[0] == 'n' ) 602 && ( pnt[1] == '\0' || pnt[1] == ' ' || pnt[1] == '\t' ) ) 603 { 604 /* dep_tristate 'foo' CONFIG_FOO m */ 605 if ( pnt[0] == 'Y' || pnt[0] == 'y' ) 606 (*dep_ptr)->name = strdup( "CONSTANT_Y" ); 607 else if ( pnt[0] == 'N' || pnt[0] == 'n' ) 608 (*dep_ptr)->name = strdup( "CONSTANT_N" ); 609 else 610 (*dep_ptr)->name = strdup( "CONSTANT_M" ); 611 pnt++; 612 get_varnum( (*dep_ptr)->name ); 613 } 614 else if ( *pnt == '$' ) 615 { 616 pnt++; 617 pnt = get_string( pnt, &(*dep_ptr)->name ); 618 get_varnum( (*dep_ptr)->name ); 619 } 620 else 621 { 622 syntax_error( "can't handle dep_bool/dep_mbool/dep_tristate condition" ); 623 } 624 dep_ptr = &(*dep_ptr)->next; 625 while ( *pnt == ' ' || *pnt == '\t' ) 626 pnt++; 627 } while ( *pnt ); 628 629 /* 630 * Create a conditional for this object's dependencies. 631 */ 632 { 633 char fake_if [1024]; 634 struct dependency * dep; 635 struct condition ** cond_ptr; 636 int first = 1; 637 638 cond_ptr = &(cfg->cond); 639 for ( dep = cfg->depend; dep; dep = dep->next ) 640 { 641 if ( token == token_dep_tristate 642 && ! strcmp( dep->name, "CONSTANT_M" ) ) 643 { 644 continue; 645 } 646 if ( first ) 647 { 648 first = 0; 649 } 650 else 651 { 652 *cond_ptr = malloc( sizeof(struct condition) ); 653 memset( *cond_ptr, 0, sizeof(struct condition) ); 654 (*cond_ptr)->op = op_and; 655 cond_ptr = &(*cond_ptr)->next; 656 } 657 *cond_ptr = malloc( sizeof(struct condition) ); 658 memset( *cond_ptr, 0, sizeof(struct condition) ); 659 (*cond_ptr)->op = op_lparen; 660 if ( token == token_dep_bool ) 661 sprintf( fake_if, "[ \"$%s\" = \"y\" -o \"$%s\" = \"\" ]; then", 662 dep->name, dep->name ); 663 else 664 sprintf( fake_if, "[ \"$%s\" = \"y\" -o \"$%s\" = \"m\" -o \"$%s\" = \"\" ]; then", 665 dep->name, dep->name, dep->name ); 666 (*cond_ptr)->next = tokenize_if( fake_if ); 667 while ( *cond_ptr ) 668 cond_ptr = &(*cond_ptr)->next; 669 *cond_ptr = malloc( sizeof(struct condition) ); 670 memset( *cond_ptr, 0, sizeof(struct condition) ); 671 (*cond_ptr)->op = op_rparen; 672 cond_ptr = &(*cond_ptr)->next; 673 } 674 } 675 break; 676 677 case token_else: 678 case token_endmenu: 679 case token_fi: 680 break; 681 682 case token_hex: 683 case token_int: 684 pnt = get_qstring ( pnt, &cfg->label ); 685 pnt = get_string ( pnt, &buffer ); 686 cfg->nameindex = get_varnum( buffer ); 687 pnt = get_string ( pnt, &cfg->value ); 688 break; 689 690 case token_string: 691 pnt = get_qstring ( pnt, &cfg->label ); 692 pnt = get_string ( pnt, &buffer ); 693 cfg->nameindex = get_varnum( buffer ); 694 pnt = get_qnqstring ( pnt, &cfg->value ); 695 if (cfg->value == NULL) 696 syntax_error( "missing initial value" ); 697 break; 698 699 case token_if: 700 cfg->cond = tokenize_if( pnt ); 701 break; 702 703 case token_mainmenu_name: 704 pnt = get_qstring( pnt, &cfg->label ); 705 break; 706 707 case token_mainmenu_option: 708 if ( strncmp( pnt, "next_comment", 12 ) == 0 ) 709 last_menuoption = cfg; 710 else 711 pnt = get_qstring( pnt, &cfg->label ); 712 break; 713 714 case token_unset: 715 pnt = get_string( pnt, &buffer ); 716 cfg->nameindex = get_varnum( buffer ); 717 while ( *pnt == ' ' || *pnt == '\t' ) 718 pnt++; 719 while (*pnt) 720 { 721 cfg->next = (struct kconfig *) malloc( sizeof(struct kconfig) ); 722 memset( cfg->next, 0, sizeof(struct kconfig) ); 723 cfg = cfg->next; 724 cfg->token = token_unset; 725 pnt = get_string( pnt, &buffer ); 726 cfg->nameindex = get_varnum( buffer ); 727 while ( *pnt == ' ' || *pnt == '\t' ) 728 pnt++; 729 } 730 break; 731 } 732 return; 733} 734 735 736 737/* 738 * Implement the "source" command. 739 */ 740static void do_source( const char * filename ) 741{ 742 char buffer [2048]; 743 FILE * infile; 744 const char * old_file; 745 int old_lineno; 746 int offset; 747 748 /* open the file */ 749 if ( strcmp( filename, "-" ) == 0 ) 750 infile = stdin; 751 else 752 infile = fopen( filename, "r" ); 753 754 /* if that failed, try ../filename */ 755 if ( infile == NULL ) 756 { 757 sprintf( buffer, "../%s", filename ); 758 infile = fopen( buffer, "r" ); 759 } 760 761 if ( infile == NULL ) 762 { 763 sprintf( buffer, "unable to open %s", filename ); 764 syntax_error( buffer ); 765 } 766 767 /* push the new file name and line number */ 768 old_file = current_file; 769 old_lineno = lineno; 770 current_file = filename; 771 lineno = 0; 772 773 /* read and process lines */ 774 for ( offset = 0; ; ) 775 { 776 char * pnt; 777 778 /* read a line */ 779 fgets( buffer + offset, sizeof(buffer) - offset, infile ); 780 if ( feof( infile ) ) 781 break; 782 lineno++; 783 784 /* strip the trailing return character */ 785 pnt = buffer + strlen(buffer) - 1; 786 if ( *pnt == '\n' ) 787 *pnt-- = '\0'; 788 789 /* eat \ NL pairs */ 790 if ( *pnt == '\\' ) 791 { 792 offset = pnt - buffer; 793 continue; 794 } 795 796 /* tokenize this line */ 797 tokenize_line( buffer ); 798 offset = 0; 799 } 800 801 /* that's all, folks */ 802 if ( infile != stdin ) 803 fclose( infile ); 804 current_file = old_file; 805 lineno = old_lineno; 806 return; 807} 808 809 810 811/* 812 * Main program. 813 */ 814int main( int argc, const char * argv [] ) 815{ 816 do_source ( "-" ); 817 fix_conditionals ( config_list ); 818 dump_tk_script ( config_list ); 819 free(vartable); 820 return 0; 821} 822