File: /usr/src/linux/scripts/tkparse.c

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     
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;
58     
59     static void do_source( const char * );
60     
61     #undef strcmp
62     int 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      */
68     static 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 specyfic variable in the symbol table.
78      * Create a new entry if it does not exist yet.
79      */
80     #define VARTABLE_SIZE 2048
81     struct variable vartable[VARTABLE_SIZE];
82     int max_varnum = 0;
83     
84     int 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         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;
96     }
97     
98     
99     
100     /*
101      * Get a string.
102      */
103     static const char * get_string( const char * pnt, char ** label )
104     {
105         const char * word;
106     
107         word = pnt;
108         for ( ; ; )
109         {
110     	if ( *pnt == '\0' || *pnt == ' ' || *pnt == '\t' )
111     	    break;
112     	pnt++;
113         }
114     
115         *label = malloc( pnt - word + 1 );
116         memcpy( *label, word, pnt - word );
117         (*label)[pnt - word] = '\0';
118     
119         if ( *pnt != '\0' )
120     	pnt++;
121         return pnt;
122     }
123     
124     
125     
126     /*
127      * Get a quoted string.
128      * Insert a '\' before any characters that need quoting.
129      */
130     static const char * get_qstring( const char * pnt, char ** label )
131     {
132         char quote_char;
133         char newlabel [2048];
134         char * pnt1;
135     
136         /* advance to the open quote */
137         for ( ; ; )
138         {
139     	if ( *pnt == '\0' )
140     	    return pnt;
141     	quote_char = *pnt++;
142     	if ( quote_char == '"' || quote_char == '\'' )
143     	    break;
144         }
145     
146         /* copy into an intermediate buffer */
147         pnt1 = newlabel;
148         for ( ; ; )
149         {
150     	if ( *pnt == '\0' )
151     	    syntax_error( "unterminated quoted string" );
152     	if ( *pnt == quote_char && pnt[-1] != '\\' )
153     	    break;
154     
155     	/* copy the character, quoting if needed */
156     	if ( *pnt == '"' || *pnt == '\'' || *pnt == '[' || *pnt == ']' )
157     	    *pnt1++ = '\\';
158     	*pnt1++ = *pnt++;
159         }
160     
161         /* copy the label into a permanent location */
162         *pnt1++ = '\0';
163         *label = (char *) malloc( pnt1 - newlabel );
164         memcpy( *label, newlabel, pnt1 - newlabel );
165     
166         /* skip over last quote and next whitespace */
167         pnt++;
168         while ( *pnt == ' ' || *pnt == '\t' )
169     	pnt++;
170         return pnt;
171     }
172     
173     
174     
175     /*
176      * Get a quoted or unquoted string. It is recognized by the first 
177      * non-white character. '"' and '"' are not allowed inside the string.
178      */
179     static const char * get_qnqstring( const char * pnt, char ** label )
180     {
181         char quote_char;
182     
183         while ( *pnt == ' ' || *pnt == '\t' )
184     	pnt++;
185     
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 );
193     }
194     
195     
196     
197     /*
198      * Tokenize an 'if' statement condition.
199      */
200     static struct condition * tokenize_if( const char * pnt )
201     {
202         struct condition * list;
203         struct condition * last;
204         struct condition * prev;
205     
206         /* eat the open bracket */
207         while ( *pnt == ' ' || *pnt == '\t' )
208     	pnt++;
209         if ( *pnt != '[' )
210     	syntax_error( "bad 'if' condition" );
211         pnt++;
212     
213         list = last = NULL;
214         for ( ; ; )
215         {
216     	struct condition * cond;
217     
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;
225     
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; }
233     
234     	/* determine the token value */
235     	if ( *pnt == '-' && pnt[1] == 'a' )
236     	{
237     	    if ( ! prev || ( prev->op != op_variable && prev->op != op_constant ) )
238     		syntax_error( "incorrect argument" );
239     	    cond->op = op_and;  pnt += 2; continue;
240     	}
241     
242     	if ( *pnt == '-' && pnt[1] == 'o' )
243     	{
244     	    if ( ! prev || ( prev->op != op_variable && prev->op != op_constant ) )
245     		syntax_error( "incorrect argument" );
246     	    cond->op = op_or;   pnt += 2; continue;
247     	}
248     
249     	if ( *pnt == '!' && pnt[1] == '=' )
250     	{
251     	    if ( ! prev || ( prev->op != op_variable && prev->op != op_constant ) )
252     		syntax_error( "incorrect argument" );
253     	    cond->op = op_neq;  pnt += 2; continue;
254     	}
255     
256     	if ( *pnt == '=' )
257     	{
258     	    if ( ! prev || ( prev->op != op_variable && prev->op != op_constant ) )
259     		syntax_error( "incorrect argument" );
260     	    cond->op = op_eq;   pnt += 1; continue;
261     	}
262     
263     	if ( *pnt == '!' )
264     	{
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;
269     	}
270     
271     	if ( *pnt == '"' )
272     	{
273     	    const char * word;
274     
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; }
283     
284     	    /* find the end of the word */
285     	    word = pnt;
286     	    for ( ; ; )
287     	    {
288     		if ( *pnt == '\0' )
289     		    syntax_error( "unterminated double quote" );
290     		if ( *pnt == '"' )
291     		    break;
292     		pnt++;
293     	    }
294     
295     	    /* store a copy of this word */
296     	    {
297     		char * str = malloc( pnt - word + 1 );
298     		memcpy( str, word, pnt - word );
299     		str [pnt - word] = '\0';
300     		if ( cond->op == op_variable )
301     		{
302     		    cond->nameindex = get_varnum( str );
303     		    free( str );
304     		}
305     		else /* op_constant */
306     		{
307     		    cond->str = str;
308     		}
309     	    }
310     
311     	    pnt++;
312     	    continue;
313     	}
314     
315     	/* unknown token */
316     	syntax_error( "bad if condition" );
317         }
318     }
319     
320     
321     
322     /*
323      * Tokenize a choice list.  Choices appear as pairs of strings;
324      * note that I am parsing *inside* the double quotes.  Ugh.
325      */
326     static const char * tokenize_choices( struct kconfig * cfg_choose,
327         const char * pnt )
328     {
329         int default_checked = 0;
330         for ( ; ; )
331         {
332     	struct kconfig * cfg;
333     	char * buffer = malloc( 64 );
334     
335     	/* skip whitespace */
336     	while ( *pnt == ' ' || *pnt == '\t' )
337     	    pnt++;
338     	if ( *pnt == '\0' )
339     	    return pnt;
340     
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; }
348     
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 ) ) )
355     	{
356     	    default_checked = 1;
357     	    free( cfg_choose->value );
358     	    cfg_choose->value = cfg->label;
359     	}
360     	while ( *pnt == ' ' || *pnt == '\t' )
361     	    pnt++;
362     	pnt = get_string( pnt, &buffer );
363     	cfg->nameindex = get_varnum( buffer );
364         }
365         if ( ! default_checked )
366     	syntax_error( "bad 'choice' default value" );
367         return pnt;
368     }
369     
370     
371     
372     /*
373      * Tokenize one line.
374      */
375     static void tokenize_line( const char * pnt )
376     {
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 );
382     
383         /* skip white space */
384         while ( *pnt == ' ' || *pnt == '\t' )
385     	pnt++;
386     
387         /*
388          * categorize the next token
389          */
390     
391     #define match_token(t, s) \
392         if (strncmp(pnt, s, strlen(s)) == 0) { token = t; pnt += strlen(s); break; }
393     
394         token = token_UNKNOWN;
395         switch ( *pnt )
396         {
397         default:
398     	break;
399     
400         case '#':
401         case '\0':
402     	return;
403     
404         case 'b':
405     	match_token( token_bool, "bool" );
406     	break;
407     
408         case 'c':
409     	match_token( token_choice_header, "choice"  );
410     	match_token( token_comment, "comment" );
411     	break;
412     
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;
423     
424         case 'e':
425     	match_token( token_else, "else" );
426     	match_token( token_endmenu, "endmenu" );
427     	break;
428     
429         case 'f':
430     	match_token( token_fi, "fi" );
431     	break;
432     
433         case 'h':
434     	match_token( token_hex, "hex" );
435     	break;
436     
437         case 'i':
438     	match_token( token_if, "if" );
439     	match_token( token_int, "int" );
440     	break;
441     
442         case 'm':
443     	match_token( token_mainmenu_name, "mainmenu_name" );
444     	match_token( token_mainmenu_option, "mainmenu_option" );
445     	break;
446     
447         case 's':
448     	match_token( token_source, "source" );
449     	match_token( token_string, "string" );
450     	break;
451     
452         case 't':
453     	match_token( token_then, "then" );
454     	match_token( token_tristate, "tristate" );
455     	break;
456     
457         case 'u':
458     	match_token( token_unset, "unset" );
459     	break;
460         }
461     
462     #undef match_token
463     
464         if ( token == token_source )
465         {
466     	while ( *pnt == ' ' || *pnt == '\t' )
467     	    pnt++;
468     	do_source( pnt );
469     	return;
470         }
471     
472         if ( token == token_then )
473         {
474     	if ( config_last != NULL && config_last->token == token_if )
475     	    return;
476     	syntax_error( "bogus 'then'" );
477         }
478     
479     #if 0
480         if ( token == token_unset )
481         {
482     	fprintf( stderr, "Ignoring 'unset' command\n" );
483     	return;
484         }
485     #endif
486     
487         if ( token == token_UNKNOWN )
488     	syntax_error( "unknown command" );
489     
490         /*
491          * Allocate an item.
492          */
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; }
499     
500         /*
501          * Tokenize the arguments.
502          */
503         while ( *pnt == ' ' || *pnt == '\t' )
504     	pnt++;
505     
506         cfg->token = token;
507         switch ( token )
508         {
509         default:
510     	syntax_error( "unknown token" );
511     
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;
518     
519         case token_choice_header:
520     	{
521     	    static int choose_number = 0;
522     	    char * choice_list;
523     
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 );
530     	}
531     	break;
532     
533         case token_comment:
534     	pnt = get_qstring(pnt, &cfg->label);
535     	if ( last_menuoption != NULL )
536     	{
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;
542     	}
543     	break;
544     
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' ) )
554     	{
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" );
558     	}
559     	else if ( *pnt == '$' )
560     	{
561     	    pnt++;
562     	    pnt = get_string( pnt, &cfg->value );
563     	}
564     	else
565     	{
566     	    syntax_error( "unknown define_bool value" );
567     	}
568     	get_varnum( cfg->value );
569     	break;
570     
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;
577     
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;
585     
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 );
592     
593     	while ( *pnt == ' ' || *pnt == '\t' )
594     	    pnt++;
595     
596     	dep_ptr = &(cfg->depend);
597     
598     	do {
599     	    *dep_ptr = (struct dependency *) malloc( sizeof( struct dependency ) );
600     	    (*dep_ptr)->next = NULL;
601     
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' ) )
605     	    {
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 );
615     	    }
616     	    else if ( *pnt == '$' )
617     	    {
618     		pnt++;
619     		pnt = get_string( pnt, &(*dep_ptr)->name );
620     		get_varnum( (*dep_ptr)->name );
621     	    }
622     	    else
623     	    {
624     		syntax_error( "can't handle dep_bool/dep_mbool/dep_tristate condition" );
625     	    }
626     	    dep_ptr = &(*dep_ptr)->next;
627     	    while ( *pnt == ' ' || *pnt == '\t' )
628     		pnt++;
629     	} while ( *pnt );
630     
631     	/*
632     	 * Create a conditional for this object's dependencies.
633     	 */
634     	{
635     	    char fake_if [1024];
636     	    struct dependency * dep;
637     	    struct condition ** cond_ptr;
638     	    int first = 1;
639     
640     	    cond_ptr = &(cfg->cond);
641     	    for ( dep = cfg->depend; dep; dep = dep->next )
642     	    {
643     		if ( token == token_dep_tristate
644     		&& ! strcmp( dep->name, "CONSTANT_M" ) )
645     		{
646     		    continue;
647     		}
648     		if ( first )
649     		{
650     		    first = 0;
651     		}
652     		else
653     		{
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;
658     		}
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;
675     	    }
676     	}
677     	break;
678     
679         case token_else:
680         case token_endmenu:
681         case token_fi:
682     	break;
683     
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;
691     
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;
700     
701         case token_if:
702     	cfg->cond = tokenize_if( pnt );
703     	break;
704     
705         case token_mainmenu_name:
706     	pnt = get_qstring( pnt, &cfg->label );
707     	break;
708     
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;
715     
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)
722     	{
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++;
731     	}
732     	break;
733         }
734         return;
735     }
736     
737     
738     
739     /*
740      * Implement the "source" command.
741      */
742     static void do_source( const char * filename )
743     {
744         char buffer [2048];
745         FILE * infile;
746         const char * old_file;
747         int old_lineno;
748         int offset;
749     
750         /* open the file */
751         if ( strcmp( filename, "-" ) == 0 )
752     	infile = stdin;
753         else
754     	infile = fopen( filename, "r" );
755     
756         /* if that failed, try ../filename */
757         if ( infile == NULL )
758         {
759     	sprintf( buffer, "../%s", filename );
760     	infile = fopen( buffer, "r" );
761         }
762     
763         if ( infile == NULL )
764         {
765     	sprintf( buffer, "unable to open %s", filename );
766     	syntax_error( buffer );
767         }
768     
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;
774     
775         /* read and process lines */
776         for ( offset = 0; ; )
777         {
778     	char * pnt;
779     
780     	/* read a line */
781     	fgets( buffer + offset, sizeof(buffer) - offset, infile );
782     	if ( feof( infile ) )
783     	    break;
784     	lineno++;
785     
786     	/* strip the trailing return character */
787     	pnt = buffer + strlen(buffer) - 1;
788     	if ( *pnt == '\n' )
789     	    *pnt-- = '\0';
790     
791     	/* eat \ NL pairs */
792     	if ( *pnt == '\\' )
793     	{
794     	    offset = pnt - buffer;
795     	    continue;
796     	}
797     
798     	/* tokenize this line */
799     	tokenize_line( buffer );
800     	offset = 0;
801         }
802     
803         /* that's all, folks */
804         if ( infile != stdin )
805     	fclose( infile );
806         current_file = old_file;
807         lineno       = old_lineno;
808         return;
809     }
810     
811     
812     
813     /*
814      * Main program.
815      */
816     int main( int argc, const char * argv [] )
817     {
818         do_source        ( "-"         );
819         fix_conditionals ( config_list );
820         dump_tk_script   ( config_list );
821         return 0;
822     }
823