1 
2 /***************************************************************************
3  *                    __            __ _ ___________                       *
4  *                    \ \          / /| |____   ____|                      *
5  *                     \ \        / / | |    | |                           *
6  *                      \ \  /\  / /  | |    | |                           *
7  *                       \ \/  \/ /   | |    | |                           *
8  *                        \  /\  /    | |    | |                           *
9  *                         \/  \/     |_|    |_|                           *
10  *                                                                         *
11  *                           Wiimms ISO Tools                              *
12  *                         http://wit.wiimm.de/                            *
13  *                                                                         *
14  ***************************************************************************
15  *                                                                         *
16  *   This file is part of the WIT project.                                 *
17  *   Visit http://wit.wiimm.de/ for project details and sources.           *
18  *                                                                         *
19  *   Copyright (c) 2009-2013 by Dirk Clemens <wiimm@wiimm.de>              *
20  *                                                                         *
21  ***************************************************************************
22  *                                                                         *
23  *   This program is free software; you can redistribute it and/or modify  *
24  *   it under the terms of the GNU General Public License as published by  *
25  *   the Free Software Foundation; either version 2 of the License, or     *
26  *   (at your option) any later version.                                   *
27  *                                                                         *
28  *   This program is distributed in the hope that it will be useful,       *
29  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
30  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
31  *   GNU General Public License for more details.                          *
32  *                                                                         *
33  *   See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt       *
34  *                                                                         *
35  ***************************************************************************/
36 
37 #define _GNU_SOURCE 1
38 
39 #include <sys/types.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <ctype.h>
44 
45 #include "types.h"
46 #include "lib-std.h"
47 #include "iso-interface.h"
48 #include "ui.h"
49 
50 #include "tab-ui.c"
51 #include "ui-head.inc"
52 
53 //
54 ///////////////////////////////////////////////////////////////////////////////
55 ///////////////			struct control_t		///////////////
56 ///////////////////////////////////////////////////////////////////////////////
57 
58 typedef struct control_t
59 {
60     FILE		* cf;		// output c file
61     FILE		* hf;		// output h file
62     FILE		* df;		// output def file
63 
64     const info_t	* info;		// pointer to first info_t
65     const info_t	* end;		// pointer to end of info_t
66 
67     int			n_cmd;		// number of commands
68     int			n_grp;		// number of helper groups
69     int			n_opt_specific;	// number of cmd specific options (=OPT__N_SPECIFIC)
70     int			n_opt;		// number of options (=OPT__N_TOTAL)
71     int			n_cmd_opt;	// number of options of current command
72     bool		need_sep;	// separator needed
73     ccp			cmd_name;	// name of current command
74     ccp			opt_prefix;	// prefix for option variables, never NULL
75 
76     u8			*opt_allow_grp;	// grp allowed options: field[n_grp][n_opt_specific]
77     u8			*opt_allow_cmd;	// cmd allowed options: field[n_cmd][n_opt_specific]
78 
79     StringField_t	gopt;		// global options
80     StringField_t	copt;		// command specfic options
81     StringField_t	opt_done;	// option handled
82 
83 } control_t;
84 
85 //
86 ///////////////////////////////////////////////////////////////////////////////
87 ///////////////			    helpers			///////////////
88 ///////////////////////////////////////////////////////////////////////////////
89 
90 static char tabs[] = "\t\t\t\t";
91 
92 static char sep1[] =
93 	"////////////////////////////////////////"
94 	"///////////////////////////////////////";
95 
96 static char sep2[] =
97 	"########################################"
98 	"#######################################";
99 
100 //-----------------------------------------------------------------------------
101 
DumpText(FILE * f,char * pbuf,char * pbuf_end,ccp text,bool is_uidef,ccp end)102 static void DumpText
103 	( FILE * f, char * pbuf, char * pbuf_end, ccp text, bool is_uidef, ccp end )
104 {
105     ASSERT(f||pbuf);
106 
107     ccp tie = is_uidef ? " \\" : "";
108     char buf[100], *buf_end = buf + 70, *dest = buf;
109 
110     bool apos = false;
111 
112     if (!text)
113 	text = "";
114     while (*text)
115     {
116 	char * dest_break = 0;
117 	ccp text_break = 0;
118 
119 	bool eol = false;
120 	while ( !eol && *text && dest < buf_end )
121 	{
122 	    if ( *text >= 1 && *text <= 3 )
123 	    {
124 		// \1 : Text only for built in help
125 		// \2 : Text only for ui.def
126 		// \3 : Text for all (default)
127 
128 		const bool skip = *text == ( is_uidef ? 1 : 2 );
129 		//PRINT("CH=%d, SKIP=%d\n",*text,skip);
130 		text++;
131 		if (skip)
132 		    while ( (u8)*text > 3 )
133 			text++;
134 		continue;
135 	    }
136 
137 	    switch (*text)
138 	    {
139 		case ' ':
140 		    if ( dest > buf )
141 		    {
142 			dest_break = dest;
143 			text_break = text;
144 		    }
145 		    *dest++ = *text++;
146 		    break;
147 
148 		case '\n':
149 		    eol = true;
150 		    *dest++ = '\\';
151 		    *dest++ = 'n';
152 		    text++;
153 		    dest_break = 0;
154 		    text_break = 0;
155 		    break;
156 
157 		case '\\':
158 		case '"':
159 		    *dest++ = '\\';
160 		    *dest++ = *text++;
161 		    break;
162 
163 		case '{':
164 		    if (is_uidef)
165 			*dest++ = *text++;
166 		    else if ( *++text == '{' )
167 			*dest++ = *text++;
168 		    else
169 		    {
170 			ccp ptr = text;
171 			while ( *ptr > ' ' && *ptr != '}' )
172 			    ptr++;
173 			apos = *ptr != '}';
174 			if (apos)
175 			    *dest++ = '\'';
176 		    }
177 		    break;
178 
179 
180 		case '}':
181 		    if (is_uidef)
182 			*dest++ = *text++;
183 		    else if ( *++text == '}' )
184 			*dest++ = *text++;
185 		    else if (apos)
186 		    {
187 			apos = false;
188 			*dest++ = '\'';
189 		    }
190 		    break;
191 
192 		case '@':
193 		case '$':
194 		case '�':
195 		    if (!is_uidef)
196 		    {
197 			if ( text[1] == *text )
198 			    *dest++ = *text++;
199 			text++;
200 			break;
201 		    }
202 		    // fall through
203 
204 		default:
205 		    *dest++ = *text++;
206 	    }
207 	    DASSERT( dest < buf_end + 10 );
208 	}
209 
210 	if ( dest >= buf_end && dest_break )
211 	{
212 	    dest = dest_break;
213 	    text = text_break;
214 	}
215 
216 	if ( dest > buf && *text )
217 	{
218 	    *dest = 0;
219 	    if (f)
220 		fprintf(f,"\t\"%s\"%s\n",buf,tie);
221 	    else
222 		pbuf += snprintf(pbuf,pbuf_end-pbuf,"\t\"%s\"%s\n",buf,tie);
223 	    dest = buf;
224 	}
225     }
226 
227     *dest = 0;
228     if (f)
229 	fprintf(f,"\t\"%s\"%s",buf,end);
230     else
231 	pbuf += snprintf(pbuf,pbuf_end-pbuf,"\t\"%s\"%s",buf,end);
232 }
233 
234 //-----------------------------------------------------------------------------
235 
print_section(FILE * f,ccp sep,ccp name)236 static void print_section ( FILE *f, ccp sep, ccp name )
237 {
238     const size_t namelen = strlen(name);
239     const int fill = ( 49 - namelen ) /2;
240     const int fw   = 49 - fill;
241     fprintf(f,"\n%.2s\f\n%s\n%.15s%*s%*s%.15s\n%s\n\n",
242 		sep, sep, sep, fill, "", -fw, name, sep, sep );
243 }
244 
245 //-----------------------------------------------------------------------------
246 
print_info_opt(control_t * ctrl,const info_t * info,const info_t * default_info,ccp cmd_name)247 static void print_info_opt
248 (
249 	control_t * ctrl,
250 	const info_t * info,
251 	const info_t * default_info,
252 	ccp cmd_name
253 )
254 {
255     ASSERT(ctrl);
256     ASSERT(info);
257 
258     FILE * cf = ctrl->cf;
259     ASSERT(cf);
260 
261     if (cmd_name)
262 	fprintf(cf,"const InfoOption_t option_%s_%s_%s =\n",
263 		ctrl->opt_prefix, cmd_name, info->c_name );
264 
265     fprintf(cf,"    {\tOPT_%s, ",info->c_name);
266     ccp start = ( default_info ? default_info : info ) -> namelist;
267     if ( *start && start[1] == '|' )
268     {
269 	fprintf(cf,"'%c', ",*start);
270 	start += 2;
271     }
272     else
273 	fprintf(cf,"0, ");
274 
275     ccp end = start;
276     while ( *end && *end != '|' )
277 	end++;
278     fprintf(cf,"\"%.*s\",\n", (int)(end-start), start );
279 
280     ccp param = info->param;
281     ccp help  = info->help;
282     if (default_info)
283     {
284 	if (!help)
285 	    help  = default_info->help;
286 	if (!param)
287 	    param  = default_info->param;
288     }
289 
290     if ( info->type & F_OPT_XPARAM || default_info && default_info->type & F_OPT_XPARAM )
291 	fprintf(cf,"\t\"%s\",\n", param && *param ? param : "param" );
292     else
293 	fprintf(cf,"\t0,\n");
294 
295     DumpText(cf,0,0,help,0,"");
296     fprintf(cf,"\n    }%c\n\n",cmd_name ? ';' : ',' );
297 }
298 
299 //-----------------------------------------------------------------------------
300 
print_opt(control_t * ctrl,const info_t * info)301 static void print_opt ( control_t * ctrl, const info_t * info )
302 {
303     ASSERT(ctrl);
304     ASSERT(info);
305 
306     ctrl->n_opt++;
307     print_info_opt(ctrl,info,0,0);
308     fprintf( ctrl->hf, "\tOPT_%s,\n", info->c_name );
309 };
310 
311 //-----------------------------------------------------------------------------
312 
print_opt_link(control_t * ctrl,const info_t * info)313 static void print_opt_link ( control_t * ctrl, const info_t * info )
314 {
315     ASSERT(ctrl);
316     ASSERT(ctrl->cf);
317     ASSERT(ctrl->opt_prefix);
318     ASSERT(info);
319 
320     if (!InsertStringField(&ctrl->opt_done,info->c_name,false))
321 	return;
322 
323     if ( ctrl->need_sep )
324     {
325 	ctrl->need_sep = false;
326 	if ( ctrl->n_cmd_opt )
327 	    fprintf(ctrl->cf,"\n\tOptionInfo + OPT_NONE, // separator\n\n" );
328     }
329 
330     if ( !info->help || info->type & T_DEF_OPT )
331 	fprintf(ctrl->cf,"\tOptionInfo + OPT_%s,\n", info->c_name );
332     else
333 	fprintf(ctrl->cf,"\t&option_%s_%s,\n", ctrl->opt_prefix, info->c_name );
334 
335     if (ctrl->cmd_name)
336     {
337 	fprintf(ctrl->df,"#:def_cmd_opt( \"%s\", \"%s\", \\\n",
338 		ctrl->cmd_name, info->c_name );
339 	DumpText(ctrl->df,0,0,info->param,1,", \\\n");
340 	DumpText(ctrl->df,0,0,info->help,1," )\n\n");
341     }
342 
343     ctrl->n_cmd_opt++;
344 }
345 
346 //-----------------------------------------------------------------------------
347 
348 static const info_t * print_links_iterator
349 	( control_t * ctrl, const info_t * info, ccp prefix );
350 
351 //-----------------------------------------------------------------------------
352 
print_cmd_links(control_t * ctrl,const info_t * ref)353 static void print_cmd_links ( control_t * ctrl, const info_t * ref )
354 {
355     ASSERT(ctrl);
356     ASSERT(ref);
357 
358     const info_t * info;
359     for ( info = ctrl->info; info < ctrl->end; info++ )
360 	if ( info->type & T_CMD_BEG && !strcmp(info->c_name,ref->c_name) )
361 	{
362 	    print_links_iterator(ctrl,info,"cmd");
363 	    break;
364 	}
365 }
366 
367 //-----------------------------------------------------------------------------
368 
print_grp_links(control_t * ctrl,const info_t * ref)369 static void print_grp_links ( control_t * ctrl, const info_t * ref )
370 {
371     ASSERT(ctrl);
372     ASSERT(ref);
373 
374     const info_t * info;
375     for ( info = ctrl->info; info < ctrl->end; info++ )
376 	if ( info->type & T_GRP_BEG && !strcmp(info->c_name,ref->c_name) )
377 	{
378 	    print_links_iterator(ctrl,info,"grp");
379 	    break;
380 	}
381 }
382 
383 //-----------------------------------------------------------------------------
384 
print_links_iterator(control_t * ctrl,const info_t * info,ccp prefix)385 static const info_t * print_links_iterator
386 	( control_t * ctrl, const info_t * info, ccp prefix )
387 {
388     ASSERT(ctrl);
389     ASSERT(info);
390 
391     ccp saved_prefix = ctrl->opt_prefix;
392     char buf[100];
393     if (prefix)
394     {
395 	snprintf(buf,sizeof(buf),"%s_%s",prefix,info->c_name);
396 	ctrl->opt_prefix = buf;
397     };
398 
399     for ( info++; info < ctrl->end && !(info->type & (T_CMD_BEG|T_GRP_BEG) ); info++ )
400     {
401 	if ( info->type & F_HIDDEN )
402 	{
403 	    // ignored -> do nothing
404 	}
405 	else if ( info->type & T_SEP_OPT )
406 	    ctrl->need_sep = true;
407 	else if ( info->type & (F_OPT_GLOBAL|T_CMD_OPT) )
408 	    print_opt_link(ctrl,info);
409 	else if ( info->type & T_COPY_CMD )
410 	    print_cmd_links(ctrl,info);
411 	else if ( info->type & T_COPY_GRP )
412 	    print_grp_links(ctrl,info);
413     }
414 
415     ctrl->opt_prefix = saved_prefix;
416     return info;
417 }
418 
419 //-----------------------------------------------------------------------------
420 
print_links(control_t * ctrl)421 static void print_links ( control_t * ctrl )
422 {
423     ASSERT(ctrl);
424     FILE * cf = ctrl->cf;
425     ASSERT(cf);
426 
427     print_section(cf,sep1,"InfoOption tabs");
428 
429     char * temp_param	= iobuf;
430     char * temp_help	= iobuf +  sizeof(iobuf)/8;
431     char * sum_beg	= iobuf + sizeof(iobuf)/2;
432     char * sum_end	= iobuf + sizeof(iobuf);
433     char * sum		= sum_beg;
434 
435     const info_t * info_cmd = ctrl->info;
436     ASSERT ( info_cmd->type & T_DEF_TOOL );
437     fprintf(cf,"const InfoOption_t * option_tab_tool[] =\n{\n");
438 
439     ctrl->n_cmd_opt	= 0;
440     ctrl->need_sep	= false;
441 
442     print_links_iterator(ctrl,info_cmd,"def");
443     fprintf(cf,"\n\t0\n};\n\n");
444 
445     DumpText(0,temp_param,iobuf+sizeof(iobuf),info_cmd->param,0,"");
446     DumpText(0,temp_help,iobuf+sizeof(iobuf),info_cmd->help,0,"");
447     sum += snprintf(sum,sum_end-sum,
448 			"    {\t0,\n"		// id
449 			"\tfalse,\n"		// hidden
450 			"\tfalse,\n"		// separator
451 			"\t\"%s\",\n"		// name1
452 			"\t0,\n"		// name2
453 			"%s,\n"			// param
454 			"%s,\n"			// help
455 			"\t%u,\n"		// n_opt
456 			"\toption_tab_tool,\n"	// opt
457 			"\t0\n"			// opt_allowed
458 			"    },\n\n"
459 			,info_cmd->c_name
460 			,temp_param
461 			,temp_help
462 			,ctrl->n_cmd_opt
463 			);
464 
465     bool separator = false;
466     for ( info_cmd++; info_cmd < ctrl->end; info_cmd++ )
467     {
468 	if ( info_cmd->type & T_SEP_CMD )
469 	    separator = true;
470 	//if ( !( info_cmd->type & T_DEF_CMD ) || info_cmd->type & F_HIDDEN )
471 	if ( !( info_cmd->type & T_DEF_CMD ) )
472 	    continue;
473 
474 	fprintf(cf,"static const InfoOption_t * option_tab_cmd_%s[] =\n{\n",
475 		info_cmd->c_name);
476 
477 	ctrl->n_cmd_opt	= 0;
478 	ctrl->need_sep	= false;
479 	ctrl->cmd_name	= info_cmd->c_name;
480 	ResetStringField(&ctrl->opt_done);
481 
482 	const info_t * info;
483 	for ( info = info_cmd;  info < ctrl->end; info++ )
484 	    if ( info->type & T_CMD_BEG && !strcmp(info->c_name,info_cmd->c_name) )
485 	    {
486 		print_links_iterator(ctrl,info,"cmd");
487 		break;
488 	    }
489 	fprintf(cf,"\n\t0\n};\n\n");
490 
491 	ccp name1 = info_cmd->namelist, ptr = name1;
492 	while ( *ptr && *ptr != '|' )
493 	    ptr++;
494 	const int len1 = ptr - name1;
495 	char name2[100] = "0";
496 	if ( *ptr == '|' )
497 	{
498 	    ccp n2 = ++ptr;
499 	    while ( *ptr && *ptr != '|' )
500 		ptr++;
501 	    if ( ptr > n2 )
502 		snprintf(name2,sizeof(name2),"\"%.*s\"",(int)(ptr-n2),n2);
503 	}
504 
505 	DumpText(0,temp_param,iobuf+sizeof(iobuf),info_cmd->param,0,"");
506 	DumpText(0,temp_help,iobuf+sizeof(iobuf),info_cmd->help,0,"");
507 	sum += snprintf(sum,sum_end-sum,
508 			"    {\tCMD_%s,\n"		// id
509 			"\t%s,\n"			// hidden
510 			"\t%s,\n"			// separator
511 			"\t\"%.*s\",\n"			// name1
512 			"\t%s,\n"			// name2
513 			"%s,\n"				// param
514 			"%s,\n"				// help
515 			"\t%u,\n"			// n_opt
516 			"\toption_tab_cmd_%s,\n"	// opt
517 			"\toption_allowed_cmd_%s\n"	// opt_allowed
518 			"    },\n\n"
519 			,info_cmd->c_name
520 			,info_cmd->type & F_HIDDEN ? "true" : "false"
521 			,separator ? "true" : "false"
522 			,len1 ,name1
523 			,name2
524 			,temp_param
525 			,temp_help
526 			,ctrl->n_cmd_opt
527 			,info_cmd->c_name
528 			,info_cmd->c_name
529 			);
530 	separator = false;
531     }
532 
533     print_section(cf,sep1,"InfoCommand");
534     fprintf(cf,"const InfoCommand_t CommandInfo[CMD__N+1] =\n{\n");
535     fputs(sum_beg,cf);
536     fprintf(cf,"    {0,0,0,0,0,0,0,0,0}\n};\n");
537 }
538 
539 //
540 ///////////////////////////////////////////////////////////////////////////////
541 ///////////////			    Generate()			///////////////
542 ///////////////////////////////////////////////////////////////////////////////
543 
544 static char var_buf[10000];
545 
546 //-----------------------------------------------------------------------------
547 
Generate(control_t * ctrl)548 static enumError Generate ( control_t * ctrl )
549 {
550     ASSERT(ctrl);
551     ASSERT(ctrl->info);
552 
553     FILE * cf = ctrl->cf;
554     FILE * hf = ctrl->hf;
555     ASSERT(cf);
556     ASSERT(hf);
557 
558     FREE(ctrl->opt_allow_grp);
559     FREE(ctrl->opt_allow_cmd);
560     ctrl->opt_allow_grp = ctrl->opt_allow_cmd = 0;
561 
562     const info_t *info;
563 
564 
565     //----- ui header
566 
567     fprintf(cf,text_ui_head);
568     fprintf(hf,text_ui_head);
569 
570 
571     //----- setup guard
572 
573     char guard[100];
574     snprintf(guard,sizeof(guard),"WIT_UI_%s_h",ctrl->info->c_name);
575     char * ptr;
576     for ( ptr = guard; *ptr; ptr++ )
577 	*ptr = *ptr == '-' ? '_' : toupper((int)*ptr);
578     fprintf(hf,"\n#ifndef %s\n#define %s\n",guard,guard);
579 
580 
581     //----- header
582 
583     ASSERT( ctrl->info->type & T_DEF_TOOL );
584     ccp tool_name = ctrl->info->c_name;
585 
586     fprintf(cf,"#include <getopt.h>\n");
587     fprintf(cf,"#include \"ui-%s.h\"\n",tool_name);
588 
589     fprintf(hf,"#include \"lib-std.h\"\n");
590     fprintf(hf,"#include \"ui.h\"\n");
591 
592 
593     //----- print enum enumOptions & OptionInfo[]
594 
595     print_section(cf,sep1,"OptionInfo[]");
596     print_section(hf,sep1,"enum enumOptions");
597 
598     char * var_ptr = var_buf;
599     char * var_end = var_buf + sizeof(var_buf);
600     var_ptr += snprintf(var_ptr,var_end-var_ptr,
601 		"extern const InfoOption_t OptionInfo[OPT__N_TOTAL+1];\n");
602 
603     fprintf(cf,
604 	    "const InfoOption_t OptionInfo[OPT__N_TOTAL+1] =\n"
605 	    "{\n"
606 	    "    {0,0,0,0,0}, // OPT_NONE,\n"
607 	    "\n"
608 	    );
609 
610     fprintf(hf,
611 	    "typedef enum enumOptions\n"
612 	    "{\n"
613 	    "\tOPT_NONE,\n"
614 	    "\n"
615 	    );
616 
617     ctrl->n_opt = 1;
618     ctrl->n_opt_specific = 0;
619     if ( ctrl->n_cmd )
620     {
621 	fprintf(cf,"    //----- command specific options -----\n\n");
622 	fprintf(hf,"\t//----- command specific options -----\n\n");
623 
624 	for ( info = ctrl->info; info < ctrl->end; info++ )
625 	    if ( info->type & F_OPT_COMMAND )
626 		print_opt(ctrl,info);
627 
628 	ctrl->n_opt_specific = ctrl->n_opt;
629 
630 	fprintf(cf,
631 		"    {0,0,0,0,0}, // OPT__N_SPECIFIC == %d\n\n"
632 		"    //----- global options -----\n\n",
633 		ctrl->n_opt_specific );
634 
635 	fprintf(hf,
636 		"\n\tOPT__N_SPECIFIC, // == %d \n\n"
637 		"\t//----- global options -----\n\n",
638 		ctrl->n_opt_specific );
639     }
640 
641     for ( info = ctrl->info; info < ctrl->end; info++ )
642 	if ( info->type & F_OPT_GLOBAL )
643 	    print_opt(ctrl,info);
644 
645     fprintf(cf,
646 	    "    {0,0,0,0,0} // OPT__N_TOTAL == %d\n\n"
647 	    "};\n"
648 	    ,ctrl->n_opt );
649 
650     fprintf(hf,
651 	    "\n\tOPT__N_TOTAL // == %d\n\n"
652 	    "} enumOptions;\n"
653 	    ,ctrl->n_opt );
654 
655     if (ctrl->n_opt_specific)
656     {
657 	noTRACE("opt_allowed = ( %2u + %2u ) * %2u\n",
658 		ctrl->n_grp, ctrl->n_cmd, ctrl->n_opt_specific );
659 	if (ctrl->n_grp)
660 	    ctrl->opt_allow_grp = CALLOC(ctrl->n_grp,ctrl->n_opt_specific);
661 	ctrl->opt_allow_cmd = CALLOC(ctrl->n_cmd,ctrl->n_opt_specific);
662     }
663 
664 
665     //----- print alternate option infos
666 
667     bool done = false;
668     const info_t * last_cmd = ctrl->info;
669     ctrl->opt_prefix = "def";
670     for ( info = ctrl->info; info < ctrl->end; info++ )
671     {
672 	if ( info->type & T_CMD_BEG )
673 	{
674 	    ctrl->opt_prefix = "cmd";
675 	    last_cmd = info;
676 	}
677 	else if ( info->type & T_GRP_BEG )
678 	{
679 	    ctrl->opt_prefix = "grp";
680 	    last_cmd = info;
681 	}
682 	else if ( info->type & T_CMD_OPT && info->help && *info->help )
683 	{
684 	    if (!done)
685 	    {
686 		print_section(cf,sep1,"alternate option infos");
687 		done = true;
688 	    }
689 
690 	    const info_t * info0;
691 	    for ( info0 = ctrl->info; info0 < info; info0++ )
692 		if ( info0->type & T_DEF_OPT && !strcmp(info->c_name,info0->c_name) )
693 		{
694 		    print_info_opt(ctrl,info,info0,last_cmd->c_name);
695 		    break;
696 		}
697 	}
698     }
699 
700     //----- print enum enumOptionsBit
701 
702     if ( ctrl->n_cmd )
703     {
704 	print_section(hf,sep1,"enum enumOptionsBit");
705 
706 	fprintf(hf,
707 		"//\t*****  only for verification  *****\n"
708 		"\n"
709 		"//typedef enum enumOptionsBit\n"
710 		"//{\n"
711 		"//\t//----- command specific options -----\n"
712 		"//\n"
713 		);
714 
715 	for ( info = ctrl->info; info < ctrl->end; info++ )
716 	    if ( info->type & F_OPT_COMMAND )
717 		fprintf(hf,"//\tOB_%s%.*s= 1llu << OPT_%s,\n",
718 			info->c_name,
719 			( 28 - (int)strlen(info->c_name) ) / 8, tabs,
720 			info->c_name );
721 
722 	fprintf(hf,"//\n//\t//----- group & command options -----\n");
723 
724 	for ( info = ctrl->info; info < ctrl->end; )
725 	{
726 	    ccp cmd_name;
727 	    u8 * opt_allow = 0;
728 	    if ( info->type & T_CMD_BEG )
729 	    {
730 		cmd_name = info->c_name;
731 		fprintf(hf,"//\n//\tOB_CMD_%s%.*s=",
732 			info->c_name,
733 			( 24 - (int)strlen(info->c_name) ) / 8, tabs );
734 		if (ctrl->opt_allow_cmd)
735 		{
736 		    //PRINT("SELECT ALLOW CMD %u/%s\n",info->index,info->c_name);
737 		    opt_allow = ctrl->opt_allow_cmd + info->index * ctrl->n_opt_specific;
738 		}
739 	    }
740 	    else if ( info->type & T_GRP_BEG )
741 	    {
742 		cmd_name = info->c_name;
743 		fprintf(hf,"//\n//\tOB_GRP_%s%.*s=",
744 			info->c_name,
745 			( 24 - (int)strlen(info->c_name) ) / 8, tabs );
746 		if (ctrl->opt_allow_grp)
747 		{
748 		    //PRINT("SELECT ALLOW GRP %u/%s\n",info->index,info->c_name);
749 		    opt_allow = ctrl->opt_allow_grp + info->index * ctrl->n_opt_specific;
750 		}
751 	    }
752 	    else
753 	    {
754 		info++;
755 		continue;
756 	    }
757 
758 	    info++;
759 	    char * dest = iobuf;
760 	    while ( info < ctrl->end )
761 	    {
762 		if ( info->type & T_ALL_OPT )
763 		{
764 		    dest += sprintf(dest,"\n//\t\t\t\t| ~(u64)0");
765 		    if (opt_allow)
766 		    {
767 			//PRINT("ALLOW ALL\n");
768 			memset(opt_allow,1,ctrl->n_opt_specific);
769 		    }
770 		}
771 		else if ( info->type & T_COPY_CMD )
772 		{
773 		    dest += sprintf(dest,"\n//\t\t\t\t| OB_CMD_%s",info->c_name);
774 		    if (opt_allow)
775 		    {
776 			//PRINT("OR CMD %u/%s\n",info->index,info->c_name);
777 			DASSERT(ctrl->opt_allow_cmd);
778 			u8 * src = ctrl->opt_allow_cmd + info->index * ctrl->n_opt_specific;
779 			u8 * dest = opt_allow;
780 			int count = ctrl->n_opt_specific;
781 			while ( count-- > 0 )
782 			    *dest++ |= *src++;
783 		    }
784 		}
785 		else if ( info->type & T_COPY_GRP )
786 		{
787 		    dest += sprintf(dest,"\n//\t\t\t\t| OB_GRP_%s",info->c_name);
788 		    if ( opt_allow && ctrl->opt_allow_grp )
789 		    {
790 			//PRINT("OR GRP %u/%s\n",info->index,info->c_name);
791 			u8 * src = ctrl->opt_allow_grp + info->index * ctrl->n_opt_specific;
792 			u8 * dest = opt_allow;
793 			int count = ctrl->n_opt_specific;
794 			while ( count-- > 0 )
795 			    *dest++ |= *src++;
796 		    }
797 		}
798 		else if ( info->type & T_CMD_OPT )
799 		{
800 		    if (FindStringField(&ctrl->copt,info->c_name))
801 		    {
802 			dest += sprintf(dest,"\n//\t\t\t\t| OB_%s",info->c_name);
803 			if ( opt_allow && info->index )
804 			{
805 			    //PRINT("ALLOW OPT %u/%s\n",info->index,info->c_name);
806 			    opt_allow[info->index] = 1;
807 			}
808 		    }
809 		    else if (!FindStringField(&ctrl->gopt,info->c_name))
810 			ERROR0(ERR_SEMANTIC,"Option not defined: %s %s --%s",
811 				tool_name, cmd_name, info->c_name );
812 		}
813 		else if ( info->type & (T_CMD_BEG|T_GRP_BEG) )
814 		    break;
815 		ASSERT( dest < iobuf + sizeof(iobuf) );
816 		info++;
817 	    }
818 	    if ( dest == iobuf )
819 		fprintf(hf," 0,\n");
820 	    else
821 		fprintf(hf,"%s,\n",iobuf+8);
822 	}
823 
824 	fprintf(hf,"//\n//} enumOptionsBit;\n");
825     }
826 
827 
828     //----- print enum enumCommands & CommandTab[]
829 
830     print_section(hf,sep1,"enum enumCommands");
831     fprintf(hf,
832 	    "typedef enum enumCommands\n"
833 	    "{\n"
834 	    "\tCMD__NONE,"
835 	    );
836 
837     if ( ctrl->n_cmd )
838     {
839 	print_section(cf,sep1,"CommandTab[]");
840 	fputs("\n\n",hf);
841 
842 	var_ptr += snprintf(var_ptr,var_end-var_ptr,
843 		"extern const CommandTab_t CommandTab[];\n");
844 
845 	fprintf(cf,
846 		"const CommandTab_t CommandTab[] =\n"
847 		"{\n"
848 		);
849 
850 	for ( info = ctrl->info; info < ctrl->end; info++ )
851 	    if ( info->type & T_DEF_CMD )
852 	    {
853 		fprintf( hf, "\tCMD_%s,\n",info->c_name);
854 		ccp ptr = info->namelist;
855 		while (*ptr)
856 		{
857 		    ccp n1 = ptr;
858 		    while ( *ptr && *ptr != '|' )
859 			ptr++;
860 		    const int l1 = ptr - n1;
861 		    while ( *ptr == '|' )
862 			ptr++;
863 		    if (*ptr)
864 		    {
865 			ccp n2 = ptr;
866 			while ( *ptr && *ptr != '|' )
867 			    ptr++;
868 			const int l2 = ptr - n2;
869 
870 			fprintf(cf,
871 				"    { CMD_%s,%.*s\"%.*s\",%.*s\"%.*s\",%.*s0 },\n",
872 				info->c_name, (20-(int)strlen(info->c_name))/8, tabs,
873 				l1, n1, (20-l1)/8, tabs,
874 				l2, n2, (20-l2)/8, tabs );
875 
876 			while ( *ptr == '|' )
877 			    ptr++;
878 		    }
879 		    else
880 			fprintf(cf, "    { CMD_%s,%.*s\"%.*s\",%.*s0,\t\t0 },\n",
881 				info->c_name, (20-(int)strlen(info->c_name))/8, tabs,
882 				l1, n1, (20-l1)/8, tabs );
883 		}
884 	    }
885 	    else if ( info->type == T_SEP_CMD )
886 		fprintf(hf,"\n");
887 
888 	fprintf(cf,
889 		"\n    { CMD__N,0,0,0 }\n"
890 		"};\n"
891 		);
892     }
893 
894     fprintf(hf,
895 	    "\n\tCMD__N // == %u\n\n"
896 	    "} enumCommands;\n"
897 	    , ctrl->n_cmd );
898 
899 
900     //----- print options
901 
902     print_section(cf,sep1,"OptionShort & OptionLong");
903 
904     char * dest = iobuf;
905     for ( info = ctrl->info; info < ctrl->end; info++ )
906 	if ( info->type & T_DEF_OPT && info->namelist[1] == '|' )
907 	{
908 	    *dest++ = info->namelist[0];
909 	    if ( info->type & F_OPT_OPTPARAM )
910 		*dest++ = ':';
911 	    if ( info->type & (F_OPT_OPTPARAM|F_OPT_PARAM) )
912 		*dest++ = ':';
913 	}
914     *dest = 0;
915     fprintf(cf,"const char OptionShort[] = \"%s\";\n\n",iobuf);
916     var_ptr += snprintf(var_ptr,var_end-var_ptr,
917 		"extern const char OptionShort[];\n");
918 
919     ccp opt_buf[OPT_INDEX_SIZE];
920     memset(opt_buf,0,sizeof(opt_buf));
921     int getopt_idx = OPT_LONG_BASE;
922 
923     fprintf(cf,"const struct option OptionLong[] =\n{\n");
924     var_ptr += snprintf(var_ptr,var_end-var_ptr,
925 		"extern const struct option OptionLong[];\n");
926 
927     for ( info = ctrl->info; info < ctrl->end; info++ )
928 	if ( info->type & T_DEF_OPT )
929 	{
930 	    ccp ptr = info->namelist;
931 	    const int pmode = (info->type & F_OPT_OPTPARAM)
932 				? 2
933 				: (info->type & F_OPT_PARAM)
934 					? 1
935 					: 0;
936 
937 	    if ( info->namelist[1] == '|' )
938 	    {
939 		snprintf(iobuf,sizeof(iobuf),"%d, 0, '%c'",
940 			pmode, info->namelist[0] );
941 		ptr += 2;
942 		opt_buf[(u8)(info->namelist[0])] = info->c_name;
943 	    }
944 	    else
945 	    {
946 		snprintf(iobuf,sizeof(iobuf),"%d, 0, GO_%s",
947 			pmode, info->c_name );
948 		ASSERT_MSG( getopt_idx < OPT_INDEX_SIZE,
949 				"getopt_idx[%x] >= OPT_INDEX_SIZE[%x]\n",
950 				getopt_idx, OPT_INDEX_SIZE );
951 		opt_buf[(u8)(getopt_idx++)] = info->c_name;
952 	    }
953 
954 	    int indent = 0;
955 	    while (*ptr)
956 	    {
957 		ccp start = ptr;
958 		while ( *ptr && *ptr != '|' )
959 		    ptr++;
960 		const int len = ptr - start;
961 		fprintf(cf,"\t%s{ \"%.*s\",%.*s%s },\n",
962 			indent ? " " : "", len, start, (26-len-indent)/8, tabs, iobuf );
963 		if (*ptr)
964 		    ptr++;
965 		indent = 1;
966 	    }
967 	}
968     fprintf(cf,"\n\t{0,0,0,0}\n};\n");
969 
970 
971     //----- print enumGetOpt
972 
973     print_section(hf,sep1,"enumGetOpt");
974     fprintf(hf,"typedef enum enumGetOpt\n{");
975 
976     // add '?' temporary;
977     ASSERT(!opt_buf['?']);
978     opt_buf['?'] = "_ERR";
979 
980     static const int septab[] = { 0, '0', '9'+1, '?', '?'+1,
981 				  'A', 'Z'+1, 'a', 'z'+1,
982 				  OPT_LONG_BASE, OPT_INDEX_SIZE };
983     const int * sepptr = septab;
984     int i;
985     for ( i = 0; i < OPT_INDEX_SIZE; i++ )
986 	if ( opt_buf[i] )
987 	{
988 	    if ( i >= *sepptr )
989 	    {
990 		fputc('\n',hf);
991 		while ( i >= *sepptr )
992 		    sepptr++;
993 	    }
994 	    if ( i < OPT_LONG_BASE )
995 		fprintf(hf,"\tGO_%s%.*s= '%c',\n",
996 			opt_buf[i], (28-(int)strlen(opt_buf[i]))/8, tabs, i );
997 	    else if ( i == OPT_LONG_BASE )
998 		fprintf(hf,"\tGO_%s%.*s= 0x%02x,\n",
999 			opt_buf[i], (28-(int)strlen(opt_buf[i]))/8, tabs, i );
1000 	    else
1001 		fprintf(hf,"\tGO_%s,\n",opt_buf[i]);
1002 	}
1003 
1004     fprintf(hf,"\n} enumGetOpt;\n");
1005     opt_buf['?'] = 0;
1006 
1007 
1008     //----- print option index
1009 
1010     print_section(cf,sep1,"OptionUsed & OptionIndex");
1011 
1012     fprintf(cf,"u8 OptionUsed[OPT__N_TOTAL+1] = {0};\n\n");
1013     var_ptr += snprintf(var_ptr,var_end-var_ptr,
1014 		"extern u8 OptionUsed[OPT__N_TOTAL+1];\n");
1015 
1016     fprintf(cf,"const u8 OptionIndex[OPT_INDEX_SIZE] = \n{\n");
1017     var_ptr += snprintf(var_ptr,var_end-var_ptr,
1018 		"extern const u8 OptionIndex[OPT_INDEX_SIZE];\n");
1019 
1020     for ( i = 0; i < OPT_INDEX_SIZE; )
1021     {
1022 	int start = i;
1023 	while ( i < OPT_INDEX_SIZE && !opt_buf[i] )
1024 	    i++;
1025 	int len = i - start;
1026 	while ( len > 0 )
1027 	{
1028 	    const int now_len = len < 16 ? len : 16 - start % 16;
1029 	    fprintf(cf,"\t/* 0x%02x   */\t %.*s\n",
1030 		    start,
1031 		    2*now_len + now_len/4,
1032 		    "0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0," );
1033 	    start += now_len;
1034 	    len -= now_len;
1035 	}
1036 
1037 	while ( i < OPT_INDEX_SIZE && opt_buf[i] )
1038 	{
1039 	    fprintf(cf,"\t/* 0x%02x %c */\tOPT_%s,\n",
1040 		i, i > ' ' && i < 0x7f ? i : ' ', opt_buf[i]);
1041 	    i++;
1042 	}
1043     }
1044     fprintf(cf,"};\n");
1045 
1046 
1047     //----- option allowed
1048 
1049     if (ctrl->opt_allow_cmd)
1050     {
1051 	print_section(cf,sep1,"opt_allowed_cmd_*");
1052 	for ( info = ctrl->info; info < ctrl->end; info++ )
1053 	{
1054 	    if ( !( info->type & T_DEF_CMD ) )
1055 		continue;
1056 
1057 	    fprintf(cf,"static u8 option_allowed_cmd_%s[%u] = // cmd #%u\n{",
1058 		info->c_name, ctrl->n_opt_specific, info->index );
1059 
1060 	    int i;
1061 	    u8 * ptr = ctrl->opt_allow_cmd + info->index * ctrl->n_opt_specific;
1062 	    for ( i = 0; i < ctrl->n_opt_specific; i++ )
1063 		fprintf(cf, "%s%u%s",
1064 			    !(i%30) ? "\n    " : !(i%10) ? "  " : !(i%5) ? " " : "",
1065 			    ptr[i],
1066 			    i < ctrl->n_opt_specific-1 ? "," : "" );
1067 
1068 	    fprintf(cf,"\n};\n\n");
1069 	}
1070     }
1071 
1072     //----- InfoCommand
1073 
1074     print_links(ctrl);
1075     var_ptr += snprintf(var_ptr,var_end-var_ptr,
1076 		"extern const InfoCommand_t CommandInfo[CMD__N+1];\n");
1077 
1078     //----- InfoUI
1079 
1080     print_section(cf,sep1,"InfoUI");
1081 
1082     var_ptr += snprintf(var_ptr,var_end-var_ptr,
1083 		"extern const InfoUI_t InfoUI;\n");
1084 
1085     fprintf(cf,
1086 	    "const InfoUI_t InfoUI =\n"
1087 	    "{\n"
1088 	    "\t\"%s\",\n"	// tool_name
1089 	    "\t%s\n"		// n_cmd
1090 	    "\t%s\n"		// cmd_tab
1091 	    "\tCommandInfo,\n"	// cmd_info
1092 	    "\t%s\n"		// n_opt_specific
1093 	    "\tOPT__N_TOTAL,\n"	// n_opt_total
1094 	    "\tOptionInfo,\n"	// opt_info
1095 	    "\tOptionUsed,\n"	// opt_used
1096 	    "\tOptionIndex,\n"	// opt_index
1097 	    "\tOptionShort,\n"	// opt_short
1098 	    "\tOptionLong\n"	// opt_long
1099 	    "};\n",
1100 	    ctrl->info->c_name,
1101 	    ctrl->n_cmd ? "CMD__N," : "0, // n_cmd",
1102 	    ctrl->n_cmd ? "CommandTab," : "0, // cmd_tab",
1103 	    ctrl->n_cmd ? "OPT__N_SPECIFIC," : "0, // n_opt_specific" );
1104 
1105 
1106     //----- external vars
1107 
1108     print_section(hf,sep1,"external vars");
1109     fputs(var_buf,hf);
1110 
1111 
1112     //----- terminate
1113 
1114     print_section(cf,sep1,"END");
1115     print_section(hf,sep1,"END");
1116     fprintf(hf,"#endif // %s\n\n",guard);
1117 
1118     return ERR_OK;
1119 };
1120 
1121 //
1122 ///////////////////////////////////////////////////////////////////////////////
1123 ///////////////			  AddTables()			///////////////
1124 ///////////////////////////////////////////////////////////////////////////////
1125 
AddTables(FILE * df)1126 static void AddTables ( FILE * df )
1127 {
1128     ASSERT(df);
1129 
1130     //--------------------------------------------------
1131 
1132  #if 0 // [[2do]] not needed yet
1133 
1134     print_section(df,sep2,"Region Info");
1135 
1136     char ch;
1137     for ( ch = 'A'; ch <= 'Z'; ch++ )
1138     {
1139 	const RegionInfo_t * reg = GetRegionInfo(ch);
1140 	fprintf(df,"#:def_tab(\"region\",'%c',%u,%u,\"%s\",\"%s\")\n",
1141 		ch, reg->reg, reg->mandatory, reg->name4, reg->name );
1142     }
1143     const RegionInfo_t * reg = GetRegionInfo(0);
1144     fprintf(df,"#:def_tab(\"region\",'',%2u,%u,\"%s\",\"%s\")\n",
1145 	reg->reg, reg->mandatory, reg->name4, reg->name );
1146 
1147  #endif
1148 
1149     //--------------------------------------------------
1150 }
1151 
1152 //
1153 ///////////////////////////////////////////////////////////////////////////////
1154 ///////////////			    main()			///////////////
1155 ///////////////////////////////////////////////////////////////////////////////
1156 
main(int argc,char ** argv)1157 int main ( int argc, char ** argv )
1158 {
1159     SetupLib(argc,argv,"gen-ui",PROG_UNKNOWN);
1160 
1161     static ccp def_null_name = "/dev/null";
1162     FILE * nf = fopen(def_null_name,"wb");
1163     if (!nf)
1164     {
1165 	fprintf(stderr,"!!! Can't create file: %s\n",def_null_name);
1166 	return ERR_CANT_CREATE;
1167     }
1168 
1169     static ccp def_fname = "src/ui/ui.def";
1170     FILE * df = fopen(def_fname,"wb");
1171     if (!df)
1172     {
1173 	fprintf(stderr,"!!! Can't create file: %s\n",def_fname);
1174 	return ERR_CANT_CREATE;
1175     }
1176 
1177     char fname[200];
1178     info_t * info = info_tab;
1179 
1180     while ( info->type != T_END )
1181     {
1182 	if ( ! ( info->type & T_DEF_TOOL ) )
1183 	{
1184 	    fprintf(stderr,"!!! Missing T_DEF_TOOL entry.\n");
1185 	    return ERR_SYNTAX;
1186 	}
1187 
1188 	control_t ctrl;
1189 	memset(&ctrl,0,sizeof(ctrl));
1190 	ctrl.df = nf;
1191 	ctrl.opt_prefix = "";
1192 	InitializeStringField(&ctrl.gopt);
1193 	InitializeStringField(&ctrl.copt);
1194 	InitializeStringField(&ctrl.opt_done);
1195 
1196 	if ( !( info->type & F_HIDDEN ) )
1197 	{
1198 	    ctrl.df = df;
1199 	    snprintf(iobuf,sizeof(iobuf),"Tool '%s'",info->c_name);
1200 	    print_section(df,sep2,iobuf);
1201 	    fprintf(df,"#:def_tool( \"%s\", \\\n",info->c_name);
1202 	    DumpText(df,0,0,info->param,1,", \\\n");
1203 	    DumpText(df,0,0,info->help,1," )\n\n");
1204 	}
1205 
1206 	snprintf(fname,sizeof(fname),"src/ui/ui-%s.c",info->c_name);
1207 	ctrl.cf = fopen(fname,"wb");
1208 	if (!ctrl.cf)
1209 	{
1210 	    fprintf(stderr,"!!! Can't create file: %s\n",fname);
1211 	    return ERR_CANT_CREATE;
1212 	}
1213 
1214 	snprintf(fname,sizeof(fname),"src/ui/ui-%s.h",info->c_name);
1215 	ctrl.hf = fopen(fname,"wb");
1216 	if (!ctrl.hf)
1217 	{
1218 	    fprintf(stderr,"!!! Can't create file: %s\n",fname);
1219 	    return ERR_CANT_CREATE;
1220 	}
1221 
1222 	ctrl.info = info++;
1223 	while ( ! ( info->type & (T_END|T_DEF_TOOL)) )
1224 	{
1225 	    if ( info->type & T_DEF_OPT )
1226 	    {
1227 		if ( info->type & F_OPT_GLOBAL )
1228 		{
1229 		    InsertStringField(&ctrl.gopt,info->c_name,false);
1230 		    ++ctrl.n_opt;
1231 		    //opt_index = 0 for global options
1232 		}
1233 		else
1234 		{
1235 		    InsertStringField(&ctrl.copt,info->c_name,false);
1236 		    info->index = ++ctrl.n_opt_specific;
1237 		}
1238 
1239 		if ( !info->help )
1240 		{
1241 		    // copy 'param' and 'help' info from previous tool
1242 		    const info_t * search;
1243 		    for ( search = info; search >= info_tab; search-- )
1244 			if ( search->type & T_DEF_OPT
1245 			    && search->help
1246 			    && !strcmp(search->c_name,info->c_name) )
1247 			{
1248 			    info->help  = search->help;
1249 			    if (!info->param)
1250 				info->param = search->param;
1251 			    break;
1252 			}
1253 		}
1254 
1255 		if ( !( info->type & F_HIDDEN ) )
1256 		{
1257 		    fprintf(ctrl.df,"#:def_opt( \"%s\", \"%s\", \"%s%s%s%s%s\", \\\n",
1258 			info->c_name, info->namelist,
1259 			info->type & F_OPT_COMMAND  ? "C" : "",
1260 			info->type & F_OPT_GLOBAL   ? "G" : "",
1261 			info->type & F_OPT_MULTIUSE ? "M" : "",
1262 			info->type & F_OPT_PARAM    ? "P" : "",
1263 			info->type & F_OPT_OPTPARAM ? "O" : "" );
1264 		    DumpText(ctrl.df,0,0,info->param,1,", \\\n");
1265 		    DumpText(ctrl.df,0,0,info->help,1," )\n\n");
1266 		}
1267 	    }
1268 	    else if ( info->type & T_DEF_CMD )
1269 	    {
1270 		info->index = ++ctrl.n_cmd;
1271 
1272 		if ( !info->help )
1273 		{
1274 		    // copy 'param' and 'help' info from previous tool
1275 		    const info_t * search = info-1;
1276 		    for ( search = info; search >= info_tab; search-- )
1277 			if ( search->type & T_DEF_CMD
1278 			    && search->help
1279 			    && !strcmp(search->c_name,info->c_name) )
1280 			{
1281 			    info->help  = search->help;
1282 			    if (!info->param)
1283 				info->param = search->param;
1284 			    break;
1285 			}
1286 		}
1287 
1288 		if ( !( info->type & F_HIDDEN ) )
1289 		{
1290 		    fprintf(ctrl.df,"#:def_cmd( \"%s\", \"%s\", \\\n",
1291 			info->c_name, info->namelist );
1292 		    DumpText(ctrl.df,0,0,info->param,1,", \\\n");
1293 		    DumpText(ctrl.df,0,0,info->help,1," )\n\n");
1294 		}
1295 	    }
1296 	    else if ( info->type & T_GRP_BEG )
1297 		info->index = ctrl.n_grp++; // NULL based
1298 	    else if ( info->type & (T_CMD_OPT|T_CMD_BEG|T_COPY_GRP|T_COPY_CMD) )
1299 	    {
1300 		const int type	= info->type & T_CMD_OPT  ? T_DEF_OPT
1301 				: info->type & T_COPY_GRP ? T_GRP_BEG
1302 				: T_DEF_CMD;
1303 
1304 		const info_t * search;
1305 		for ( search = ctrl.info; search < info; search++ )
1306 		    if ( search->type & type
1307 			&& !strcmp(search->c_name,info->c_name) )
1308 		    {
1309 			//PRINT("COPY INDEX #%x: %s\n",info->type,info->c_name);
1310 			info->index = search->index;
1311 			break;
1312 		    }
1313 	    }
1314 
1315 	    info++;
1316 	}
1317 	ctrl.end = info;
1318 
1319 	if (ctrl.n_cmd)
1320 	    ctrl.n_cmd++; // one more for CMD_NONE;
1321 
1322 	if (ctrl.n_opt_specific)
1323 	    ctrl.n_opt += ++ctrl.n_opt_specific;
1324 
1325 	TRACE("N: cmd=%u, grp=%u, opt=%d/%d\n",
1326 		ctrl.n_cmd, ctrl.n_grp, ctrl.n_opt_specific, ctrl.n_opt );
1327 	const enumError err = Generate(&ctrl);
1328 
1329 	fclose(ctrl.cf);
1330 	fclose(ctrl.hf);
1331 	ResetStringField(&ctrl.gopt);
1332 	ResetStringField(&ctrl.copt);
1333 	ResetStringField(&ctrl.opt_done);
1334 
1335 	if (err)
1336 	    return err;
1337     }
1338 
1339     AddTables(df);
1340     print_section(df,sep2,"END");
1341     fclose(df);
1342     fclose(nf);
1343     CloseAll();
1344     return ERR_OK;
1345 }
1346 
1347 //
1348 ///////////////////////////////////////////////////////////////////////////////
1349 ///////////////			    E N D			///////////////
1350 ///////////////////////////////////////////////////////////////////////////////
1351