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