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