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
my_strcmp(const char * s1,const char * s2)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  */
syntax_error(const char * msg)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 specific variable in the symbol table.
78  * Create a new entry if it does not exist yet.
79  */
80 struct variable *vartable;
81 int max_varnum = 0;
82 static int vartable_size = 0;
83 
get_varnum(char * name)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     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  */
get_string(const char * pnt,char ** label)108 static 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  */
get_qstring(const char * pnt,char ** label)135 static 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  */
get_qnqstring(const char * pnt,char ** label)184 static 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  */
tokenize_if(const char * pnt)205 static 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  */
tokenize_choices(struct kconfig * cfg_choose,const char * pnt)331 static 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  */
tokenize_line(const char * pnt)380 static 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 #if 0
485     if ( token == token_unset )
486     {
487 	fprintf( stderr, "Ignoring 'unset' command\n" );
488 	return;
489     }
490 #endif
491 
492     if ( token == token_UNKNOWN )
493 	syntax_error( "unknown command" );
494 
495     /*
496      * Allocate an item.
497      */
498     cfg = malloc( sizeof(*cfg) );
499     memset( cfg, 0, sizeof(*cfg) );
500     if ( config_last == NULL )
501 	{ config_last = config_list = cfg; }
502     else
503 	{ config_last->next = cfg; config_last = cfg; }
504 
505     /*
506      * Tokenize the arguments.
507      */
508     while ( *pnt == ' ' || *pnt == '\t' )
509 	pnt++;
510 
511     cfg->token = token;
512     switch ( token )
513     {
514     default:
515 	syntax_error( "unknown token" );
516 
517     case token_bool:
518     case token_tristate:
519 	pnt = get_qstring ( pnt, &cfg->label );
520 	pnt = get_string  ( pnt, &buffer );
521 	cfg->nameindex = get_varnum( buffer );
522 	break;
523 
524     case token_choice_header:
525 	{
526 	    static int choose_number = 0;
527 	    char * choice_list;
528 
529 	    pnt = get_qstring ( pnt, &cfg->label  );
530 	    pnt = get_qstring ( pnt, &choice_list );
531 	    pnt = get_string  ( pnt, &cfg->value  );
532 	    cfg->nameindex = -(choose_number++);
533 	    tokenize_choices( cfg, choice_list );
534 	    free( choice_list );
535 	}
536 	break;
537 
538     case token_comment:
539 	pnt = get_qstring(pnt, &cfg->label);
540 	if ( last_menuoption != NULL )
541 	{
542 	    pnt = get_qstring(pnt, &cfg->label);
543 	    if (cfg->label == NULL)
544 		syntax_error( "missing comment text" );
545 	    last_menuoption->label = cfg->label;
546 	    last_menuoption = NULL;
547 	}
548 	break;
549 
550     case token_define_bool:
551     case token_define_tristate:
552 	pnt = get_string( pnt, &buffer );
553 	cfg->nameindex = get_varnum( buffer );
554 	while ( *pnt == ' ' || *pnt == '\t' )
555 	    pnt++;
556 	if ( ( pnt[0] == 'Y'  || pnt[0] == 'M' || pnt[0] == 'N'
557 	||     pnt[0] == 'y'  || pnt[0] == 'm' || pnt[0] == 'n' )
558 	&&   ( pnt[1] == '\0' || pnt[1] == ' ' || pnt[1] == '\t' ) )
559 	{
560 	    if      ( *pnt == 'n' || *pnt == 'N' ) cfg->value = strdup( "CONSTANT_N" );
561 	    else if ( *pnt == 'y' || *pnt == 'Y' ) cfg->value = strdup( "CONSTANT_Y" );
562 	    else if ( *pnt == 'm' || *pnt == 'M' ) cfg->value = strdup( "CONSTANT_M" );
563 	}
564 	else if ( *pnt == '$' )
565 	{
566 	    pnt++;
567 	    pnt = get_string( pnt, &cfg->value );
568 	}
569 	else
570 	{
571 	    syntax_error( "unknown define_bool value" );
572 	}
573 	get_varnum( cfg->value );
574 	break;
575 
576     case token_define_hex:
577     case token_define_int:
578 	pnt = get_string( pnt, &buffer );
579 	cfg->nameindex = get_varnum( buffer );
580 	pnt = get_string( pnt, &cfg->value );
581 	break;
582 
583     case token_define_string:
584 	pnt = get_string( pnt, &buffer );
585 	cfg->nameindex = get_varnum( buffer );
586 	pnt = get_qnqstring( pnt, &cfg->value );
587 	if (cfg->value == NULL)
588 	    syntax_error( "missing value" );
589 	break;
590 
591     case token_dep_bool:
592     case token_dep_mbool:
593     case token_dep_tristate:
594 	pnt = get_qstring ( pnt, &cfg->label );
595 	pnt = get_string  ( pnt, &buffer );
596 	cfg->nameindex = get_varnum( buffer );
597 
598 	while ( *pnt == ' ' || *pnt == '\t' )
599 	    pnt++;
600 
601 	dep_ptr = &(cfg->depend);
602 
603 	do {
604 	    *dep_ptr = (struct dependency *) malloc( sizeof( struct dependency ) );
605 	    (*dep_ptr)->next = NULL;
606 
607 	    if ( ( pnt[0] == 'Y'  || pnt[0] == 'M' || pnt[0] == 'N'
608 	    ||     pnt[0] == 'y'  || pnt[0] == 'm' || pnt[0] == 'n' )
609 	    &&   ( pnt[1] == '\0' || pnt[1] == ' ' || pnt[1] == '\t' ) )
610 	    {
611 		/* dep_tristate 'foo' CONFIG_FOO m */
612 		if      ( pnt[0] == 'Y' || pnt[0] == 'y' )
613 		    (*dep_ptr)->name = strdup( "CONSTANT_Y" );
614 		else if ( pnt[0] == 'N' || pnt[0] == 'n' )
615 		    (*dep_ptr)->name = strdup( "CONSTANT_N" );
616 		else
617 		    (*dep_ptr)->name = strdup( "CONSTANT_M" );
618 		pnt++;
619 		get_varnum( (*dep_ptr)->name );
620 	    }
621 	    else if ( *pnt == '$' )
622 	    {
623 		pnt++;
624 		pnt = get_string( pnt, &(*dep_ptr)->name );
625 		get_varnum( (*dep_ptr)->name );
626 	    }
627 	    else
628 	    {
629 		syntax_error( "can't handle dep_bool/dep_mbool/dep_tristate condition" );
630 	    }
631 	    dep_ptr = &(*dep_ptr)->next;
632 	    while ( *pnt == ' ' || *pnt == '\t' )
633 		pnt++;
634 	} while ( *pnt );
635 
636 	/*
637 	 * Create a conditional for this object's dependencies.
638 	 */
639 	{
640 	    char fake_if [1024];
641 	    struct dependency * dep;
642 	    struct condition ** cond_ptr;
643 	    int first = 1;
644 
645 	    cond_ptr = &(cfg->cond);
646 	    for ( dep = cfg->depend; dep; dep = dep->next )
647 	    {
648 		if ( token == token_dep_tristate
649 		&& ! strcmp( dep->name, "CONSTANT_M" ) )
650 		{
651 		    continue;
652 		}
653 		if ( first )
654 		{
655 		    first = 0;
656 		}
657 		else
658 		{
659 		    *cond_ptr = malloc( sizeof(struct condition) );
660 		    memset( *cond_ptr, 0, sizeof(struct condition) );
661 		    (*cond_ptr)->op = op_and;
662 		    cond_ptr = &(*cond_ptr)->next;
663 		}
664 		*cond_ptr = malloc( sizeof(struct condition) );
665 		memset( *cond_ptr, 0, sizeof(struct condition) );
666 		(*cond_ptr)->op = op_lparen;
667 		if ( token == token_dep_bool )
668 		    sprintf( fake_if, "[ \"$%s\" = \"y\" -o \"$%s\" = \"\" ]; then",
669 			dep->name, dep->name );
670 		else
671 		    sprintf( fake_if, "[ \"$%s\" = \"y\" -o \"$%s\" = \"m\" -o \"$%s\" = \"\" ]; then",
672 			dep->name, dep->name, dep->name );
673 		(*cond_ptr)->next = tokenize_if( fake_if );
674 		while ( *cond_ptr )
675 		    cond_ptr = &(*cond_ptr)->next;
676 		*cond_ptr = malloc( sizeof(struct condition) );
677 		memset( *cond_ptr, 0, sizeof(struct condition) );
678 		(*cond_ptr)->op = op_rparen;
679 		cond_ptr = &(*cond_ptr)->next;
680 	    }
681 	}
682 	break;
683 
684     case token_else:
685     case token_endmenu:
686     case token_fi:
687 	break;
688 
689     case token_hex:
690     case token_int:
691 	pnt = get_qstring ( pnt, &cfg->label );
692 	pnt = get_string  ( pnt, &buffer );
693 	cfg->nameindex = get_varnum( buffer );
694 	pnt = get_string  ( pnt, &cfg->value );
695 	break;
696 
697     case token_string:
698 	pnt = get_qstring ( pnt, &cfg->label );
699 	pnt = get_string  ( pnt, &buffer );
700 	cfg->nameindex = get_varnum( buffer );
701 	pnt = get_qnqstring  ( pnt, &cfg->value );
702 	if (cfg->value == NULL)
703 	    syntax_error( "missing initial value" );
704 	break;
705 
706     case token_if:
707 	cfg->cond = tokenize_if( pnt );
708 	break;
709 
710     case token_mainmenu_name:
711 	pnt = get_qstring( pnt, &cfg->label );
712 	break;
713 
714     case token_mainmenu_option:
715 	if ( strncmp( pnt, "next_comment", 12 ) == 0 )
716 	    last_menuoption = cfg;
717 	else
718 	    pnt = get_qstring( pnt, &cfg->label );
719 	break;
720 
721     case token_unset:
722 	pnt = get_string( pnt, &buffer );
723 	cfg->nameindex = get_varnum( buffer );
724 	while ( *pnt == ' ' || *pnt == '\t' )
725 	    pnt++;
726 	while (*pnt)
727 	{
728 	    cfg->next = (struct kconfig *) malloc( sizeof(struct kconfig) );
729 	    memset( cfg->next, 0, sizeof(struct kconfig) );
730 	    cfg = cfg->next;
731 	    cfg->token = token_unset;
732 	    pnt = get_string( pnt, &buffer );
733 	    cfg->nameindex = get_varnum( buffer );
734 	    while ( *pnt == ' ' || *pnt == '\t' )
735 		pnt++;
736 	}
737 	break;
738     }
739     return;
740 }
741 
742 
743 
744 /*
745  * Implement the "source" command.
746  */
do_source(const char * filename)747 static void do_source( const char * filename )
748 {
749     char buffer [2048];
750     FILE * infile;
751     const char * old_file;
752     int old_lineno;
753     int offset;
754 
755     /* open the file */
756     if ( strcmp( filename, "-" ) == 0 )
757 	infile = stdin;
758     else
759 	infile = fopen( filename, "r" );
760 
761     /* if that failed, try ../filename */
762     if ( infile == NULL )
763     {
764 	sprintf( buffer, "../%s", filename );
765 	infile = fopen( buffer, "r" );
766     }
767 
768     if ( infile == NULL )
769     {
770 	sprintf( buffer, "unable to open %s", filename );
771 	syntax_error( buffer );
772     }
773 
774     /* push the new file name and line number */
775     old_file     = current_file;
776     old_lineno   = lineno;
777     current_file = filename;
778     lineno       = 0;
779 
780     /* read and process lines */
781     for ( offset = 0; ; )
782     {
783 	char * pnt;
784 
785 	/* read a line */
786 	fgets( buffer + offset, sizeof(buffer) - offset, infile );
787 	if ( feof( infile ) )
788 	    break;
789 	lineno++;
790 
791 	/* strip the trailing return character */
792 	pnt = buffer + strlen(buffer) - 1;
793 	if ( *pnt == '\n' )
794 	    *pnt-- = '\0';
795 
796 	/* eat \ NL pairs */
797 	if ( *pnt == '\\' )
798 	{
799 	    offset = pnt - buffer;
800 	    continue;
801 	}
802 
803 	/* tokenize this line */
804 	tokenize_line( buffer );
805 	offset = 0;
806     }
807 
808     /* that's all, folks */
809     if ( infile != stdin )
810 	fclose( infile );
811     current_file = old_file;
812     lineno       = old_lineno;
813     return;
814 }
815 
816 
817 
818 /*
819  * Main program.
820  */
main(int argc,const char * argv[])821 int main( int argc, const char * argv [] )
822 {
823     do_source        ( "-"         );
824     fix_conditionals ( config_list );
825     dump_tk_script   ( config_list );
826     free(vartable);
827     return 0;
828 }
829