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 #define _XOPEN_SOURCE 1
39 
40 #include <sys/time.h>
41 #include <sys/ioctl.h>
42 
43 #include <signal.h>
44 #include <time.h>
45 #include <ctype.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 
49 #if defined(__CYGWIN__)
50   #include <cygwin/fs.h>
51   #include <io.h>
52 #elif defined(__APPLE__)
53   #include <sys/disk.h>
54 #elif defined(__linux__)
55   #include <linux/fs.h>
56 #endif
57 
58 #include "version.h"
59 #include "lib-std.h"
60 #include "lib-sf.h"
61 #include "lib-bzip2.h"
62 #include "lib-lzma.h"
63 #include "wbfs-interface.h"
64 #include "crypt.h"
65 #include "titles.h"
66 #include "dclib-utf8.h"
67 
68 //
69 ///////////////////////////////////////////////////////////////////////////////
70 ///////////////			    Setup			///////////////
71 ///////////////////////////////////////////////////////////////////////////////
72 
73 enumProgID	prog_id			= PROG_UNKNOWN;
74 u32		revision_id		= SYSTEMID + REVISION_NUM;
75 ccp		progname		= "?";
76 ccp		search_path[6]		= {0};
77 ccp		lang_info		= 0;
78 volatile int	SIGINT_level		= 0;
79 volatile int	verbose			= 0;
80 volatile int	logging			= 0;
81 int		progress		= 0;
82 int		scan_progress		= 0;
83 SortMode	sort_mode		= SORT_DEFAULT;
84 ShowMode	opt_show_mode		= SHOW__DEFAULT;
85 wd_size_mode_t	opt_unit		= WD_SIZE_DEFAULT;
86 RepairMode	repair_mode		= REPAIR_NONE;
87 char		escape_char		= '%';
88 int		opt_force		= 0;
89 bool		use_utf8		= true;
90 enumOFT		output_file_type	= OFT_UNKNOWN;
91 uint		opt_wdf_version		= WDF_VERSION;
92 uint		opt_wdf_align		= WDF_ALIGN;
93 int		opt_truncate		= 0;
94 int		opt_split		= 0;
95 u64		opt_split_size		= 0;
96 ccp		opt_patch_file		= 0;
97 bool		opt_copy_gc		= false;
98 bool		opt_no_link		= false;
99 int		testmode		= 0;
100 int		newmode			= 0;
101 ccp		opt_dest		= 0;
102 bool		opt_overwrite		= false;
103 bool		opt_mkdir		= false;
104 int		opt_limit		= -1;
105 int		opt_file_limit		= -1;
106 int		opt_block_size		= -1;
107 int		print_old_style		= 0;
108 int		print_sections		= 0;
109 int		long_count		= 0;
110 int		brief_count		= 0;
111 int		ignore_count		= 0;
112 int		opt_technical		= 0;
113 u32		job_limit		= ~(u32)0;
114 enumIOMode	io_mode			= 0;
115 bool		opt_no_expand		= false;
116 u32		opt_recurse_depth	= DEF_RECURSE_DEPTH;
117 PreallocMode	prealloc_mode		= PREALLOC_DEFAULT;
118 
119 StringField_t	source_list;
120 StringField_t	recurse_list;
121 StringField_t	created_files;
122 char		iobuf [0x400000];		// global io buffer
123 const char	zerobuf[0x40000]	= {0};	// global zero buffer
124 
125 // 'tempbuf' is only for short usage
126 //	==> don't call other functions while using tempbuf
127 u8		* tempbuf		= 0;	// global temp buffer -> AllocTempBuffer()
128 size_t		tempbuf_size		= 0;	// size of 'tempbuf'
129 
130 const char sep_79[80] =		//  79 * '-' + NULL
131 	"----------------------------------------"
132 	"---------------------------------------";
133 
134 ///////////////////////////////////////////////////////////////////////////////
135 
sig_handler(int signum)136 static void sig_handler ( int signum )
137 {
138     static const char usr_msg[] =
139 	"\n"
140 	"****************************************************************\n"
141 	"***  SIGNAL USR%d CATCHED: VERBOSE LEVEL %s TO %-4d    ***\n"
142 	"***  THE EFFECT IS DELAYED UNTIL BEGINNING OF THE NEXT JOB.  ***\n"
143 	"****************************************************************\n";
144 
145     fflush(stdout);
146     switch(signum)
147     {
148       case SIGINT:
149       case SIGTERM:
150 	SIGINT_level++;
151 	TRACE("#SIGNAL# INT/TERM, level = %d\n",SIGINT_level);
152 
153 	switch(SIGINT_level)
154 	{
155 	  case 1:
156 	    fprintf(stderr,
157 		"\n"
158 		"****************************************************************\n"
159 		"***  PROGRAM INTERRUPTED BY USER (LEVEL=1).                  ***\n"
160 		"***  PROGRAM WILL TERMINATE AFTER CURRENT JOB HAS FINISHED.  ***\n"
161 		"****************************************************************\n" );
162 	    break;
163 
164 	  case 2:
165 	    fprintf(stderr,
166 		"\n"
167 		"*********************************************************\n"
168 		"***  PROGRAM INTERRUPTED BY USER (LEVEL=2).           ***\n"
169 		"***  PROGRAM WILL TRY TO TERMINATE NOW WITH CLEANUP.  ***\n"
170 		"*********************************************************\n" );
171 	    break;
172 
173 	  default:
174 	    fprintf(stderr,
175 		"\n"
176 		"*************************************************************\n"
177 		"***  PROGRAM INTERRUPTED BY USER (LEVEL>=3).              ***\n"
178 		"***  PROGRAM WILL TERMINATE IMMEDIATELY WITHOUT CLEANUP.  ***\n"
179 		"*************************************************************\n" );
180 	    fflush(stderr);
181 	    exit(ERR_INTERRUPT);
182 	  }
183 	  break;
184 
185       case SIGUSR1:
186 	if ( verbose >= -1 )
187 	    verbose--;
188 	TRACE("#SIGNAL# USR1: verbose = %d\n",verbose);
189 	fprintf(stderr,usr_msg,1,"DECREASED",verbose);
190 	break;
191 
192       case SIGUSR2:
193 	if ( verbose < 4 )
194 	    verbose++;
195 	TRACE("#SIGNAL# USR2: verbose = %d\n",verbose);
196 	fprintf(stderr,usr_msg,2,"INCREASED",verbose);
197 	break;
198 
199       default:
200 	TRACE("#SIGNAL# %d\n",signum);
201     }
202     fflush(stderr);
203 }
204 
205 ///////////////////////////////////////////////////////////////////////////////
206 
SetupLib(int argc,char ** argv,ccp p_progname,enumProgID prid)207 void SetupLib ( int argc, char ** argv, ccp p_progname, enumProgID prid )
208 {
209  #ifdef DEBUG
210     if (!TRACE_FILE)
211     {
212 	char fname[100];
213 	snprintf(fname,sizeof(fname),"_trace-%s.tmp",p_progname);
214 	TRACE_FILE = fopen(fname,"w");
215 	if (!TRACE_FILE)
216 	    fprintf(stderr,"open TRACE_FILE failed: %s\n",fname);
217     }
218  #endif
219 
220  #ifdef __BYTE_ORDER
221     TRACE("__BYTE_ORDER=%d\n",__BYTE_ORDER);
222  #endif
223  #ifdef LITTLE_ENDIAN
224     TRACE("LITTLE_ENDIAN=%d\n",LITTLE_ENDIAN);
225  #endif
226  #ifdef BIG_ENDIAN
227     TRACE("BIG_ENDIAN=%d\n",BIG_ENDIAN);
228  #endif
229 
230     // numeric types
231 
232     TRACE("- int\n");
233     TRACE_SIZEOF(bool);
234     TRACE_SIZEOF(short);
235     TRACE_SIZEOF(int);
236     TRACE_SIZEOF(long);
237     TRACE_SIZEOF(long long);
238     TRACE_SIZEOF(size_t);
239     TRACE_SIZEOF(off_t);
240 
241     TRACE_SIZEOF(int8_t);
242     TRACE_SIZEOF(int16_t);
243     TRACE_SIZEOF(int32_t);
244     TRACE_SIZEOF(int64_t);
245     TRACE_SIZEOF(uint8_t);
246     TRACE_SIZEOF(uint16_t);
247     TRACE_SIZEOF(uint32_t);
248     TRACE_SIZEOF(uint64_t);
249 
250     TRACE_SIZEOF(u8);
251     TRACE_SIZEOF(u16);
252     TRACE_SIZEOF(u32);
253     TRACE_SIZEOF(u64);
254     TRACE_SIZEOF(s8);
255     TRACE_SIZEOF(s16);
256     TRACE_SIZEOF(s32);
257     TRACE_SIZEOF(s64);
258     TRACE_SIZEOF(be16_t);
259     TRACE_SIZEOF(be32_t);
260     TRACE_SIZEOF(be64_t);
261 
262     // base types A-Z
263 
264     TRACE("-\n");
265     TRACE_SIZEOF(AWData_t);
266     TRACE_SIZEOF(AWRecord_t);
267     TRACE_SIZEOF(CISO_Head_t);
268     TRACE_SIZEOF(CISO_Info_t);
269     TRACE_SIZEOF(CheckDisc_t);
270     TRACE_SIZEOF(CheckWBFS_t);
271     TRACE_SIZEOF(CommandTab_t);
272     TRACE_SIZEOF(DataArea_t);
273     TRACE_SIZEOF(DataList_t);
274     TRACE_SIZEOF(Diff_t);
275     TRACE_SIZEOF(File_t);
276     TRACE_SIZEOF(FileAttrib_t);
277     TRACE_SIZEOF(FileCache_t);
278     TRACE_SIZEOF(FileIndex_t);
279     TRACE_SIZEOF(FileIndexData_t);
280     TRACE_SIZEOF(FileIndexItem_t);
281     TRACE_SIZEOF(FileMapItem_t);
282     TRACE_SIZEOF(FileMap_t);
283     TRACE_SIZEOF(FilePattern_t);
284     TRACE_SIZEOF(ID_DB_t);
285     TRACE_SIZEOF(ID_t);
286     //TRACE_SIZEOF(InfoCommand_t);
287     //TRACE_SIZEOF(InfoOption_t);
288     //TRACE_SIZEOF(InfoUI_t);
289     TRACE_SIZEOF(IOData_t);
290     TRACE_SIZEOF(IsoMappingItem_t);
291     TRACE_SIZEOF(IsoMapping_t);
292     TRACE_SIZEOF(Iterator_t);
293     TRACE_SIZEOF(MemMapItem_t);
294     TRACE_SIZEOF(MemMap_t);
295     TRACE_SIZEOF(ParamField_t);
296     TRACE_SIZEOF(ParamFieldItem_t);
297     TRACE_SIZEOF(ParamFieldType_t);
298     TRACE_SIZEOF(ParamList_t);
299     TRACE_SIZEOF(PartitionInfo_t);
300     TRACE_SIZEOF(PrintTime_t);
301     TRACE_SIZEOF(ReadPatch_t);
302     TRACE_SIZEOF(RegionInfo_t);
303     TRACE_SIZEOF(StringField_t);
304     TRACE_SIZEOF(IdField_t);
305     TRACE_SIZEOF(IdItem_t);
306     TRACE_SIZEOF(StringList_t);
307     TRACE_SIZEOF(SubstString_t);
308     TRACE_SIZEOF(SuperFile_t);
309     TRACE_SIZEOF(TDBfind_t);
310     TRACE_SIZEOF(Verify_t);
311     TRACE_SIZEOF(WBFS_t);
312     TRACE_SIZEOF(WDF_Chunk_t);
313     TRACE_SIZEOF(WDF_Head_t);
314     TRACE_SIZEOF(WDiscInfo_t);
315     TRACE_SIZEOF(WDiscListItem_t);
316     TRACE_SIZEOF(WDiscList_t);
317     TRACE_SIZEOF(WiiFstFile_t);
318     TRACE_SIZEOF(WiiFstInfo_t);
319     TRACE_SIZEOF(WiiFstPart_t);
320     TRACE_SIZEOF(WiiFst_t);
321     TRACE_SIZEOF(WritePatch_t);
322 
323     // base types a-z
324 
325     TRACE("-\n");
326     TRACE_SIZEOF(aes_key_t);
327 
328     TRACE_SIZEOF(cert_chain_t);
329     TRACE_SIZEOF(cert_data_t);
330     TRACE_SIZEOF(cert_head_t);
331     TRACE_SIZEOF(cert_item_t);
332     TRACE_SIZEOF(cert_stat_flags_t);
333     TRACE_SIZEOF(cert_stat_t);
334 
335     TRACE_SIZEOF(dcUnicodeTripel);
336     TRACE_SIZEOF(dol_header_t);
337     TRACE_SIZEOF(id6_t);
338     TRACE_SIZEOF(sha1_hash_t);
339 
340     TRACE_SIZEOF(wbfs_disc_info_t);
341     TRACE_SIZEOF(wbfs_disc_t);
342     TRACE_SIZEOF(wbfs_head_t);
343     TRACE_SIZEOF(wbfs_inode_info_t);
344     TRACE_SIZEOF(wbfs_param_t);
345     TRACE_SIZEOF(wbfs_t);
346 
347     TRACE_SIZEOF(wd_boot_t);
348     TRACE_SIZEOF(wd_compression_t);
349     TRACE_SIZEOF(wd_disc_t);
350     TRACE_SIZEOF(wd_file_list_t);
351     TRACE_SIZEOF(wd_file_t);
352     TRACE_SIZEOF(wd_fst_item_t);
353     TRACE_SIZEOF(wd_header_128_t);
354     TRACE_SIZEOF(wd_header_t);
355     TRACE_SIZEOF(wd_icm_t);
356     TRACE_SIZEOF(wd_ipm_t);
357     TRACE_SIZEOF(wd_iterator_t);
358     TRACE_SIZEOF(wd_memmap_item_t);
359     TRACE_SIZEOF(wd_memmap_t);
360     TRACE_SIZEOF(wd_modify_t);
361     TRACE_SIZEOF(wd_part_control_t);
362     TRACE_SIZEOF(wd_part_header_t);
363     TRACE_SIZEOF(wd_part_sector_t);
364     TRACE_SIZEOF(wd_part_t);
365     TRACE_SIZEOF(wd_part_type_t);
366     TRACE_SIZEOF(wd_patch_mode_t);
367     TRACE_SIZEOF(wd_pfst_t);
368     TRACE_SIZEOF(wd_pname_mode_t);
369     TRACE_SIZEOF(wd_print_fst_t);
370     TRACE_SIZEOF(wd_ptab_info_t);
371     TRACE_SIZEOF(wd_ptab_entry_t);
372     TRACE_SIZEOF(wd_ptab_t);
373     TRACE_SIZEOF(wd_region_t);
374     TRACE_SIZEOF(wd_reloc_t);
375     TRACE_SIZEOF(wd_scrubbed_t);
376     TRACE_SIZEOF(wd_sector_status_t);
377     TRACE_SIZEOF(wd_select_item_t);
378     TRACE_SIZEOF(wd_select_mode_t);
379     TRACE_SIZEOF(wd_select_t);
380     TRACE_SIZEOF(wd_ticket_t);
381     TRACE_SIZEOF(wd_tmd_content_t);
382     TRACE_SIZEOF(wd_tmd_t);
383     TRACE_SIZEOF(wd_trim_mode_t);
384     TRACE_SIZEOF(wd_usage_t);
385 
386     TRACE_SIZEOF(wia_controller_t);
387     TRACE_SIZEOF(wia_segment_t);
388     TRACE_SIZEOF(wia_disc_t);
389     TRACE_SIZEOF(wia_exception_t);
390     TRACE_SIZEOF(wia_except_list_t);
391     TRACE_SIZEOF(wia_file_head_t);
392     TRACE_SIZEOF(wia_group_t);
393     TRACE_SIZEOF(wia_part_data_t);
394     TRACE_SIZEOF(wia_part_t);
395     TRACE_SIZEOF(wia_raw_data_t);
396 
397     TRACE_SIZEOF(WIT_PATCH_MAGIC);
398     TRACE_SIZEOF(wpat_magic);
399     TRACE_SIZEOF(wpat_comment_t);
400     TRACE_SIZEOF(wpat_data_t);
401     TRACE_SIZEOF(wpat_filename_t);
402     TRACE_SIZEOF(wpat_filenames_t);
403     TRACE_SIZEOF(wpat_header_t);
404     TRACE_SIZEOF(wpat_patch_file_t);
405     TRACE_SIZEOF(wpat_size_t);
406     TRACE_SIZEOF(wpat_toc_file_t);
407     TRACE_SIZEOF(wpat_toc_header_t);
408     TRACE_SIZEOF(wpat_type_t);
409 
410     // assertions
411 
412     TRACE("-\n");
413     ASSERT( 1 == sizeof(u8)  );
414     ASSERT( 2 == sizeof(u16) );
415     ASSERT( 4 == sizeof(u32) );
416     ASSERT( 8 == sizeof(u64) );
417     ASSERT( 1 == sizeof(s8)  );
418     ASSERT( 2 == sizeof(s16) );
419     ASSERT( 4 == sizeof(s32) );
420     ASSERT( 8 == sizeof(s64) );
421 
422     ASSERT( Count1Bits32(sizeof(WDF_Hole_t)) == 1 );
423     ASSERT( sizeof(CISO_Head_t) == CISO_HEAD_SIZE );
424 
425     ASSERT(  79 == strlen(sep_79) );
426     ASSERT( 200 == strlen(wd_sep_200) );
427 
428     TRACE("- file formats:\n");
429     validate_file_format_sizes(1);
430     TRACE("-\n");
431 
432     //----- setup textmode for cygwin stdout+stderr
433 
434     #if defined(__CYGWIN__)
435 	setmode(fileno(stdout),O_TEXT);
436 	setmode(fileno(stderr),O_TEXT);
437 	//setlocale(LC_ALL,"en_US.utf-8");
438     #endif
439 
440 
441     //----- setup prog id
442 
443     prog_id = prid;
444 
445     #ifdef WIIMM_TRUNK
446 	revision_id = (prid << 20) + SYSTEMID + REVISION_NUM + REVID_WIIMM_TRUNK;
447     #elif defined(WIIMM)
448 	revision_id = (prid << 20) + SYSTEMID + REVISION_NUM + REVID_WIIMM;
449     #else
450 	revision_id = (prid << 20) + SYSTEMID + REVISION_NUM + REVID_UNKNOWN;
451     #endif
452 
453 
454     //----- setup progname
455 
456     if ( argc > 0 && *argv && **argv )
457 	p_progname = *argv;
458     progname = strrchr(p_progname,'/');
459     progname = progname ? progname+1 : p_progname;
460     argv[0] = (char*)progname;
461 
462     TRACE("##PROG## REV-ID=%08x, PROG-ID=%d, PROG-NAME=%s\n",
463 		revision_id, prog_id, progname );
464 
465 
466     //----- setup signals
467 
468     static const int sigtab[] = { SIGTERM, SIGINT, SIGUSR1, SIGUSR2, -1 };
469     int i;
470     for ( i = 0; sigtab[i] >= 0; i++ )
471     {
472 	struct sigaction sa;
473 	memset(&sa,0,sizeof(sa));
474 	sa.sa_handler = &sig_handler;
475 	sigaction(sigtab[i],&sa,0);
476     }
477 
478 
479     //----- setup search_path
480 
481     ccp *sp = search_path, *sp2;
482     ASSERT( sizeof(search_path)/sizeof(*search_path) > 5 );
483 
484     // determine program path
485     char proc_path[30];
486     snprintf(proc_path,sizeof(proc_path),"/proc/%u/exe",getpid());
487     TRACE("PROC-PATH: %s\n",proc_path);
488 
489     static char share[] = "/share/wit/";
490     static char local_share[] = "/usr/local/share/wit/";
491 
492     char path[PATH_MAX];
493     if (readlink(proc_path,path,sizeof(path)))
494     {
495 	// program path found!
496 	TRACE("PROG-PATH: %s\n",path);
497 
498 	char * file_ptr = strrchr(path,'/');
499 	if ( file_ptr )
500 	{
501 	    // seems to be a real path -> terminate string behind '/'
502 	    *++file_ptr = 0;
503 
504 	    #ifdef TEST
505 		// for development: append 'share' dir
506 		StringCopyE(file_ptr,path+sizeof(path),"share/");
507 		*sp = STRDUP(path);
508 		TRACE("SEARCH_PATH[%zd] = %s\n",sp-search_path,*sp);
509 		sp++;
510 		*file_ptr = 0;
511 	    #endif
512 
513 	    *sp = STRDUP(path);
514 	    TRACE("SEARCH_PATH[%zd] = %s\n",sp-search_path,*sp);
515 	    sp++;
516 
517 	    if ( file_ptr-5 >= path && !memcmp(file_ptr-4,"/bin/",5) )
518 	    {
519 		StringCopyS(file_ptr-5,sizeof(path),share);
520 		*sp = STRDUP(path);
521 		TRACE("SEARCH_PATH[%zd] = %s\n",sp-search_path,*sp);
522 		sp++;
523 	    }
524 	}
525     }
526 
527     // insert 'local_share' if not already done
528 
529     for ( sp2 = search_path; sp2 < sp && strcmp(*sp2,local_share); sp2++ )
530 	;
531     if ( sp2 == sp )
532     {
533 	*sp = STRDUP(local_share);
534 	TRACE("SEARCH_PATH[%zd] = %s\n",sp-search_path,*sp);
535 	sp++;
536     }
537 
538     // insert CWD if not already done
539     getcwd(path,sizeof(path)-1);
540     strcat(path,"/");
541     for ( sp2 = search_path; sp2 < sp && strcmp(*sp2,path); sp2++ )
542 	;
543     if ( sp2 == sp )
544     {
545 	*sp = STRDUP("./");
546 	TRACE("SEARCH_PATH[%zd] = %s\n",sp-search_path,*sp);
547 	sp++;
548     }
549 
550     *sp = 0;
551     ASSERT( sp - search_path < sizeof(search_path)/sizeof(*search_path) );
552 
553 
554     //----- setup language info
555 
556     char * wit_lang = getenv("WIT_LANG");
557     if ( wit_lang && *wit_lang )
558     {
559 	lang_info = STRDUP(wit_lang);
560 	TRACE("LANG_INFO = %s [WIT_LANG]\n",lang_info);
561     }
562     else
563     {
564 	char * lc_ctype = getenv("LC_CTYPE");
565 	if (lc_ctype)
566 	{
567 	    char * lc_ctype_end = lc_ctype;
568 	    while ( *lc_ctype_end >= 'a' && *lc_ctype_end <= 'z' )
569 		lc_ctype_end++;
570 	    const int len = lc_ctype_end - lc_ctype;
571 	    if ( len > 0 )
572 	    {
573 		char * temp = MALLOC(len+1);
574 		memcpy(temp,lc_ctype,len);
575 		temp[len] = 0;
576 		lang_info = temp;
577 		TRACE("LANG_INFO = %s\n",lang_info);
578 	    }
579 	}
580     }
581 
582 
583     //----- setup common key
584     {
585 	static u8 key_base[] = "Wiimms WBFS Tool";
586 	static u8 key_mask[WD_CKEY__N][WII_KEY_SIZE] =
587 	{
588 	    {
589 		0x25, 0x00, 0x94, 0x12,  0x2a, 0x3e, 0x4e, 0x1f,
590 		0x03, 0x2d, 0xc5, 0xc9,  0x30, 0x2a, 0x58, 0xab
591 	    },
592 	    {
593 		0xad, 0x5c, 0x95, 0x84,  0x80, 0xda, 0x93, 0xd5,
594 		0x58, 0x06, 0xfe, 0x77,  0xf9, 0xe7, 0x69, 0x22
595 	    },
596 	};
597 
598 	u8 h1[WII_HASH_SIZE], h2[WII_HASH_SIZE], key[WII_KEY_SIZE];
599 
600 	SHA1(key_base,WII_KEY_SIZE,h1);
601 	int ci;
602 	for ( ci = 0; ci < WD_CKEY__N; ci++ )
603 	{
604 	    SHA1(wd_get_common_key(ci),WII_KEY_SIZE,h2);
605 
606 	    int i;
607 	    for ( i = 0; i < WII_KEY_SIZE; i++ )
608 		key[i] = key_mask[ci][i] ^ h1[i] ^ h2[i];
609 
610 	    wd_set_common_key(ci,key);
611 	}
612     }
613 
614     //----- setup data structures
615 
616     InitializeAllFilePattern();
617     wd_initialize_select(&part_selector);
618 
619 
620     //----- verify oft_info
621 
622  #if defined(TEST) || defined(DEBUG)
623     {
624 	ASSERT( OFT__N + 1 == sizeof(oft_info)/sizeof(*oft_info) );
625 	enumOFT oft;
626 	for ( oft = 0; oft <= OFT__N; oft++ )
627 	{
628 	    ASSERT( oft_info[oft].oft == oft );
629 	}
630     }
631  #endif
632 }
633 
634 ///////////////////////////////////////////////////////////////////////////////
635 
CloseAll()636 void CloseAll()
637 {
638     CloseWBFSCache();
639 }
640 
641 ///////////////////////////////////////////////////////////////////////////////
642 
CheckEnvOptions(ccp varname,check_opt_func func)643 enumError CheckEnvOptions ( ccp varname, check_opt_func func )
644 {
645     TRACE("CheckEnvOptions(%s,%p)\n",varname,func);
646 
647     ccp env = getenv(varname);
648     if ( !env || !*env )
649 	return ERR_OK;
650 
651     TRACE("env[%s] = %s\n",varname,env);
652 
653     const int envlen = strlen(env);
654     char * buf = MALLOC(envlen+1);
655     char * dest = buf;
656 
657     int argc = 1; // argv[0] = progname
658     ccp src = env;
659     while (*src)
660     {
661 	while ( *src > 0 && *src <= ' ' ) // skip blanks & control
662 	    src++;
663 
664 	if (!*src)
665 	    break;
666 
667 	argc++;
668 	while ( *(u8*)src > ' ' )
669 	    *dest++ = *src++;
670 	*dest++ = 0;
671 	ASSERT( dest <= buf+envlen+1 );
672     }
673     TRACE("argc = %d\n",argc);
674 
675     char ** argv = MALLOC((argc+1)*sizeof(*argv));
676     argv[0] = (char*)progname;
677     argv[argc] = 0;
678     dest = buf;
679     int i;
680     for ( i = 1; i < argc; i++ )
681     {
682 	TRACE("argv[%d] = %s\n",i,dest);
683 	argv[i] = dest;
684 	while (*dest)
685 	    dest++;
686 	dest++;
687 	ASSERT( dest <= buf+envlen+1 );
688     }
689 
690     enumError stat = func(argc,argv,true);
691     if (stat)
692 	fprintf(stderr,
693 	    "Error while scanning the environment variable '%s'\n",varname);
694 
695     // don't FREE() because is's possible that there are pointers to arguments
696     //FREE(argv);
697     //FREE(buf);
698 
699     return stat;
700 }
701 
702 //
703 ///////////////////////////////////////////////////////////////////////////////
704 ///////////////			error messages			///////////////
705 ///////////////////////////////////////////////////////////////////////////////
706 
GetErrorName(int stat)707 ccp GetErrorName ( int stat )
708 {
709     switch(stat)
710     {
711 	case ERR_OK:			return "OK";
712 	case ERR_DIFFER:		return "FILES DIFFER";
713 	case ERR_NOTHING_TO_DO:		return "NOTHING TO DO";
714 	case ERR_NO_SOURCE_FOUND:	return "NO SOURCE FOUND";
715 	case ERR_JOB_IGNORED:		return "JOB IGNORED";
716 	case ERR_WARNING:		return "WARNING";
717 
718 	case ERR_INVALID_FILE:		return "INVALID FILE";
719 	case ERR_INVALID_VERSION:	return "INVALID VERSION";
720 
721 	case ERR_NO_WDF:		return "NO WDF";
722 	case ERR_WDF_VERSION:		return "WDF VERSION NOT SUPPORTED";
723 	case ERR_WDF_SPLIT:		return "SPLITTED WDF NOT SUPPORTED";
724 	case ERR_WDF_INVALID:		return "INVALID WDF";
725 
726 	case ERR_NO_CISO:		return "NO WDF";
727 	case ERR_CISO_INVALID:		return "INVALID WDF";
728 
729 	case ERR_WPART_INVALID:		return "INVALID WII PARTITION";
730 	case ERR_WDISC_INVALID:		return "INVALID WII DISC";
731 	case ERR_WDISC_NOT_FOUND:	return "WII DISC NOT FOUND";
732 
733 	case ERR_NO_WBFS_FOUND:		return "NO WBFS FOUND";
734 	case ERR_TO_MUCH_WBFS_FOUND:	return "TO MUCH WBFS FOUND";
735 	case ERR_WBFS_INVALID:		return "INVALID WBFS";
736 
737 	case ERR_NO_WIA:		return "NO WIA FOUND";
738 	case ERR_WIA_INVALID:		return "INVALID WIA";
739 	case ERR_BZIP2:			return "BZIP2 ERROR";
740 	case ERR_LZMA:			return "LZMA ERROR";
741 
742 	case ERR_ALREADY_EXISTS:	return "FILE ALREADY EXISTS";
743 	case ERR_CANT_OPEN:		return "CAN'T OPEN FILE";
744 	case ERR_CANT_CREATE:		return "CAN'T CREATE FILE";
745 	case ERR_CANT_CREATE_DIR:	return "CAN'T CREATE DIRECTORY";
746 	case ERR_WRONG_FILE_TYPE:	return "WRONG FILE TYPE";
747 	case ERR_READ_FAILED:		return "READ FILE FAILED";
748 	case ERR_REMOVE_FAILED:		return "REMOVE FILE FAILED";
749 	case ERR_WRITE_FAILED:		return "WRITE FILE FAILED";
750 
751 	case ERR_WBFS:			return "WBFS ERROR";
752 
753 	case ERR_MISSING_PARAM:		return "MISSING PARAMETERS";
754 	case ERR_SEMANTIC:		return "SEMANTIC ERROR";
755 	case ERR_SYNTAX:		return "SYNTAX ERROR";
756 
757 	case ERR_INTERRUPT:		return "INTERRUPT";
758 
759 	case ERR_ERROR:			return "ERROR";
760 
761 	case ERR_NOT_IMPLEMENTED:	return "NOT IMPLEMENTED YET";
762 	case ERR_INTERNAL:		return "INTERNAL ERROR";
763 	case ERR_OUT_OF_MEMORY:		return "OUT OF MEMORY";
764 	case ERR_FATAL:			return "FATAL ERROR";
765     }
766     return "?";
767 }
768 
769 ///////////////////////////////////////////////////////////////////////////////
770 
GetErrorText(int stat)771 ccp GetErrorText ( int stat )
772 {
773     switch(stat)
774     {
775 	case ERR_OK:			return "Ok";
776 	case ERR_DIFFER:		return "Files differ";
777 	case ERR_NOTHING_TO_DO:		return "Nothing to do";
778 	case ERR_NO_SOURCE_FOUND:	return "No source file found";
779 	case ERR_JOB_IGNORED:		return "Job ignored";
780 	case ERR_WARNING:		return "Unspecific warning";
781 
782 	case ERR_INVALID_FILE:		return "File has invalid content";
783 	case ERR_INVALID_VERSION:	return "File version not supported";
784 
785 	case ERR_NO_WDF:		return "File is not a WDF";
786 	case ERR_WDF_VERSION:		return "WDF version not supported";
787 	case ERR_WDF_SPLIT:		return "Splitted WDF not supported";
788 	case ERR_WDF_INVALID:		return "File is an invalid WDF";
789 
790 	case ERR_NO_CISO:		return "File is not a CISO";
791 	case ERR_CISO_INVALID:		return "File is an invalid CISO";
792 
793 	case ERR_WPART_INVALID:		return "Invalid Wii partition";
794 	case ERR_WDISC_INVALID:		return "Invalid Wii disc";
795 	case ERR_WDISC_NOT_FOUND:	return "Wii disc not found";
796 
797 	case ERR_NO_WBFS_FOUND:		return "No WBFS found";
798 	case ERR_TO_MUCH_WBFS_FOUND:	return "To much WBFS found";
799 	case ERR_WBFS_INVALID:		return "Invalid WBFS";
800 
801 	case ERR_NO_WIA:		return "File is not a WIA";
802 	case ERR_WIA_INVALID:		return "File is an invalid WIA";
803 	case ERR_BZIP2:			return "A bzip2 error occurred";
804 	case ERR_LZMA:			return "A lzma error occurred";
805 
806 	case ERR_ALREADY_EXISTS:	return "File already exists";
807 	case ERR_CANT_OPEN:		return "Can't open file";
808 	case ERR_CANT_CREATE:		return "Can't create file";
809 	case ERR_CANT_CREATE_DIR:	return "Can't create directory";
810 	case ERR_WRONG_FILE_TYPE:	return "Wrong type of file";
811 	case ERR_READ_FAILED:		return "Reading from file failed";
812 	case ERR_REMOVE_FAILED:		return "Removing a file failed";
813 	case ERR_WRITE_FAILED:		return "Writing to file failed";
814 
815 	case ERR_WBFS:			return "A WBFS error occurred";
816 
817 	case ERR_MISSING_PARAM:		return "Missing ate least one parameter";
818 	case ERR_SEMANTIC:		return "Semantic error";
819 	case ERR_SYNTAX:		return "Syntax error";
820 
821 	case ERR_INTERRUPT:		return "Program interrupted by user";
822 
823 	case ERR_ERROR:			return "Unspecific error";
824 
825 	case ERR_NOT_IMPLEMENTED:	return "Not implemented yet";
826 	case ERR_INTERNAL:		return "Internal error";
827 	case ERR_OUT_OF_MEMORY:		return "Allocation of dynamic memory failed";
828 	case ERR_FATAL:			return "Unspecific fatal error";
829     }
830     return "?";
831 }
832 
833 ///////////////////////////////////////////////////////////////////////////////
834 
835 enumError last_error = ERR_OK;
836 enumError max_error  = ERR_OK;
837 u32 error_count = 0;
838 
839 ///////////////////////////////////////////////////////////////////////////////
840 
PrintError(ccp func,ccp file,uint line,int syserr,enumError err_code,ccp format,...)841 int PrintError ( ccp func, ccp file, uint line,
842 		int syserr, enumError err_code, ccp format, ... )
843 {
844     fflush(stdout);
845     char msg[1000];
846     const int plen = strlen(progname)+2;
847 
848     if (format)
849     {
850 	va_list arg;
851 	va_start(arg,format);
852 	vsnprintf(msg,sizeof(msg),format,arg);
853 	msg[sizeof(msg)-2] = 0;
854 	va_end(arg);
855 
856 	const int mlen = strlen(msg);
857 	if ( mlen > 0 && msg[mlen-1] != '\n' )
858 	{
859 	    msg[mlen]   = '\n';
860 	    msg[mlen+1] = 0;
861 	}
862     }
863     else
864 	StringCat2S(msg,sizeof(msg),GetErrorText(err_code),"\n");
865 
866     ccp prefix = err_code == ERR_OK ? "" : err_code <= ERR_WARNING ? "! " : "!! ";
867     const int fw = GetTermWidth(80,40) - 1;
868 
869  #ifdef DEBUG
870     TRACE("%s%s #%d [%s] in %s() @ %s#%d\n",
871 		prefix, err_code <= ERR_WARNING ? "WARNING" : "ERROR",
872 		err_code, GetErrorName(err_code), func, file, line );
873     TRACE("%s%*s%s",prefix,plen,"",msg);
874     if (syserr)
875 	TRACE("!! ERRNO=%d: %s\n",syserr,strerror(syserr));
876     fflush(TRACE_FILE);
877  #endif
878 
879  #if defined(EXTENDED_ERRORS)
880     if ( err_code > ERR_WARNING )
881  #else
882     if ( err_code >= ERR_NOT_IMPLEMENTED )
883  #endif
884     {
885 	if ( err_code > ERR_WARNING )
886 	    fprintf(stderr,"%s%s: ERROR #%d [%s] in %s() @ %s#%d\n",
887 		prefix, progname, err_code, GetErrorName(err_code), func, file, line );
888 	else
889 	    fprintf(stderr,"%s%s: WARNING in %s() @ %s#%d\n",
890 		prefix, progname, func, file, line );
891 
892      #if defined(EXTENDED_ERRORS) && EXTENDED_ERRORS > 1
893 	fprintf(stderr,"%s -> %s/%s?annotate=%d#l%d\n",
894 		prefix, URI_VIEWVC, file, REVISION_NEXT, line );
895      #endif
896 
897 	fputs(prefix,stderr);
898 	PutLines(stderr,plen,fw,0,prefix,msg);
899     }
900     else
901     {
902 	fprintf(stderr,"%s%s:",prefix,progname);
903 	PutLines(stderr,plen,fw,strlen(progname)+1,prefix,msg);
904     }
905 
906     if (syserr)
907     {
908 	fprintf(stderr,"%s%*s-> ",prefix,plen,"");
909 	snprintf(msg,sizeof(msg),"%s [%d]",strerror(syserr),syserr);
910 	PutLines(stderr,plen+3,fw,plen+3,prefix,msg);
911     }
912     fflush(stderr);
913 
914     if ( err_code > ERR_OK )
915 	error_count++;
916 
917     last_error = err_code;
918     if ( max_error < err_code )
919 	max_error = err_code;
920 
921     if ( err_code > ERR_NOT_IMPLEMENTED )
922 	exit(err_code);
923 
924     return err_code;
925 }
926 
927 ///////////////////////////////////////////////////////////////////////////////
928 
HexDump16(FILE * f,int indent,u64 addr,const void * data,size_t count)929 void HexDump16 ( FILE * f, int indent, u64 addr,
930 		 const void * data, size_t count )
931 {
932     HexDump(f,indent,addr,4,16,data,count);
933 }
934 
935 //-----------------------------------------------------------------------------
936 
HexDump(FILE * f,int indent,u64 addr,int addr_fw,int row_len,const void * p_data,size_t count)937 void HexDump ( FILE * f, int indent, u64 addr, int addr_fw, int row_len,
938 		const void * p_data, size_t count )
939 {
940     if ( !f || !p_data || !count )
941 	return;
942 
943     const int MAX_LEN = 256;
944     char buf[MAX_LEN+1];
945     const u8 * data = p_data;
946 
947     indent = NormalizeIndent(indent);
948     addr_fw = NormalizeIndent(addr_fw);
949 
950     const bool show_ascii = row_len >= 0;
951     if ( row_len < 0 )
952 	row_len = -row_len;
953     else if ( row_len < 1 )
954 	row_len = 16;
955     else if ( row_len > MAX_LEN )
956 	row_len = MAX_LEN;
957 
958 
959     if ( (s64)addr != -1 )
960     {
961 	const int fw = snprintf(buf,sizeof(buf),"%llx",addr+count-1);
962 	if ( addr_fw < fw )
963 	     addr_fw = fw;
964     }
965 
966     while ( count > 0 )
967     {
968 	if ( (s64)addr == -1 )
969 	    fprintf(f,"%*s", indent,"" );
970 	else
971 	{
972 	    fprintf(f,"%*s%*llx:", indent,"", addr_fw, addr );
973 	    addr += row_len;
974 	}
975 	char * dest = buf;
976 
977 	int i;
978 	for ( i = 0; i < row_len; i++ )
979 	{
980 	    u8 ch = *data++;
981 	    if ( count > 0 )
982 	    {
983 		count--;
984 		fprintf(f,"%s%02x ", i&3 ? "" : " ", ch );
985 		*dest++ = ch < ' ' || ch >= 0x7f ? '.' : ch;
986 	    }
987 	    else
988 		fprintf(f,"%s   ", i&3 ? "" : " " );
989 	}
990 	*dest = 0;
991 	if (show_ascii)
992 	    fprintf(f,":%s:\n",buf);
993 	else
994 	    fputc('\n',f);
995     }
996 }
997 
998 //
999 ///////////////////////////////////////////////////////////////////////////////
1000 ///////////////			terminal cap			///////////////
1001 ///////////////////////////////////////////////////////////////////////////////
1002 
1003 u32 opt_width = 0;
1004 
1005 ///////////////////////////////////////////////////////////////////////////////
1006 
ScanOptWidth(ccp source)1007 int ScanOptWidth ( ccp source )
1008 {
1009     return ERR_OK != ScanSizeOptU32(
1010 			&opt_width,		// u32 * num
1011 			source,			// ccp source
1012 			1,			// default_factor1
1013 			0,			// int force_base
1014 			"width",		// ccp opt_name
1015 			40,			// u64 min
1016 			10000,			// u64 max
1017 			1,			// u32 multiple
1018 			0,			// u32 pow2
1019 			true			// bool print_err
1020 			);
1021 }
1022 
1023 ///////////////////////////////////////////////////////////////////////////////
1024 
GetTermWidth(int default_value,int min_value)1025 int GetTermWidth ( int default_value, int min_value )
1026 {
1027     int term_width = opt_width > 0
1028 			? opt_width
1029 			: GetTermWidthFD(STDOUT_FILENO,-1,min_value);
1030     if ( term_width <= 0 )
1031 	term_width = GetTermWidthFD(STDERR_FILENO,-1,min_value);
1032 
1033     return term_width > 0 ? term_width : default_value;
1034 }
1035 
1036 ///////////////////////////////////////////////////////////////////////////////
1037 
GetTermWidthFD(int fd,int default_value,int min_value)1038 int GetTermWidthFD ( int fd, int default_value, int min_value )
1039 {
1040     TRACE("GetTermWidthFD(%d,%d)\n",fd,default_value);
1041 
1042  #ifdef TIOCGSIZE
1043     TRACE(" - have TIOCGSIZE\n");
1044  #endif
1045 
1046  #ifdef TIOCGWINSZ
1047     TRACE(" - have TIOCGWINSZ\n");
1048  #endif
1049 
1050     if (isatty(fd))
1051     {
1052      #ifdef TIOCGSIZE
1053 	{
1054 	    struct ttysize ts;
1055 	    if ( !ioctl(fd,TIOCGSIZE,&ts))
1056 	    {
1057 		TRACE(" - TIOCGSIZE = %d*%d\n",ts.ts_cols,ts.ts_lines);
1058 		if ( ts.ts_cols > 0 )
1059 		    return ts.ts_cols > min_value ? ts.ts_cols : min_value;
1060 	    }
1061 	}
1062      #endif
1063 
1064      #ifdef TIOCGWINSZ
1065 	{
1066 	    struct winsize ws;
1067 	    if ( !ioctl(fd,TIOCGWINSZ,&ws))
1068 	    {
1069 		TRACE(" - TIOCGWINSZ = %d*%d\n",ws.ws_col,ws.ws_row);
1070 		if ( ws.ws_col > 0 )
1071 		    return ws.ws_col > min_value ? ws.ws_col : min_value;
1072 	    }
1073 	}
1074      #endif
1075     }
1076 
1077     return default_value ? default_value : opt_width;
1078 }
1079 
1080 //
1081 ///////////////////////////////////////////////////////////////////////////////
1082 ///////////////			    timer			///////////////
1083 ///////////////////////////////////////////////////////////////////////////////
1084 
GetTimerMSec()1085 u32 GetTimerMSec()
1086 {
1087     struct timeval tval;
1088     gettimeofday(&tval,NULL);
1089 
1090     static time_t timebase = 0;
1091     if (!timebase)
1092 	timebase = tval.tv_sec;
1093 
1094     return ( tval.tv_sec - timebase ) * 1000 + tval.tv_usec/1000;
1095 }
1096 
1097 ///////////////////////////////////////////////////////////////////////////////
1098 
PrintMSec(char * buf,int bufsize,u32 msec,bool PrintMSec)1099 ccp PrintMSec ( char * buf, int bufsize, u32 msec, bool PrintMSec )
1100 {
1101     if (PrintMSec)
1102 	snprintf(buf,bufsize,"%02d:%02d:%02d.%03d",
1103 	    msec/3600000, msec/60000%60, msec/1000%60, msec%1000 );
1104     else
1105 	snprintf(buf,bufsize,"%02d:%02d:%02d",
1106 	    msec/3600000, msec/60000%60, msec/1000%60 );
1107     ccp ptr = buf;
1108     int colon_counter = 0;
1109     while ( *ptr == '0' || *ptr == ':' && !colon_counter++ )
1110 	ptr++;
1111     return *ptr == ':' ? ptr-1 : ptr;
1112 }
1113 
1114 //
1115 ///////////////////////////////////////////////////////////////////////////////
1116 ///////////////			string functions		///////////////
1117 ///////////////////////////////////////////////////////////////////////////////
1118 
PathCatPP(char * buf,size_t bufsize,ccp path1,ccp path2)1119 ccp PathCatPP ( char * buf, size_t bufsize, ccp path1, ccp path2 )
1120 {
1121     // concatenate path + path
1122 
1123     if ( !path1 || !*path1 )
1124 	return path2 ? path2 : "";
1125 
1126     if ( !path2 || !*path2 )
1127 	return path1;
1128 
1129     char * ptr = StringCopyS(buf,bufsize-1,path1);
1130     DASSERT( ptr > buf );
1131     if ( ptr[-1] != '/' )
1132 	*ptr++ = '/';
1133     while ( *path2 == '/' )
1134 	path2++;
1135     StringCopyE(ptr,buf+bufsize,path2);
1136     return buf;
1137 }
1138 
1139 ///////////////////////////////////////////////////////////////////////////////
1140 
PathCatPPE(char * buf,size_t bufsize,ccp path1,ccp path2,ccp ext)1141 ccp PathCatPPE ( char * buf, size_t bufsize, ccp path1, ccp path2, ccp ext )
1142 {
1143     // concatenate path + path + extension
1144 
1145     char * ptr = path1 ? StringCopyS(buf,bufsize-1,path1) : buf;
1146     if ( ptr > buf && ptr[-1] != '/' )
1147 	*ptr++ = '/';
1148 
1149     if (path2)
1150     {
1151 	while ( *path2 == '/' )
1152 	    path2++;
1153 	ptr = StringCopyE(ptr,buf+bufsize,path2);
1154     }
1155 
1156     if (ext)
1157 	StringCopyE(ptr,buf+bufsize,ext);
1158 
1159     return buf;
1160 }
1161 
1162 ///////////////////////////////////////////////////////////////////////////////
1163 
SetupDirPath(char * buf,size_t bufsize,ccp src_path)1164 char * SetupDirPath ( char * buf, size_t bufsize, ccp src_path )
1165 {
1166     DASSERT( buf );
1167     DASSERT( bufsize > 10 );
1168     bufsize--;
1169 
1170     char * path_dest = StringCopyS(buf,bufsize,src_path);
1171     if ( path_dest == buf )
1172 	path_dest = StringCopyS(buf,bufsize,"./");
1173     else if ( path_dest[-1] != '/' )
1174     {
1175 	*path_dest++ = '/';
1176 	*path_dest = 0;
1177     }
1178 
1179     TRACE(" - dir-path=%s\n",buf);
1180     return path_dest;
1181 }
1182 
1183 ///////////////////////////////////////////////////////////////////////////////
1184 ///////////////////////////////////////////////////////////////////////////////
1185 
PathCMP(ccp path1,ccp path2)1186 int PathCMP ( ccp path1, ccp path2 )
1187 {
1188     noPRINT("PathCMP( | %s | %s | )\n",path1,path2);
1189 
1190     //--- eliminate equal characters
1191 
1192     while ( *path1 == *path2 )
1193     {
1194 	if (!*path1)
1195 	    return 0;
1196 	path1++;
1197 	path2++;
1198     }
1199 
1200     //--- start the case+path test
1201 
1202     ccp p1 = path1;
1203     ccp p2 = path2;
1204     while ( *p1 || *p2 )
1205     {
1206 	int ch1 = (uchar)tolower((int)*p1++);
1207 	int ch2 = (uchar)tolower((int)*p2++);
1208 	int stat = ch1 - ch2;
1209 	noPRINT("%02x,%02x,%c  %02x,%02x,%c -> %2d\n",
1210 		p1[-1], ch1, ch1, p2[-1], ch2, ch2, stat );
1211 	if (stat)
1212 	    return ch1 == '/' ? -1 : ch2 == '/' ? 1 : stat;
1213 
1214 	if ( ch1 == '/' )
1215 	    break;
1216     }
1217     return *path1 - *path2;
1218 }
1219 
1220 ///////////////////////////////////////////////////////////////////////////////
1221 
NintendoCMP(ccp path1,ccp path2)1222 int NintendoCMP ( ccp path1, ccp path2 )
1223 {
1224     noPRINT("NintendoCMP( | %s | %s | )\n",path1,path2);
1225 
1226     // try to sort in a nintendo like way
1227     //  1.) ignoring case but sort carefully directories
1228     //  2.) files before sub directories
1229 
1230     static uchar transform[0x100] = {0,0};
1231     if (!transform[1]) // ==> setup needed once!
1232     {
1233 	// define some special characters
1234 
1235 	uint index = 1;
1236 	transform[(u8)'/'] = index++;
1237 	transform[(u8)'.'] = index++;
1238 
1239 	// define all characters until and excluding 'A'
1240 
1241 	uint i;
1242 	for ( i = 1; i < 'A'; i++ )
1243 	    if (!transform[i])
1244 		transform[i] = index++;
1245 
1246 	// define letters
1247 
1248 	for ( i = 'A'; i <= 'Z'; i++ )
1249 	    transform[i] = transform[i-'A'+'a'] = index++;
1250 
1251 	// define all other
1252 
1253 	for ( i = 1; i < sizeof(transform); i++ )
1254 	    if (!transform[i])
1255 		transform[i] = index++;
1256 
1257 	DASSERT( index <= sizeof(transform) );
1258      #if defined(TEST) && defined(DEBUG)
1259 	//HexDump16(stderr,0,0,transform,sizeof(transform));
1260      #endif
1261     }
1262 
1263 
1264     //--- eliminate equal characters
1265 
1266     while ( *path1 == *path2 )
1267     {
1268 	if (!*path1)
1269 	    return 0;
1270 	path1++;
1271 	path2++;
1272     }
1273 
1274     //--- start the case+path test
1275 
1276     const uchar * p1 = (const uchar *)path1;
1277     const uchar * p2 = (const uchar *)path2;
1278     while ( *p1 || *p2 )
1279     {
1280 	int ch1 = transform[*p1++];
1281 	int ch2 = transform[*p2++];
1282 	int stat = ch1 - ch2;
1283 	noPRINT("%02x,%02x,%c  %02x,%02x,%c -> %2d\n",
1284 		p1[-1], ch1, ch1, p2[-1], ch2, ch2, stat );
1285 	if (stat)
1286 	{
1287 	 #ifdef WSZST // special case for Wiimms SZS Tools
1288 	    // sort files before dirs
1289 	    const bool indir1 = strchr((ccp)p1-1,'/') != 0;
1290 	    const bool indir2 = strchr((ccp)p2-1,'/') != 0;
1291 	    return  indir1 != indir2 ? indir1 - indir2 : stat;
1292 	 #else
1293 	    return stat;
1294 	 #endif
1295 	}
1296 
1297 	if ( ch1 == 1 )
1298 	    break;
1299     }
1300     return *path1 - *path2;
1301 }
1302 
1303 ///////////////////////////////////////////////////////////////////////////////
1304 ///////////////////////////////////////////////////////////////////////////////
1305 
NormalizeIndent(int indent)1306 int NormalizeIndent ( int indent )
1307 {
1308     return indent < 0 ? 0 : indent < 50 ? indent : 50;
1309 }
1310 
1311 ///////////////////////////////////////////////////////////////////////////////
1312 
CheckIDHelper(const void * id,int max_len,bool allow_any_len,bool ignore_case,bool allow_point)1313 int CheckIDHelper // helper for all other id functions
1314 (
1315 	const void	* id,		// valid pointer to test ID
1316 	int		max_len,	// max length of ID, a NULL terminates ID too
1317 	bool		allow_any_len,	// if false, only length 4 and 6 are allowed
1318 	bool		ignore_case,	// lower case letters are allowed
1319 	bool		allow_point	// the wildcard '.' is allowed
1320 )
1321 {
1322     ASSERT(id);
1323     ccp ptr = id;
1324     ccp end = ptr + max_len;
1325     while ( ptr != end && ( *ptr >= 'A' && *ptr <= 'Z'
1326 			|| ignore_case && *ptr >= 'a' && *ptr <= 'z'
1327 			|| *ptr >= '0' && *ptr <= '9'
1328 			|| allow_point && *ptr == '.'
1329 			|| *ptr == '_' ))
1330 	ptr++;
1331 
1332     const int len = ptr - (ccp)id;
1333     return allow_any_len || len == 4 || len == 6 ? len : 0;
1334 }
1335 
1336 //-----------------------------------------------------------------------------
1337 
CheckID(const void * id,bool ignore_case,bool allow_point)1338 int CheckID // check up to 7 chars for ID4|ID6
1339 (
1340 	const void	* id,		// valid pointer to test ID
1341 	bool		ignore_case,	// lower case letters are allowed
1342 	bool		allow_point	// the wildcard '.' is allowed
1343 )
1344 {
1345     // check up to 7 chars
1346     return CheckIDHelper(id,7,false,ignore_case,allow_point);
1347 }
1348 
1349 //-----------------------------------------------------------------------------
1350 
CheckID4(const void * id,bool ignore_case,bool allow_point)1351 bool CheckID4 // check exact 4 chars
1352 (
1353 	const void	* id,		// valid pointer to test ID
1354 	bool		ignore_case,	// lower case letters are allowed
1355 	bool		allow_point	// the wildcard '.' is allowed
1356 )
1357 {
1358     // check exact 4 chars
1359     return CheckIDHelper(id,4,false,ignore_case,allow_point) == 4;
1360 }
1361 
1362 //-----------------------------------------------------------------------------
1363 
CheckID6(const void * id,bool ignore_case,bool allow_point)1364 bool CheckID6 // check exact 6 chars
1365 (
1366 	const void	* id,		// valid pointer to test ID
1367 	bool		ignore_case,	// lower case letters are allowed
1368 	bool		allow_point	// the wildcard '.' is allowed
1369 )
1370 {
1371     // check exact 6 chars
1372     return CheckIDHelper(id,6,false,ignore_case,allow_point) == 6;
1373 }
1374 
1375 //-----------------------------------------------------------------------------
1376 
CountIDChars(const void * id,bool ignore_case,bool allow_point)1377 int CountIDChars // count number of valid ID chars, max = 1000
1378 (
1379 	const void	* id,		// valid pointer to test ID
1380 	bool		ignore_case,	// lower case letters are allowed
1381 	bool		allow_point	// the wildcard '.' is allowed
1382 )
1383 {
1384     // count number of valid ID chars
1385     return CheckIDHelper(id,1000,true,ignore_case,allow_point);
1386 }
1387 
1388 //-----------------------------------------------------------------------------
1389 
ScanID(char * destbuf7,int * destlen,ccp source)1390 char * ScanID ( char * destbuf7, int * destlen, ccp source )
1391 {
1392     ASSERT(destbuf7);
1393 
1394     memset(destbuf7,0,7);
1395     ccp id_start = 0;
1396     if (source)
1397     {
1398 	ccp src = source;
1399 
1400 	// skip CTRL and SPACES
1401 	while ( *src > 0 && *src <= ' ' )
1402 	    src++;
1403 
1404 	if ( ( *src == '*' || *src == '+' ) && ( !src[1] || src[1] == '=' ) )
1405 	{
1406 	    if (destlen)
1407 		*destlen = 1;
1408 	    return src[1] == '=' && src[2] ? (char*)src+2 : 0;
1409 	}
1410 
1411 	// scan first word
1412 	const int id_len = CheckID(src,false,false);
1413 
1414 	if ( id_len == 4 )
1415 	{
1416 	    TRACE("4 chars found:%.6s\n",id_start);
1417 	    id_start = src;
1418 	    src += id_len;
1419 
1420 	    // skip CTRL and SPACES
1421 	    while ( *src > 0 && *src <= ' ' )
1422 		src++;
1423 
1424 	    if (!*src)
1425 	    {
1426 		memcpy(destbuf7,id_start,4);
1427 		if (destlen)
1428 		    *destlen = 4;
1429 		return 0;
1430 	    }
1431 	    id_start = 0;
1432 	}
1433 	else if ( id_len == 6 )
1434 	{
1435 	    // we have found an ID6 canidat
1436 	    TRACE("6 chars found:%.6s\n",id_start);
1437 	    id_start = src;
1438 	    src += id_len;
1439 
1440 	    // skip CTRL and SPACES
1441 	    while ( *src > 0 && *src <= ' ' )
1442 		src++;
1443 
1444 	    if ( !*src || *src == '=' )
1445 	    {
1446 		if ( *src == '=' )
1447 		    src++;
1448 
1449 		// pure 'ID6' or 'ID6 = name found
1450 		memcpy(destbuf7,id_start,6);
1451 		if (destlen)
1452 		    *destlen = 6;
1453 		return *src ? (char*)src : 0;
1454 	    }
1455 	}
1456 
1457 	// scan for latest '...[ID6]...'
1458 	while (*src)
1459 	{
1460 	    while ( *src && *src != '[' ) // ]
1461 		src++;
1462 
1463 	    if ( *src == '[' && src[7] == ']' && CheckID(++src,false,false) == 6 )
1464 	    {
1465 		id_start = src;
1466 		src += 8;
1467 	    }
1468 	    if (*src)
1469 		src++;
1470 	}
1471     }
1472     if (id_start)
1473 	memcpy(destbuf7,id_start,6);
1474     if (destlen)
1475 	*destlen = id_start ? 6 : 0;
1476     return 0;
1477 }
1478 
1479 //
1480 ///////////////////////////////////////////////////////////////////////////////
1481 ///////////////		    time printing & scanning		///////////////
1482 ///////////////////////////////////////////////////////////////////////////////
1483 
1484 int opt_print_time  = PT__DEFAULT;
1485 int opt_time_select = PT__DEFAULT & PT__USE_MASK;
1486 
1487 ///////////////////////////////////////////////////////////////////////////////
1488 
ScanPrintTimeMode(ccp arg,int prev_mode)1489 int ScanPrintTimeMode ( ccp arg, int prev_mode )
1490 {
1491     #undef E
1492     #undef EM
1493     #undef IT
1494     #undef MT
1495     #undef CT
1496     #undef AT
1497 
1498     #define E PT_ENABLED
1499     #define EM PT__ENABLED_MASK
1500     #define IT PT_USE_ITIME|PT_F_ITIME
1501     #define MT PT_USE_MTIME|PT_F_MTIME
1502     #define CT PT_USE_CTIME|PT_F_CTIME
1503     #define AT PT_USE_ATIME|PT_F_ATIME
1504 
1505     static const CommandTab_t tab[] =
1506     {
1507 	{ 0,			"RESET",	"-",	PT__MASK },
1508 
1509 	{ PT_DISABLED,		"OFF",		0,	EM },
1510 	{ PT_ENABLED,		"ON",		0,	EM },
1511 
1512 	{ E|PT_SINGLE,		"SINGLE",	"1",	EM|PT__MULTI_MASK|PT__F_MASK },
1513 	{ E|PT_MULTI,		"MULTI",	"+",	EM|PT__MULTI_MASK },
1514 
1515 	{ E|PT_MULTI,		"NONE",		"0",	EM|PT__MULTI_MASK|PT__F_MASK },
1516 	{ E|PT_MULTI|PT__F_MASK,"ALL",		"*",	EM|PT__MULTI_MASK|PT__F_MASK },
1517 
1518 	{ E|IT,			"I",		0,	EM|PT__USE_MASK },
1519 	{ E|MT,			"M",		0,	EM|PT__USE_MASK },
1520 	{ E|CT,			"C",		0,	EM|PT__USE_MASK },
1521 	{ E|AT,			"A",		0,	EM|PT__USE_MASK },
1522 
1523 	{ E|PT_PRINT_DATE,	"DATE",		0,	EM|PT__PRINT_MASK },
1524 	{ E|PT_PRINT_TIME,	"TIME",		"MIN",	EM|PT__PRINT_MASK },
1525 	{ E|PT_PRINT_SEC,	"SEC",		0,	EM|PT__PRINT_MASK },
1526 
1527 	{ E|IT|PT_PRINT_DATE,	"IDATE",	0,	EM|PT__USE_MASK|PT__PRINT_MASK },
1528 	{ E|MT|PT_PRINT_DATE,	"MDATE",	0,	EM|PT__USE_MASK|PT__PRINT_MASK },
1529 	{ E|CT|PT_PRINT_DATE,	"CDATE",	0,	EM|PT__USE_MASK|PT__PRINT_MASK },
1530 	{ E|AT|PT_PRINT_DATE,	"ADATE",	0,	EM|PT__USE_MASK|PT__PRINT_MASK },
1531 
1532 	{ E|IT|PT_PRINT_TIME,	"ITIME",	"IMIN",	EM|PT__USE_MASK|PT__PRINT_MASK },
1533 	{ E|MT|PT_PRINT_TIME,	"MTIME",	"MMIN",	EM|PT__USE_MASK|PT__PRINT_MASK },
1534 	{ E|CT|PT_PRINT_TIME,	"CTIME",	"CMIN",	EM|PT__USE_MASK|PT__PRINT_MASK },
1535 	{ E|AT|PT_PRINT_TIME,	"ATIME",	"AMIN",	EM|PT__USE_MASK|PT__PRINT_MASK },
1536 
1537 	{ E|IT|PT_PRINT_SEC,	"ISEC",		0,	EM|PT__USE_MASK|PT__PRINT_MASK },
1538 	{ E|MT|PT_PRINT_SEC,	"MSEC",		0,	EM|PT__USE_MASK|PT__PRINT_MASK },
1539 	{ E|CT|PT_PRINT_SEC,	"CSEC",		0,	EM|PT__USE_MASK|PT__PRINT_MASK },
1540 	{ E|AT|PT_PRINT_SEC,	"ASEC",		0,	EM|PT__USE_MASK|PT__PRINT_MASK },
1541 
1542 	{ 0,0,0,0 }
1543     };
1544 
1545     #undef E
1546     #undef EM
1547     #undef IT
1548     #undef MT
1549     #undef CT
1550     #undef AT
1551 
1552     const int stat = ScanCommandListMask(arg,tab);
1553     if ( stat >= 0 )
1554 	return SetPrintTimeMode(prev_mode,stat);
1555 
1556     ERROR0(ERR_SYNTAX,"Illegal time mode (option --time): '%s'\n",arg);
1557     return PT__ERROR;
1558 }
1559 
1560 ///////////////////////////////////////////////////////////////////////////////
1561 
ScanAndSetPrintTimeMode(ccp argv)1562 int ScanAndSetPrintTimeMode ( ccp argv )
1563 {
1564     const int stat = ScanPrintTimeMode(argv,opt_print_time);
1565     if ( stat >= 0 )
1566 	opt_print_time  = stat;
1567     return stat;
1568 }
1569 
1570 ///////////////////////////////////////////////////////////////////////////////
1571 
SetPrintTimeMode(int prev_mode,int new_mode)1572 int SetPrintTimeMode ( int prev_mode, int new_mode )
1573 {
1574     TRACE("SetPrintTimeMode(%x,%x)\n",prev_mode,new_mode);
1575     if ( new_mode & PT__USE_MASK )
1576 	prev_mode = prev_mode & ~PT__USE_MASK | new_mode & PT__USE_MASK;
1577 
1578     prev_mode |= new_mode & PT__F_MASK;
1579 
1580     if ( new_mode & PT__MULTI_MASK )
1581 	prev_mode = prev_mode & ~PT__MULTI_MASK | new_mode & PT__MULTI_MASK;
1582 
1583     if ( new_mode & PT__PRINT_MASK )
1584 	prev_mode = prev_mode & ~PT__PRINT_MASK | new_mode & PT__PRINT_MASK;
1585 
1586     if ( new_mode & PT__ENABLED_MASK )
1587 	prev_mode = prev_mode & ~PT__ENABLED_MASK | new_mode & PT__ENABLED_MASK;
1588 
1589     TRACE(" -> %x\n",prev_mode);
1590     return prev_mode;
1591 }
1592 
1593 ///////////////////////////////////////////////////////////////////////////////
1594 
EnablePrintTime(int opt_time)1595 int EnablePrintTime ( int opt_time )
1596 {
1597     return SetPrintTimeMode(PT__DEFAULT|PT_PRINT_DATE,opt_time|PT_ENABLED);
1598 }
1599 
1600 ///////////////////////////////////////////////////////////////////////////////
1601 
SetTimeOpt(int opt_time)1602 void SetTimeOpt ( int opt_time )
1603 {
1604     opt_print_time = SetPrintTimeMode( opt_print_time, opt_time|PT_ENABLED );
1605 }
1606 
1607 ///////////////////////////////////////////////////////////////////////////////
1608 
SetupPrintTime(PrintTime_t * pt,int opt_time)1609 void SetupPrintTime ( PrintTime_t * pt, int opt_time )
1610 {
1611     TRACE("SetupPrintTime(%p,%x)\n",pt,opt_time);
1612     ASSERT(pt);
1613     memset(pt,0,sizeof(*pt));
1614 
1615     if ( opt_time & PT_SINGLE )
1616     {
1617 	opt_time &= ~PT__F_MASK;
1618 	switch( opt_time & PT__USE_MASK )
1619 	{
1620 	    case PT_USE_ITIME:	opt_time |= PT_F_ITIME; break;
1621 	    case PT_USE_CTIME:	opt_time |= PT_F_CTIME; break;
1622 	    case PT_USE_ATIME:	opt_time |= PT_F_ATIME; break;
1623 	    default:		opt_time |= PT_F_MTIME; break;
1624 	}
1625     }
1626     else if ( !(opt_time&PT__F_MASK) )
1627 	opt_time |= PT_F_MTIME;
1628 
1629     ccp head_format;
1630     switch ( opt_time & (PT__ENABLED_MASK|PT__PRINT_MASK) )
1631     {
1632 	case PT_ENABLED|PT_PRINT_SEC:
1633 	    pt->format	= " %Y-%m-%d %H:%M:%S";
1634 	    pt->undef	= " ---------- --:--:--";
1635 	    head_format	= "   #-date    #-time ";
1636 	    break;
1637 
1638 	case PT_ENABLED|PT_PRINT_TIME:
1639 	    pt->format	= " %Y-%m-%d %H:%M";
1640 	    pt->undef	= " ---------- --:--";
1641 	    head_format	= "   #-date  #-time";
1642 	    break;
1643 
1644 	case PT_ENABLED|PT_PRINT_DATE:
1645 	    pt->format	= " %Y-%m-%d";
1646 	    pt->undef	= " ----------";
1647 	    head_format	= "   #-date  ";
1648 	    break;
1649 
1650 	default:
1651 	    pt->format	= "";
1652 	    pt->undef	= "";
1653 	    head_format	= "";
1654 	    opt_time &= ~PT__F_MASK;
1655 	    break;
1656     }
1657 
1658     pt->mode = opt_time & PT__MASK;
1659     pt->wd1  = strlen(head_format);
1660 
1661     ASSERT(   pt->wd1 < PT_BUF_SIZE );
1662     ASSERT( 4*pt->wd1 < sizeof(pt->head) );
1663     ASSERT( 4*pt->wd1 < sizeof(pt->fill) );
1664     ASSERT( 4*pt->wd1 < sizeof(pt->tbuf) );
1665 
1666     pt->nelem = 0;
1667     char *head = pt->head, *fill = pt->fill;
1668     ccp mptr = "imca";
1669     while (*mptr)
1670     {
1671 	char ch = *mptr++;
1672 	if ( opt_time & PT_F_ITIME )
1673 	{
1674 	    ccp src;
1675 	    for ( src = head_format; *src; src++ )
1676 	    {
1677 		*head++ = *src == '#' ? ch : *src;
1678 		*fill++ = ' ';
1679 	    }
1680 	    pt->nelem++;
1681 	}
1682 	opt_time >>= 1;
1683     }
1684     *head = 0;
1685     *fill = 0;
1686     pt->wd  = pt->nelem * pt->wd1;
1687 
1688     TRACE(" -> head:   |%s|\n",pt->head);
1689     TRACE(" -> fill:   |%s|\n",pt->fill);
1690     TRACE(" -> format: |%s|\n",pt->format);
1691     TRACE(" -> undef:  |%s|\n",pt->undef);
1692 }
1693 
1694 ///////////////////////////////////////////////////////////////////////////////
1695 
PrintTime(PrintTime_t * pt,const FileAttrib_t * fa)1696 char * PrintTime ( PrintTime_t * pt, const FileAttrib_t * fa )
1697 {
1698     ASSERT(pt);
1699     ASSERT(fa);
1700 
1701     if (!pt->wd)
1702 	*pt->tbuf = 0;
1703     else
1704     {
1705 	const time_t * timbuf[] = { &fa->itime, &fa->mtime, &fa->ctime, &fa->atime, 0 };
1706 	const time_t ** timptr = timbuf;
1707 
1708 	char *dest = pt->tbuf, *end = dest + sizeof(pt->tbuf);
1709 	int mode;
1710 	for ( mode = pt->mode; *timptr; timptr++, mode >>= 1 )
1711 	    if ( mode & PT_F_ITIME )
1712 	    {
1713 		const time_t thetime = **timptr;
1714 		if (!thetime)
1715 		    dest = StringCopyE(dest,end,pt->undef);
1716 		else
1717 		{
1718 		    struct tm * tm = localtime(&thetime);
1719 		    dest += strftime(dest,end-dest,pt->format,tm);
1720 		}
1721 	    }
1722     }
1723     return pt->tbuf;
1724 }
1725 
1726 ///////////////////////////////////////////////////////////////////////////////
1727 
SelectTime(const FileAttrib_t * fa,int opt_time)1728 time_t SelectTime ( const FileAttrib_t * fa, int opt_time )
1729 {
1730     ASSERT(fa);
1731     switch ( opt_time & PT__USE_MASK )
1732     {
1733 	case PT_USE_ITIME: return fa->itime;
1734 	case PT_USE_CTIME: return fa->ctime;
1735 	case PT_USE_ATIME: return fa->atime;
1736 	default:	   return fa->mtime;
1737     }
1738 }
1739 
1740 ///////////////////////////////////////////////////////////////////////////////
1741 
SelectSortMode(int opt_time)1742 SortMode SelectSortMode ( int opt_time )
1743 {
1744     switch ( opt_time & PT__USE_MASK )
1745     {
1746 	case PT_USE_ITIME: return SORT_ITIME;
1747 	case PT_USE_CTIME: return SORT_CTIME;
1748 	case PT_USE_ATIME: return SORT_ATIME;
1749 	default:	   return SORT_MTIME;
1750     }
1751 }
1752 
1753 ///////////////////////////////////////////////////////////////////////////////
1754 ///////////////////////////////////////////////////////////////////////////////
1755 
ScanTime(ccp arg)1756 time_t ScanTime ( ccp arg )
1757 {
1758     static ccp tab[] =
1759     {
1760 	"%Y-%m-%d %H:%M:%S",
1761 	"%Y-%m-%d %H:%M",
1762 	"%Y-%m-%d %H%M%S",
1763 	"%Y-%m-%d %H%M",
1764 	"%Y-%m-%d %H",
1765 	"%Y-%m-%d",
1766 	"%Y%m%d %H%M%S",
1767 	"%Y%m%d %H%M",
1768 	"%Y%m%d %H",
1769 	"%Y%m%d",
1770 	"%s",
1771 	0
1772     };
1773 
1774     ccp * tptr;
1775     for ( tptr = tab; *tptr; tptr++ )
1776     {
1777 	struct tm tm;
1778 	memset(&tm,0,sizeof(tm));
1779 	tm.tm_mon = 1;
1780 	tm.tm_mday = 1;
1781 	ccp res = strptime(arg,*tptr,&tm);
1782 	if (res)
1783 	{
1784 	    while (isblank((int)*res))
1785 		res++;
1786 	    if (!*res)
1787 	    {
1788 		time_t tim = mktime(&tm);
1789 		if ( tim != (time_t)-1 )
1790 		    return tim;
1791 	    }
1792 	}
1793     }
1794 
1795     ERROR0(ERR_SYNTAX,"Illegal time format: %s",arg);
1796     return (time_t)-1;
1797 }
1798 
1799 //
1800 ///////////////////////////////////////////////////////////////////////////////
1801 ///////////////			    scan number			///////////////
1802 ///////////////////////////////////////////////////////////////////////////////
1803 
ScanS32(s32 * res_num,ccp source,uint default_base)1804 char * ScanS32
1805 (
1806     // return 'source' on error
1807 
1808     s32		*res_num,		// not NULL: store result (only on success)
1809     ccp		source,			// NULL or source text
1810     uint	default_base		// base for numbers without '0x' prefix
1811 					//  0: C like with octal support
1812 					// 10: standard value for decimal numbers
1813 					// 16: standard value for hex numbers
1814 )
1815 {
1816     ccp src = source;
1817     while ( *src > 0 && *src <= ' ' )
1818 	src++;
1819 
1820     const bool minus = *src == '-';
1821     if ( minus || *src == '+' )
1822     {
1823 	src++;
1824 	while ( *src > 0 && *src <= ' ' )
1825 	    src++;
1826     }
1827     const uint base = src[0] == '0' && ( src[1] == 'x' || src[1] == 'X' )
1828 			? 16 : default_base;
1829     char *end;
1830     const s32 num = strtoul( src, &end, base );
1831     if ( (ccp)end > src )
1832     {
1833 	if (res_num)
1834 	    *res_num = minus ? -num : num;
1835 	return end;
1836     }
1837 
1838     return (char*)source;
1839 }
1840 
1841 ///////////////////////////////////////////////////////////////////////////////
1842 
ScanS64(s64 * res_num,ccp source,uint default_base)1843 char * ScanS64
1844 (
1845     // return 'source' on error
1846 
1847     s64		*res_num,		// not NULL: store result (only on success)
1848     ccp		source,			// NULL or source text
1849     uint	default_base		// base for numbers without '0x' prefix
1850 					//  0: C like with octal support
1851 					// 10: standard value for decimal numbers
1852 					// 16: standard value for hex numbers
1853 )
1854 {
1855     ccp src = source;
1856     while ( *src > 0 && *src <= ' ' )
1857 	src++;
1858 
1859     const bool minus = *src == '-';
1860     if ( minus || *src == '+' )
1861     {
1862 	src++;
1863 	while ( *src > 0 && *src <= ' ' )
1864 	    src++;
1865     }
1866     const uint base = src[0] == '0' && ( src[1] == 'x' || src[1] == 'X' )
1867 			? 16 : default_base;
1868     char *end;
1869     const s64 num = strtoull( src, &end, base );
1870     if ( (ccp)end > src )
1871     {
1872 	if (res_num)
1873 	    *res_num = minus ? -num : num;
1874 	return end;
1875     }
1876 
1877     return (char*)source;
1878 }
1879 
1880 //
1881 ///////////////////////////////////////////////////////////////////////////////
1882 ///////////////			    scan size			///////////////
1883 ///////////////////////////////////////////////////////////////////////////////
1884 
ScanSizeFactor(char ch_factor,int force_base)1885 u64 ScanSizeFactor ( char ch_factor, int force_base )
1886 {
1887     if ( force_base == 1000 )
1888     {
1889 	switch (ch_factor)
1890 	{
1891 	    case 'b': case 'c': return     1;
1892 	    case 'k': case 'K': return KB_SI;
1893 	    case 'm': case 'M': return MB_SI;
1894 	    case 'g': case 'G': return GB_SI;
1895 	    case 't': case 'T': return TB_SI;
1896 	    case 'p': case 'P': return PB_SI;
1897 	    case 'e': case 'E': return EB_SI;
1898 
1899 	    case 's': case 'S': return WII_SECTOR_SIZE;
1900 	    case 'u': case 'U': return GC_DISC_SIZE;
1901 	    case 'w': case 'W': return WII_SECTORS_SINGLE_LAYER *(u64)WII_SECTOR_SIZE;
1902 	}
1903     }
1904     else if ( force_base == 1024 )
1905     {
1906 	switch (ch_factor)
1907 	{
1908 	    case 'b': case 'c': return   1;
1909 	    case 'k': case 'K': return KiB;
1910 	    case 'm': case 'M': return MiB;
1911 	    case 'g': case 'G': return GiB;
1912 	    case 't': case 'T': return TiB;
1913 	    case 'p': case 'P': return PiB;
1914 	    case 'e': case 'E': return EiB;
1915 
1916 	    case 's': case 'S': return WII_SECTOR_SIZE;
1917 	    case 'u': case 'U': return GC_DISC_SIZE;
1918 	    case 'w': case 'W': return WII_SECTORS_SINGLE_LAYER *(u64)WII_SECTOR_SIZE;
1919 	}
1920     }
1921     else
1922     {
1923 	switch (ch_factor)
1924 	{
1925 	    case 'b':
1926 	    case 'c': return 1;
1927 
1928 	    case 'k': return KB_SI;
1929 	    case 'm': return MB_SI;
1930 	    case 'g': return GB_SI;
1931 	    case 't': return TB_SI;
1932 	    case 'p': return PB_SI;
1933 	    case 'e': return EB_SI;
1934 
1935 	    case 'K': return KiB;
1936 	    case 'M': return MiB;
1937 	    case 'G': return GiB;
1938 	    case 'T': return TiB;
1939 	    case 'P': return PiB;
1940 	    case 'E': return EiB;
1941 
1942 	    case 's':
1943 	    case 'S': return WII_SECTOR_SIZE;
1944 
1945 	    case 'u':
1946 	    case 'U': return GC_DISC_SIZE;
1947 
1948 	    case 'w':
1949 	    case 'W': return WII_SECTORS_SINGLE_LAYER *(u64)WII_SECTOR_SIZE;
1950 	}
1951     }
1952     return 0;
1953 }
1954 
1955 //-----------------------------------------------------------------------------
1956 
ScanSizeTerm(double * num,ccp source,u64 default_factor,int force_base)1957 char * ScanSizeTerm ( double * num, ccp source, u64 default_factor, int force_base )
1958 {
1959     ASSERT(source);
1960 
1961     char * end;
1962     double d = strtod(source,&end);
1963     if ( end > source )
1964     {
1965 	// something was read
1966 
1967 	if ( *end == '/' )
1968 	{
1969 	    const double div = strtod(end+1,&end);
1970 	    if ( div > 0 )
1971 		d /= div;
1972 	}
1973 
1974 	u64 factor = ScanSizeFactor(*end,force_base);
1975 	if (factor)
1976 	    end++;
1977 	else
1978 	    factor = default_factor;
1979 
1980 	if (factor)
1981 	    d *= factor;
1982 	else
1983 	    end = (char*)source;
1984     }
1985 
1986     if (num)
1987 	*num = d;
1988 
1989     return end;
1990 }
1991 
1992 //-----------------------------------------------------------------------------
1993 
ScanSize(double * num,ccp source,u64 default_factor1,u64 default_factor2,int force_base)1994 char * ScanSize ( double * num, ccp source,
1995 		  u64 default_factor1, u64 default_factor2, int force_base )
1996 {
1997     DASSERT(source);
1998     TRACE("ScanSize(df=%llx,%llx, base=%u)\n",
1999 			default_factor1, default_factor2, force_base );
2000 
2001     double sum = 0.0;
2002     bool add = true;
2003     char * end = 0;
2004     for (;;)
2005     {
2006 	double term;
2007 	end = ScanSizeTerm(&term,source,default_factor1,force_base);
2008 	if ( end == source )
2009 	    break;
2010 	if (add)
2011 	    sum += term;
2012 	else
2013 	    sum -= term;
2014 
2015 	while ( *end > 0 && *end <= ' ' )
2016 	    end++;
2017 
2018 	if ( *end == '+' )
2019 	    add = true;
2020 	else if ( *end == '-' )
2021 	    add = false;
2022 	else
2023 	    break;
2024 
2025 	source = end+1;
2026 	while ( *source > 0 && *source <= ' ' )
2027 	    source++;
2028 
2029 	if ( !*source && default_factor2 )
2030 	{
2031 	    if (add)
2032 		sum += default_factor2;
2033 	    else
2034 		sum -= default_factor2;
2035 	    end = (char*)source;
2036 	    break;
2037 	}
2038 
2039 	default_factor1 = default_factor2;
2040     }
2041 
2042     if (num)
2043 	*num = sum;
2044 
2045     return end;
2046 }
2047 
2048 //-----------------------------------------------------------------------------
2049 
ScanSizeU32(u32 * num,ccp source,u64 default_factor1,u64 default_factor2,int force_base)2050 char * ScanSizeU32 ( u32 * num, ccp source,
2051 		     u64 default_factor1, u64 default_factor2, int force_base )
2052 {
2053     double d;
2054     char * end = ScanSize(&d,source,default_factor1,default_factor2,force_base);
2055     //d = ceil(d+0.5);
2056     if ( d < 0 || d > ~(u32)0 )
2057 	end = (char*)source;
2058     else if (num)
2059 	*num = (u32)d;
2060 
2061     return end;
2062 }
2063 
2064 //-----------------------------------------------------------------------------
2065 
ScanSizeU64(u64 * num,ccp source,u64 default_factor1,u64 default_factor2,int force_base)2066 char * ScanSizeU64 ( u64 * num, ccp source,
2067 		     u64 default_factor1, u64 default_factor2, int force_base )
2068 {
2069     double d;
2070     char * end = ScanSize(&d,source,default_factor1,default_factor2,force_base);
2071     //d = ceil(d+0.5);
2072     if ( d < 0 || d > ~(u64)0 )
2073 	end = (char*)source;
2074     else if (num)
2075 	*num = (u64)d;
2076 
2077     return end;
2078 }
2079 
2080 ///////////////////////////////////////////////////////////////////////////////
2081 
ScanSizeOpt(double * num,ccp source,u64 default_factor1,u64 default_factor2,int force_base,ccp opt_name,u64 min,u64 max,bool print_err)2082 enumError ScanSizeOpt
2083 	( double * num, ccp source,
2084 	  u64 default_factor1, u64 default_factor2, int force_base,
2085 	  ccp opt_name, u64 min, u64 max, bool print_err )
2086 {
2087     double d;
2088     char * end = ScanSize(&d,source,default_factor1,default_factor2,force_base);
2089 
2090  #ifdef DEBUG
2091     {
2092 	u64 size = d;
2093 	TRACE("--%s %8.6g ~ %llu ~ %llu GiB ~ %llu GB\n",
2094 		opt_name, d, size, (size+GiB/2)/GiB, (size+500000000)/1000000000 );
2095     }
2096  #endif
2097 
2098     enumError err = ERR_OK;
2099 
2100     if ( source == end || *end )
2101     {
2102 	err = ERR_SYNTAX;
2103 	if (print_err)
2104 	    ERROR0(ERR_SYNTAX,
2105 			"Illegal number for option --%s: %s\n",
2106 			opt_name, source );
2107     }
2108     else if ( min > 0 && d < min )
2109     {
2110 	err = ERR_SYNTAX;
2111 	if (print_err)
2112 	    ERROR0(ERR_SEMANTIC,
2113 			"Value of --%s to small (must not <%llu): %s\n",
2114 			opt_name, min, source );
2115     }
2116     else if ( max > 0 && d > max )
2117     {
2118 	err = ERR_SYNTAX;
2119 	if (print_err)
2120 	    ERROR0(ERR_SEMANTIC,
2121 			"Value of --%s to large (must not >%llu): %s\n",
2122 			opt_name, max, source );
2123     }
2124 
2125     if ( num && !err )
2126 	*num = d;
2127     return err;
2128 }
2129 
2130 //-----------------------------------------------------------------------------
2131 
ScanSizeOptU64(u64 * num,ccp source,u64 default_factor1,int force_base,ccp opt_name,u64 min,u64 max,u32 multiple,u32 pow2,bool print_err)2132 enumError ScanSizeOptU64
2133 	( u64 * num, ccp source, u64 default_factor1, int force_base,
2134 	  ccp opt_name, u64 min, u64 max, u32 multiple, u32 pow2, bool print_err )
2135 {
2136     if (!max)
2137 	max = ~(u64)0;
2138 
2139     if ( pow2 && !force_base )
2140     {
2141 	// try base 1024 first without error messages
2142 	u64 val;
2143 	if (!ScanSizeOptU64( &val, source, default_factor1, 1024,
2144 				opt_name, min,max, multiple, pow2, false ))
2145 	{
2146 	    if (num)
2147 		*num = val;
2148 	    return ERR_OK;
2149 	}
2150     }
2151 
2152     double d = 0.0;
2153     enumError err = ScanSizeOpt(&d,source,default_factor1,
2154 				multiple ? multiple : 1,
2155 				force_base,opt_name,min,max,print_err);
2156 
2157     u64 val;
2158     if ( d < 0.0 )
2159     {
2160 	val = 0;
2161 	err = ERR_SEMANTIC;
2162 	if (print_err)
2163 	    ERROR0(ERR_SEMANTIC, "--%s: negative values not allowed: %s\n",
2164 			opt_name, source );
2165     }
2166     else
2167 	val = d;
2168 
2169     if ( err == ERR_OK && pow2 > 0 && val )
2170     {
2171 	int shift_count = 0;
2172 	u64 shift_val = val;
2173 	if (val)
2174 	{
2175 	    while (!(shift_val&1))
2176 	    {
2177 		shift_count++;
2178 		shift_val >>= 1;
2179 	    }
2180 	}
2181 
2182 	if ( shift_val != 1 || shift_count/pow2*pow2 != shift_count )
2183 	{
2184 	    err = ERR_SEMANTIC;
2185 	    if (print_err)
2186 		ERROR0(ERR_SYNTAX,
2187 			"--%s: value must be a power of %d but not %llu\n",
2188 			opt_name, 1<<pow2, val );
2189 	}
2190     }
2191 
2192     if ( err == ERR_OK && multiple > 1 )
2193     {
2194 	u64 xval = val / multiple * multiple;
2195 	if ( xval != val )
2196 	{
2197 	    if ( min > 0 && xval < min )
2198 		xval += multiple;
2199 
2200 	    if (print_err)
2201 		ERROR0(ERR_WARNING,
2202 			"--%s: value must be a multiple of %u -> use %llu instead of %llu.\n",
2203 			opt_name, multiple, xval, val );
2204 	    val = xval;
2205 	}
2206     }
2207 
2208     if ( num && !err )
2209 	*num = val;
2210     return err;
2211 }
2212 
2213 //-----------------------------------------------------------------------------
2214 
ScanSizeOptU32(u32 * num,ccp source,u64 default_factor1,int force_base,ccp opt_name,u64 min,u64 max,u32 multiple,u32 pow2,bool print_err)2215 enumError ScanSizeOptU32
2216 	( u32 * num, ccp source, u64 default_factor1, int force_base,
2217 	  ccp opt_name, u64 min, u64 max, u32 multiple, u32 pow2, bool print_err )
2218 {
2219     if ( !max || max > ~(u32)0 )
2220 	max = ~(u32)0;
2221 
2222     u64 val;
2223     enumError err = ScanSizeOptU64( &val, source, default_factor1, force_base,
2224 				opt_name, min, max, multiple, pow2, print_err );
2225 
2226     if ( num && !err )
2227 	*num = (u32)val;
2228     return err;
2229 }
2230 
2231 ///////////////////////////////////////////////////////////////////////////////
2232 
ScanOptSplitSize(ccp source)2233 int ScanOptSplitSize ( ccp source )
2234 {
2235     opt_split++;
2236     return ERR_OK != ScanSizeOptU64(
2237 			&opt_split_size,	// u64 * num
2238 			source,			// ccp source
2239 			GiB,			// default_factor1
2240 			0,			// int force_base
2241 			"split-size",		// ccp opt_name
2242 			MIN_SPLIT_SIZE,		// u64 min
2243 			0,			// u64 max
2244 			DEF_SPLIT_FACTOR,	// u32 multiple
2245 			0,			// u32 pow2
2246 			true			// bool print_err
2247 			);
2248 }
2249 
2250 ///////////////////////////////////////////////////////////////////////////////
2251 
ScanOptRDepth(ccp source)2252 int ScanOptRDepth ( ccp source )
2253 {
2254     return ERR_OK != ScanSizeOptU32(
2255 			&opt_recurse_depth,	// u32 * num
2256 			source,			// ccp source
2257 			1,			// default_factor1
2258 			0,			// int force_base
2259 			"rdepth",		// ccp opt_name
2260 			0,			// u64 min
2261 			MAX_RECURSE_DEPTH,	// u64 max
2262 			0,			// u32 multiple
2263 			0,			// u32 pow2
2264 			true			// bool print_err
2265 			);
2266 }
2267 
2268 //
2269 ///////////////////////////////////////////////////////////////////////////////
2270 ///////////////			  scan num/range		///////////////
2271 ///////////////////////////////////////////////////////////////////////////////
2272 
ScanNumU32(ccp arg,u32 * p_stat,u32 * p_num,u32 min,u32 max)2273 char * ScanNumU32 ( ccp arg, u32 * p_stat, u32 * p_num, u32 min, u32 max )
2274 {
2275     ASSERT(arg);
2276     ASSERT(p_num);
2277     TRACE("ScanNumU32(%s)\n",arg);
2278 
2279     while ( *arg > 0 && *arg <= ' ' )
2280 	arg++;
2281 
2282     char * end;
2283     u32 num = strtoul(arg,&end, arg[1] >= '0' && arg[1] <= '9' ? 10 : 0 );
2284     u32 stat = end > arg;
2285     if (stat)
2286     {
2287 	if ( num < min )
2288 	    num = min;
2289 	else if ( num > max )
2290 	    num = max;
2291 
2292 	while ( *end > 0 && *end <= ' ' )
2293 	    end++;
2294     }
2295     else
2296 	num = 0;
2297 
2298     if (p_stat)
2299 	*p_stat = stat;
2300     *p_num = num;
2301 
2302     TRACE("END ScanNumU32() stat=%u, n=%u ->%s\n",stat,num,arg);
2303     return end;
2304 }
2305 
2306 ///////////////////////////////////////////////////////////////////////////////
2307 
ScanRangeU32(ccp arg,u32 * p_stat,u32 * p_n1,u32 * p_n2,u32 min,u32 max)2308 char * ScanRangeU32 ( ccp arg, u32 * p_stat, u32 * p_n1, u32 * p_n2, u32 min, u32 max )
2309 {
2310     ASSERT(arg);
2311     ASSERT(p_n1);
2312     ASSERT(p_n2);
2313     TRACE("ScanRangeU32(%s)\n",arg);
2314 
2315     int stat = 0;
2316     u32 n1 = ~(u32)0, n2 = 0;
2317 
2318     while ( *arg > 0 && *arg <= ' ' )
2319 	arg++;
2320 
2321     if ( *arg == '-' )
2322 	n1 = min;
2323     else
2324     {
2325 	char * end;
2326 	u32 num = strtoul(arg,&end,0);
2327 	if ( arg == end )
2328 	    goto abort;
2329 
2330 	stat = 1;
2331 	arg = end;
2332 	n1 = num;
2333 
2334 	while ( *arg > 0 && *arg <= ' ' )
2335 	    arg++;
2336     }
2337 
2338     if ( *arg != '-' )
2339     {
2340 	stat = 1;
2341 	n2 = n1;
2342 	goto abort;
2343     }
2344     arg++;
2345 
2346     while ( *arg > 0 && *arg <= ' ' )
2347 	arg++;
2348 
2349     char * end;
2350     n2 = strtoul(arg,&end,0);
2351     if ( end == arg )
2352 	n2 = max;
2353     stat = 2;
2354     arg = end;
2355 
2356  abort:
2357 
2358     if ( stat > 0 )
2359     {
2360 	if ( n1 < min )
2361 	    n1 = min;
2362 	if ( n2 > max )
2363 	    n2 = max;
2364     }
2365 
2366     if ( !stat || n1 > n2 )
2367     {
2368 	stat = 0;
2369 	n1 = ~(u32)0;
2370 	n2 = 0;
2371     }
2372 
2373     if (p_stat)
2374 	*p_stat = stat;
2375     *p_n1 = n1;
2376     *p_n2 = n2;
2377 
2378     while ( *arg > 0 && *arg <= ' ' )
2379 	arg++;
2380 
2381     TRACE("END ScanRangeU32() stat=%u, n=%u..%u ->%s\n",stat,n1,n2,arg);
2382     return (char*)arg;
2383 }
2384 
2385 //
2386 ///////////////////////////////////////////////////////////////////////////////
2387 ///////////////			    ScanHex()			///////////////
2388 ///////////////////////////////////////////////////////////////////////////////
2389 
2390 // 0..15 : hex digit
2391 //    50 : spaces and control charactzers
2392 //    51 : allowed separators
2393 //    99 : invalid char
2394 
2395 const u8 HexTab[256] =
2396 {
2397    99,50,50,50, 50,50,50,50, 50,50,50,50, 50,50,50,50, // 0x00 .. 0x0f
2398    50,50,50,50, 50,50,50,50, 50,50,50,50, 50,50,50,50, // 0x10 .. 0x1f
2399 
2400    50,99,99,99, 99,99,99,99, 99,99,99,99, 51,51,51,99, //  !"# $%&' ()*+ ,-./
2401     0, 1, 2, 3,  4, 5, 6, 7,  8, 9,51,99, 99,99,99,99, // 0123 4567 89:; <=>?
2402    99,10,11,12, 13,14,15,99, 99,99,99,99, 99,99,99,99, // @ABC DEFG HIJK LMNO
2403    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99, // PQRS TUVW XYZ[ \]^_
2404    99,10,11,12, 13,14,15,99, 99,99,99,99, 99,99,99,99, // `abc defg hijk lmno
2405    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99, // PQRS TUVW xyz {|}~
2406 
2407    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99, // 0x80 .. 0x8f
2408    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99, // 0x90 .. 0x9f
2409    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99, // 0xa0 .. 0xaf
2410    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99, // 0xb0 .. 0xbf
2411    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99, // 0xc0 .. 0xcf
2412    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99, // 0xd0 .. 0xdf
2413    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99, // 0xe0 .. 0xef
2414    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99, // 0xf0 .. 0xff
2415 };
2416 
2417 ///////////////////////////////////////////////////////////////////////////////
2418 
ScanHexHelper(void * buf,int buf_size,int * bytes_read,ccp arg,int err_level)2419 char * ScanHexHelper
2420 (
2421     void	* buf,		// valid pointer to result buf
2422     int		buf_size,	// number of byte to read
2423     int		* bytes_read,	// NULL or result: number of read bytes
2424     ccp		arg,		// source string
2425     int		err_level	// error level (print message):
2426 				//	 = 0: don't print errors
2427 				//	>= 1: print syntax errors
2428 				//	>= 2: msg if bytes_read<buf_size
2429 				//	>= 3: msg if arg contains more characters
2430 )
2431 {
2432     DASSERT( buf );
2433     DASSERT( buf_size > 0 );
2434     DASSERT( arg );
2435     u8 * dest = buf;
2436     memset(dest,0,buf_size);
2437 
2438     const u8 * src = (const u8*)arg;
2439     int read_count = 0;
2440     while ( HexTab[*src] < 99 && read_count < buf_size )
2441     {
2442 	while ( HexTab[*src] >= 50 && HexTab[*src] <= 99 )
2443 	    src++;
2444 
2445 	if ( *src == '0' && ( src[1] == 'x' || src[1] == 'X' ))
2446 	    src += 2;
2447 
2448 	const u8 * end = src;
2449 	while ( HexTab[*end] < 16 )
2450 	    end++;
2451 
2452 	if ( (end-src) & 1 )
2453 	{
2454 	    *dest++ = HexTab[*src++];
2455 	    read_count++;
2456 	}
2457 
2458 	while ( src < end && read_count < buf_size )
2459 	{
2460 	    *dest    = HexTab[*src++] << 4;
2461 	    *dest++ |= HexTab[*src++];
2462 	    read_count++;
2463 	}
2464     }
2465     // [[2do]] What 2do?? Is this a forgotten marker?
2466 
2467     while ( HexTab[*src] == 50 )
2468 	src++;
2469 
2470     if ( err_level > 0 )
2471     {
2472 	if ( read_count < buf_size && *src )
2473 	    ERROR0(ERR_SYNTAX,"Illegal character for hex input: %.20s\n",src);
2474 	else if ( err_level > 1 && read_count < buf_size )
2475 	    ERROR0(ERR_SYNTAX,"Missing %d hex characters: %.20s\n",
2476 			buf_size - read_count, arg);
2477 	else if ( err_level > 2 && *src )
2478 	    ERROR0(ERR_SYNTAX,"Unexpected characters: %.20s\n",src);
2479     }
2480 
2481     if (bytes_read)
2482 	*bytes_read = read_count;
2483     return (char*)src;
2484 }
2485 
2486 ///////////////////////////////////////////////////////////////////////////////
2487 
ScanHex(void * buf,int buf_size,ccp arg)2488 enumError ScanHex
2489 (
2490     void	* buf,		// valid pointer to result buf
2491     int		buf_size,	// number of byte to read
2492     ccp		arg		// source string
2493 )
2494 {
2495     int count;
2496     arg = ScanHexHelper(buf,buf_size,&count,arg,99);
2497 
2498     return count == buf_size && !*arg ? ERR_OK : ERR_SYNTAX;
2499 }
2500 
2501 ///////////////////////////////////////////////////////////////////////////////
2502 
ScanHexSilent(void * buf,int buf_size,ccp arg)2503 enumError ScanHexSilent
2504 (
2505     void	* buf,		// valid pointer to result buf
2506     int		buf_size,	// number of byte to read
2507     ccp		arg		// source string
2508 )
2509 {
2510     int count;
2511     arg = ScanHexHelper(buf,buf_size,&count,arg,0);
2512 
2513     return count == buf_size && !*arg ? ERR_OK : ERR_SYNTAX;
2514 }
2515 
2516 ///////////////////////////////////////////////////////////////////////////////
2517 ///////////////////////////////////////////////////////////////////////////////
2518 
PutLines(FILE * f,int indent,int fw,int first_line,ccp prefix,ccp text)2519 void PutLines
2520 (
2521     FILE	* f,		// valid output stream
2522     int		indent,		// indent of output
2523     int		fw,		// field width of output
2524     int		first_line,	// length without prefix of already printed first line
2525     ccp		prefix,		// NULL or prefix for each line
2526     ccp		text		// text to print
2527 )
2528 {
2529     DASSERT(f);
2530     DASSERT( indent >= 0 );
2531 
2532     if (!prefix)
2533 	prefix = "";
2534     TRACE("PutLines(,%d,%d,%d,%.10s,%.20s)\n",indent,fw,first_line,prefix,text);
2535     fw -= strlen(prefix);
2536     if ( fw < 10 )
2537 	fw = 10;
2538 
2539     ccp prefix1 = "";
2540     int indent1, fw1;
2541     if (  indent > first_line )
2542     {
2543 	indent1 = indent - first_line;
2544 	fw1 = fw - indent;
2545     }
2546     else
2547     {
2548 	indent1 = 0;
2549 	fw1 = fw - first_line;
2550     }
2551 
2552     fw -= indent;
2553     if ( fw < 20 )
2554 	fw = 20;
2555 
2556     if ( fw1 < 20 )
2557     {
2558 	fputc('\n',f);
2559 	indent1 = indent;
2560 	fw1 = fw;
2561 	prefix1 = prefix;
2562     }
2563 
2564     while ( *text )
2565     {
2566 	// skip blank and control
2567 	if ( *text == '\n' )
2568 	{
2569 	    // don't skip spaces behind a LF ==> needed for tables
2570 	    while ( *text > 0 && *text < ' ' )
2571 		text++;
2572 	}
2573 	else
2574 	{
2575 	    // but ignore spaces on an automatic line break
2576 	    while ( *text > 0 && *text <= ' ' )
2577 		text++;
2578 	}
2579 
2580 	// setup
2581 	ccp start = text, last_blank = text;
2582 	ccp max = text + fw1;
2583 
2584 	while ( text < max && *text && *text != '\n' )
2585 	{
2586 	    if ( *text > 0 && *text <= ' ' )
2587 		last_blank = text;
2588 	    text++;
2589 	}
2590 
2591 	// set back to last blank
2592 	if ( last_blank > start && (u8)*text > ' ' )
2593 	    text = last_blank;
2594 
2595 	// print out
2596 	if ( *text || text > start )
2597 	    fprintf(f,"%s%*s%.*s\n", prefix1, indent1, "", (int)(text-start), start );
2598 
2599 	// use standard values for next lines
2600 	indent1 = indent;
2601 	fw1 = fw;
2602 	prefix1 = prefix;
2603     }
2604 }
2605 
2606 ///////////////////////////////////////////////////////////////////////////////
2607 
PrintLines(FILE * f,int indent,int fw,int first_line,ccp prefix,ccp format,...)2608 void PrintLines
2609 (
2610     FILE	* f,		// valid output stream
2611     int		indent,		// indent of output
2612     int		fw,		// field width of output
2613     int		first_line,	// length without prefix of already printed first line
2614     ccp		prefix,		// NULL or prefix for each line
2615     ccp		format,		// format string for vsnprintf()
2616     ...				// arguments for 'vsnprintf(format,...)'
2617 )
2618 {
2619     DASSERT(f);
2620     DASSERT(format);
2621 
2622     char msg[5000];
2623 
2624     va_list arg;
2625     va_start(arg,format);
2626     vsnprintf(msg,sizeof(msg),format,arg);
2627     va_end(arg);
2628 
2629     PutLines(f,indent,fw,first_line,prefix,msg);
2630 }
2631 
2632 //
2633 ///////////////////////////////////////////////////////////////////////////////
2634 ///////////////		    scan compression option		///////////////
2635 ///////////////////////////////////////////////////////////////////////////////
2636 
2637 wd_compression_t opt_compr_method = WD_COMPR__DEFAULT;
2638 int opt_compr_level		  = 0;	// 0=default, 1..9=valid
2639 u32 opt_compr_chunk_size	  = 0;	// 0=default
2640 
2641 ///////////////////////////////////////////////////////////////////////////////
2642 
ScanCompression_helper(ccp arg,bool silent,int * level,u32 * chunk_size)2643 static wd_compression_t ScanCompression_helper
2644 (
2645     ccp			arg,		// argument to scan
2646     bool		silent,		// don't print error message
2647     int			* level,	// not NULL: appendix '.digit' allowed
2648 					// The level will be stored in '*level'
2649     u32			* chunk_size	// not NULL: appendix '@size' allowed
2650 					// The size will be stored in '*chunk_size'
2651 )
2652 {
2653     enum
2654     {
2655 	COMPR_MEM = WD_COMPR__N+1
2656     };
2657 
2658     static const CommandTab_t tab[] =
2659     {
2660 	{ WD_COMPR_NONE,	"NONE",		0,	0 },
2661 	{ WD_COMPR_PURGE,	"PURGE",	"WDF",	0 },
2662 	{ WD_COMPR_BZIP2,	"BZIP2",	"BZ2",	0 },
2663 	{ WD_COMPR_LZMA,	"LZMA",		"LZ",	0 },
2664 	{ WD_COMPR_LZMA2,	"LZMA2",	"LZ2",	0 },
2665 
2666 	{ WD_COMPR__DEFAULT,	"DEFAULT",	"D",	0 },
2667 	{ WD_COMPR__FAST,	"FAST",		"F",	0x300 + 10 },
2668 	{ WD_COMPR__GOOD,	"GOOD",		"G",	0 },
2669 	{ WD_COMPR__BEST,	"BEST",		"B",	0x900 + 50 },
2670 
2671 	{ COMPR_MEM,		"MEM",		"M",	0 },
2672 
2673 	{ WD_COMPR_NONE,	"0",		0,	0 },
2674 	{ WD_COMPR_LZMA,	"1",		0,	0x100 +  5 },
2675 	{ WD_COMPR_LZMA,	"2",		0,	0x200 + 10 },
2676 	{ WD_COMPR_LZMA,	"3",		0,	0x300 + 30 },
2677 	{ WD_COMPR_LZMA,	"4",		0,	0x400 + 30 },
2678 	{ WD_COMPR_LZMA,	"5",		0,	0x400 + 50 },
2679 	{ WD_COMPR_LZMA,	"6",		0,	0x500 + 20 },
2680 	{ WD_COMPR_LZMA,	"7",		0,	0x500 + 50 },
2681 	{ WD_COMPR_LZMA,	"8",		0,	0x600 + 50 },
2682 	{ WD_COMPR_LZMA,	"9",		0,	0x900 + 50 },
2683 
2684 	{ 0,0,0,0 }
2685     };
2686 
2687     char argbuf[100];
2688     ccp scan_arg = arg ? arg : "";
2689     while ( *scan_arg > 0 && *scan_arg <= ' ' )
2690 	scan_arg++;
2691 
2692     if ( level || chunk_size )
2693     {
2694 	if (level)
2695 	    *level = -1; // -1 = mark as 'not set'
2696 	if (chunk_size)
2697 	    *chunk_size = 0;
2698 
2699 	const int len = strlen(scan_arg);
2700 	if ( len < sizeof(argbuf) )
2701 	{
2702 	    strcpy(argbuf,scan_arg);
2703 	    scan_arg = argbuf;
2704 
2705 	    if (chunk_size)
2706 	    {
2707 		char * found = strchr(argbuf,'@');
2708 		if (found)
2709 		{
2710 		    *found++ = 0;
2711 		    char * end;
2712 		    unsigned num = strtoul(found,&end,10);
2713 		    if (*end)
2714 		    {
2715 			if (!silent)
2716 			    ERROR0(ERR_SYNTAX,
2717 				"Unsigned number expected: --compression @%s\n",found);
2718 			return -1;
2719 		    }
2720 		    *chunk_size = ( num ? num : WIA_DEF_CHUNK_FACTOR ) * WII_GROUP_SIZE;
2721 		}
2722 	    }
2723 		    if (level)
2724 	    {
2725 		char * found = strchr(argbuf,'.');
2726 		if ( found && found[1] >= '0' && found[1] <= '9' && !found[2] )
2727 		{
2728 		    *level = found[1] - '0';
2729 		    *found = 0;
2730 		}
2731 	    }
2732 
2733 	    if (!*argbuf)
2734 		return WD_COMPR__DEFAULT;
2735 	}
2736     }
2737 
2738     const CommandTab_t * cmd = ScanCommand(0,scan_arg,tab);
2739     if (cmd)
2740     {
2741 	wd_compression_t compr = cmd->id;
2742 	u32 opt = cmd->opt;
2743 
2744 	if ( compr == (wd_compression_t)COMPR_MEM )
2745 	{
2746 	    compr = WD_COMPR__DEFAULT;
2747 	    u32 memlimit = GetMemLimit();
2748 	    if (memlimit)
2749 	    {
2750 		typedef struct tripel
2751 		{
2752 		    wd_compression_t	compr;
2753 		    int			level;
2754 		    u32			csize;
2755 		} tripel;
2756 
2757 		static const tripel tab[] =
2758 		{
2759 		    { WD_COMPR_LZMA,	7, 50 * WIA_BASE_CHUNK_SIZE },
2760 		    { WD_COMPR_LZMA,	7, 40 * WIA_BASE_CHUNK_SIZE },
2761 		    { WD_COMPR_LZMA,	6, 50 * WIA_BASE_CHUNK_SIZE },
2762 		    { WD_COMPR_LZMA,	6, 40 * WIA_BASE_CHUNK_SIZE },
2763 		    { WD_COMPR_LZMA,	6, 30 * WIA_BASE_CHUNK_SIZE },
2764 		    { WD_COMPR_LZMA,	5, 50 * WIA_BASE_CHUNK_SIZE },
2765 		    { WD_COMPR_LZMA,	5, 40 * WIA_BASE_CHUNK_SIZE },
2766 		    { WD_COMPR_LZMA,	5, 30 * WIA_BASE_CHUNK_SIZE },
2767 		    { WD_COMPR_LZMA,	5, 20 * WIA_BASE_CHUNK_SIZE },
2768 		    { WD_COMPR_LZMA,	4, 50 * WIA_BASE_CHUNK_SIZE },
2769 		    { WD_COMPR_LZMA,	4, 40 * WIA_BASE_CHUNK_SIZE },
2770 		    { WD_COMPR_LZMA,	4, 30 * WIA_BASE_CHUNK_SIZE },
2771 		    { WD_COMPR_LZMA,	4, 20 * WIA_BASE_CHUNK_SIZE },
2772 		    { WD_COMPR_LZMA,	4, 10 * WIA_BASE_CHUNK_SIZE },
2773 
2774 		  #ifndef NO_BZIP2
2775 		    { WD_COMPR_BZIP2,	9, 50 * WIA_BASE_CHUNK_SIZE },
2776 		    { WD_COMPR_BZIP2,	8, 40 * WIA_BASE_CHUNK_SIZE },
2777 		    { WD_COMPR_BZIP2,	7, 30 * WIA_BASE_CHUNK_SIZE },
2778 		    { WD_COMPR_BZIP2,	6, 25 * WIA_BASE_CHUNK_SIZE },
2779 		    { WD_COMPR_BZIP2,	5, 20 * WIA_BASE_CHUNK_SIZE },
2780 		    { WD_COMPR_BZIP2,	4, 15 * WIA_BASE_CHUNK_SIZE },
2781 		    { WD_COMPR_BZIP2,	3, 10 * WIA_BASE_CHUNK_SIZE },
2782 		    { WD_COMPR_BZIP2,	2,  5 * WIA_BASE_CHUNK_SIZE },
2783 		    { WD_COMPR_BZIP2,	1,  1 * WIA_BASE_CHUNK_SIZE },
2784 		  #endif
2785 
2786 		    { WD_COMPR_LZMA,	4,  5 * WIA_BASE_CHUNK_SIZE },
2787 		    { WD_COMPR_LZMA,	3,  5 * WIA_BASE_CHUNK_SIZE },
2788 		    { WD_COMPR_LZMA,	2,  5 * WIA_BASE_CHUNK_SIZE },
2789 		    { WD_COMPR_LZMA,	1,  5 * WIA_BASE_CHUNK_SIZE },
2790 		    { WD_COMPR_LZMA,	1,  1 * WIA_BASE_CHUNK_SIZE },
2791 
2792 		    { 0,0,0 } // end marker
2793 		};
2794 
2795 		const tripel * p;
2796 		for ( p = tab; p->level; p++ )
2797 		{
2798 		    if ( CalcMemoryUsageWIA(p->compr,p->level,p->csize,true) <= memlimit )
2799 		    {
2800 			compr = p->compr;
2801 			opt   = p->level << 8 | p->csize / WIA_BASE_CHUNK_SIZE;
2802 			break;
2803 		    }
2804 		}
2805 	    }
2806 	}
2807 
2808 	if (opt)
2809 	{
2810 	    if ( level && *level < 1 )
2811 		*level = opt >> 8;
2812 	    if ( chunk_size && *chunk_size <= 1 )
2813 		*chunk_size = ( opt & 0xff ) * WIA_BASE_CHUNK_SIZE;
2814 	}
2815 
2816 	return compr;
2817     }
2818 
2819 #if 0 // [[obsolete]]
2820     char * end;
2821     u32 val = strtoul(scan_arg,&end,10);
2822  #ifdef TEST
2823     if ( end > scan_arg && !*end )
2824 	return val;
2825  #else
2826     if ( end > scan_arg && !*end && val < WD_COMPR__N )
2827 	return val;
2828  #endif
2829 #endif
2830 
2831     if (!silent)
2832 	ERROR0(ERR_SYNTAX,"Illegal compression method: '%s'\n",scan_arg);
2833     return -1;
2834 }
2835 
2836 ///////////////////////////////////////////////////////////////////////////////
2837 
ScanCompression(ccp arg,bool silent,int * level,u32 * chunk_size)2838 wd_compression_t ScanCompression
2839 (
2840     ccp			arg,		// argument to scan
2841     bool		silent,		// don't print error message
2842     int			* level,	// not NULL: appendix '.digit' allowed
2843 					// The level will be stored in '*level'
2844     u32			* chunk_size	// not NULL: appendix '@size' allowed
2845 					// The size will be stored in '*chunk_size'
2846 )
2847 {
2848     wd_compression_t compr = ScanCompression_helper(arg,silent,level,chunk_size);
2849     if (level)
2850     {
2851 	if ( *level < 0 )
2852 	    *level = 0;
2853 	else switch(compr)
2854 	{
2855 	    case WD_COMPR__N:
2856 	    case WD_COMPR_NONE:
2857 	    case WD_COMPR_PURGE:
2858 		*level = 0;
2859 		break;
2860 
2861 	    case WD_COMPR_BZIP2:
2862 	     #ifdef NO_BZIP2
2863 		*level = 0;
2864 	     #else
2865 		*level = CalcCompressionLevelBZIP2(*level);
2866 	     #endif
2867 		break;
2868 		    case WD_COMPR_LZMA:
2869 	    case WD_COMPR_LZMA2:
2870 		*level = CalcCompressionLevelLZMA(*level);
2871 		break;
2872 	}
2873     }
2874 
2875     return compr;
2876 }
2877 
2878 ///////////////////////////////////////////////////////////////////////////////
2879 
ScanOptCompression(bool set_oft_wia,ccp arg)2880 int ScanOptCompression
2881 (
2882     bool		set_oft_wia,	// true: output_file_type := OFT_WIA
2883     ccp			arg		// argument to scan
2884 )
2885 {
2886     if (set_oft_wia)
2887 	output_file_type = OFT_WIA;
2888     if (arg)
2889     {
2890 	int new_level = 0;
2891 	u32 new_chunk_size;
2892 	const int new_compr = ScanCompression(arg,false,&new_level,&new_chunk_size);
2893 	if ( new_compr == -1 )
2894 	    return 1;
2895 	opt_compr_method	= new_compr;
2896 	opt_compr_level		= new_level;
2897 	opt_compr_chunk_size	= new_chunk_size;
2898     }
2899     return 0;
2900 }
2901 
2902 //
2903 ///////////////////////////////////////////////////////////////////////////////
2904 ///////////////			scan mem option			///////////////
2905 ///////////////////////////////////////////////////////////////////////////////
2906 
2907 u64 opt_mem = 0;
2908 
2909 ///////////////////////////////////////////////////////////////////////////////
2910 
ScanOptMem(ccp arg,bool print_err)2911 int ScanOptMem
2912 (
2913     ccp			arg,		// argument to scan
2914     bool		print_err	// true: print error messages
2915 )
2916 {
2917     u64 num;
2918     enumError err = ScanSizeOptU64
2919 			( &num,		// u64 * num,
2920 			  arg,		// ccp source,
2921 			  MiB,		// u64 default_factor1,
2922 			  0,		// int force_base,
2923 			  "mem",	// ccp opt_name,
2924 			  0,		// u64 min,
2925 			  0,		// u64 max,
2926 			  0,		// u32 multiple,
2927 			  0,		// u32 pow2,
2928 			  print_err	// bool print_err
2929 			);
2930 
2931     if (err)
2932 	return 1;
2933 
2934     opt_mem = num;
2935     return 0;
2936 }
2937 
2938 ///////////////////////////////////////////////////////////////////////////////
2939 
GetMemLimit()2940 u64 GetMemLimit()
2941 {
2942     static bool done = false;
2943     if ( !done && !opt_mem )
2944     {
2945 	done = true;
2946 
2947 	char * env = getenv("WIT_MEM");
2948 	if ( env && *env )
2949 	    ScanOptMem(env,false);
2950 
2951 	if (!opt_mem)
2952 	{
2953 	    FILE * f = fopen("/proc/meminfo","r");
2954 	    if (f)
2955 	    {
2956 		TRACE("SCAN /proc/meminfo\n");
2957 		char buf[500];
2958 		while (fgets(buf,sizeof(buf),f))
2959 		{
2960 		    if (memcmp(buf,"MemTotal:",9))
2961 			continue;
2962 
2963 		    char * ptr;
2964 		    s64 num = strtoul(buf+9,&ptr,10);
2965 		    if (num)
2966 		    {
2967 			while ( *ptr && *ptr <= ' ' )
2968 			    ptr++;
2969 			switch (*ptr)
2970 			{
2971 			    case 'k': case 'K': num *= KiB; break;
2972 			    case 'm': case 'M': num *= MiB; break;
2973 			    case 'g': case 'G': num *= GiB; break;
2974 			}
2975 			num -= 50 * MiB + num/5;
2976 			opt_mem = num < 1 ? 1 : num;
2977 		    }
2978 		    break;
2979 		}
2980 		fclose(f);
2981 	    }
2982 	}
2983     }
2984 
2985     return opt_mem;
2986 }
2987 
2988 //
2989 ///////////////////////////////////////////////////////////////////////////////
2990 ///////////////			 CommandTab_t			///////////////
2991 ///////////////////////////////////////////////////////////////////////////////
2992 
ScanCommand(int * res_abbrev,ccp arg,const CommandTab_t * cmd_tab)2993 const CommandTab_t * ScanCommand
2994 (
2995     int			* res_abbrev,	// NULL or pointer to result 'abbrev_count'
2996     ccp			arg,		// argument to scan
2997     const CommandTab_t	* cmd_tab	// valid pointer to command table
2998 )
2999 {
3000     ASSERT(arg);
3001     char cmd_buf[COMMAND_NAME_MAX];
3002 
3003     char *dest = cmd_buf;
3004     char *end  = cmd_buf + sizeof(cmd_buf) - 1;
3005     while ( *arg && dest < end )
3006 	*dest++ = toupper((int)*arg++);
3007     *dest = 0;
3008     const int cmd_len = dest - cmd_buf;
3009 
3010     int abbrev_count = 0;
3011     const CommandTab_t *ct, *cmd_ct = 0, *abbrev_ct = 0;
3012     for ( ct = cmd_tab; ct->name1; ct++ )
3013     {
3014 	if ( !strcmp(ct->name1,cmd_buf) || ct->name2 && !strcmp(ct->name2,cmd_buf) )
3015 	{
3016 	    cmd_ct = ct;
3017 	    break;
3018 	}
3019 
3020 	if ( *cmd_buf == '_' ) // no abbreviations for commands beginning with '_'
3021 	    continue;
3022 
3023 	if ( !memcmp(ct->name1,cmd_buf,cmd_len)
3024 		|| ct->name2 && !memcmp(ct->name2,cmd_buf,cmd_len) )
3025 	{
3026 	    if ( !abbrev_ct || abbrev_ct->id != ct->id || abbrev_ct->opt != ct->opt )
3027 	    {
3028 		abbrev_ct = ct;
3029 		abbrev_count++;
3030 	    }
3031 	}
3032     }
3033 
3034     if (cmd_ct)
3035 	abbrev_count = 0;
3036     else if ( abbrev_count == 1 )
3037 	cmd_ct = abbrev_ct;
3038     else if (!abbrev_count)
3039 	abbrev_count = -1;
3040 
3041     if (res_abbrev)
3042 	*res_abbrev = abbrev_count;
3043 
3044     return cmd_ct;
3045 }
3046 
3047 ///////////////////////////////////////////////////////////////////////////////
3048 
ScanCommandList(ccp arg,const CommandTab_t * cmd_tab,CommandCallbackFunc func,bool allow_prefix,u32 max_number,s64 result)3049 s64 ScanCommandList
3050 (
3051     ccp			arg,		// argument to scan
3052     const CommandTab_t	* cmd_tab,	// valid pointer to command table
3053     CommandCallbackFunc	func,		// NULL or calculation function
3054     bool		allow_prefix,	// allow '-' | '+' | '=' as prefix
3055     u32			max_number,	// allow numbers < 'max_number' (0=disabled)
3056     s64			result		// start value for result
3057 )
3058 {
3059     ASSERT(arg);
3060 
3061     char cmd_buf[COMMAND_NAME_MAX];
3062     char *end  = cmd_buf + sizeof(cmd_buf) - 1;
3063 
3064     for (;;)
3065     {
3066 	while ( *arg > 0 && *arg <= ' ' || *arg == ',' )
3067 	    arg++;
3068 
3069 	if (!*arg)
3070 	    return result;
3071 
3072 	char *dest = cmd_buf;
3073 	while ( *arg > ' ' && *arg != ',' &&
3074 		( *arg != '+' || dest == cmd_buf ) && dest < end )
3075 	    *dest++ = *arg++;
3076 	*dest = 0;
3077 	char prefix = 0;
3078 	int abbrev_count;
3079 	const CommandTab_t * cptr = ScanCommand(&abbrev_count,cmd_buf,cmd_tab);
3080 	if ( !cptr && allow_prefix && cmd_buf[1]
3081 	    && ( *cmd_buf == '+' || *cmd_buf == '-' || *cmd_buf == '/' || *cmd_buf == '=' ))
3082 	{
3083 	    prefix = *cmd_buf == '/' ? '-' : *cmd_buf;
3084 	    cptr = ScanCommand(&abbrev_count,cmd_buf+1,cmd_tab);
3085 	}
3086 
3087 	CommandTab_t ct_num = { 0, cmd_buf, 0, 0 };
3088 	if ( max_number && abbrev_count )
3089 	{
3090 	    char * start = cmd_buf + (prefix!=0);
3091 	    ulong num = strtol(start,&dest,10);
3092 	    if ( num < max_number && dest > start && !*dest )
3093 	    {
3094 		ct_num.id = num;
3095 		cptr = &ct_num;
3096 	    }
3097 	}
3098 
3099 	if ( !cptr || cptr->opt && prefix && prefix != '+' )
3100 	    return -1;
3101 
3102 
3103 	if (func)
3104 	{
3105 	    result = func(0,cmd_buf,cmd_tab,cptr,prefix,result);
3106 	    if ( result == -(s64)1 )
3107 		return result;
3108 	}
3109 	else
3110 	{
3111 	    switch (prefix)
3112 	    {
3113 		case 0:
3114 		case '+': result  =  result & ~cptr->opt | cptr->id; break;
3115 		case '-': result &= ~cptr->id; break;
3116 		case '=': result  =  cptr->id; break;
3117 	    }
3118 	}
3119     }
3120 }
3121 
3122 ///////////////////////////////////////////////////////////////////////////////
3123 
ScanCommandListFunc(ccp arg,const CommandTab_t * cmd_tab,CommandCallbackFunc func,void * param,bool allow_prefix)3124 enumError ScanCommandListFunc
3125 (
3126     ccp			arg,		// argument to scan
3127     const CommandTab_t	* cmd_tab,	// valid pointer to command table
3128     CommandCallbackFunc	func,		// calculation function
3129     void		* param,	// used define parameter for 'func'
3130     bool		allow_prefix	// allow '-' | '+' | '=' as prefix
3131 )
3132 {
3133     ASSERT(arg);
3134     ASSERT(func);
3135 
3136     char cmd_buf[COMMAND_NAME_MAX];
3137     char *end  = cmd_buf + sizeof(cmd_buf) - 1;
3138 
3139     for (;;)
3140     {
3141 	while ( *arg > 0 && *arg <= ' ' || *arg == ',' )
3142 	    arg++;
3143 
3144 	if (!*arg)
3145 	    return ERR_OK;
3146 
3147 	char *dest = cmd_buf;
3148 	while ( *arg > ' ' && *arg != ',' &&
3149 		( *arg != '+' || dest == cmd_buf ) && dest < end )
3150 	    *dest++ = *arg++;
3151 	*dest = 0;
3152 	char prefix = 0;
3153 	int abbrev_count;
3154 	const CommandTab_t * cptr = ScanCommand(&abbrev_count,cmd_buf,cmd_tab);
3155 	if ( !cptr && allow_prefix && cmd_buf[1]
3156 	    && ( *cmd_buf == '+' || *cmd_buf == '-' || *cmd_buf == '/' || *cmd_buf == '=' ))
3157 	{
3158 	    prefix = *cmd_buf == '/' ? '-' : *cmd_buf;
3159 	    cptr = ScanCommand(&abbrev_count,cmd_buf+1,cmd_tab);
3160 	}
3161 
3162 	const enumError err = func(param,cmd_buf,cmd_tab,cptr,prefix,0);
3163 	if (err)
3164 	    return err;
3165     }
3166 }
3167 
3168 ///////////////////////////////////////////////////////////////////////////////
3169 
ScanCommandListMaskHelper(void * param,ccp name,const CommandTab_t * cmd_tab,const CommandTab_t * cmd,char prefix,s64 result)3170 static s64 ScanCommandListMaskHelper
3171 (
3172     void		* param,	// NULL or user defined parameter
3173     ccp			name,		// normalized name of option
3174     const CommandTab_t	* cmd_tab,	// valid pointer to command table
3175     const CommandTab_t	* cmd,		// valid pointer to found command
3176     char		prefix,		// 0 | '-' | '+' | '='
3177     s64			result		// current value of result
3178 )
3179 {
3180     return cmd->opt
3181 		? result & ~cmd->opt | cmd->id
3182 		: cmd->id;
3183 }
3184 
3185 //-----------------------------------------------------------------------------
3186 
ScanCommandListMask(ccp arg,const CommandTab_t * cmd_tab)3187 s64 ScanCommandListMask
3188 (
3189     ccp			arg,		// argument to scan
3190     const CommandTab_t	* cmd_tab	// valid pointer to command table
3191 )
3192 {
3193     return ScanCommandList(arg,cmd_tab,ScanCommandListMaskHelper,false,0,0);
3194 }
3195 
3196 ///////////////////////////////////////////////////////////////////////////////
3197 
PrintCommandError(const CommandTab_t * cmd_tab,ccp cmd_arg,int cmd_stat,ccp object)3198 void PrintCommandError
3199 (
3200     const CommandTab_t	* cmd_tab,	// NULL or pointer to command table
3201     ccp			cmd_arg,	// analyzed command
3202     int			cmd_stat,	// status of ScanCommand()
3203     ccp			object		// NULL or object for error messages
3204 					//	default= 'command'
3205 )
3206 {
3207     DASSERT(cmd_arg);
3208 
3209     if ( !object || !*object )
3210 	object = "command";
3211 
3212     if ( cmd_stat <= 0 )
3213     {
3214 	ERROR0(ERR_SYNTAX,"Unknown %s: %s\n",object,cmd_arg);
3215 	return;
3216     }
3217 
3218     char buf[100], *dest = buf;
3219     if (cmd_tab)
3220     {
3221 	int n = 0;
3222 	ccp buf_end = buf + sizeof(buf) - 2;
3223 	const int arg_len = strlen(cmd_arg);
3224 	const CommandTab_t *ct;
3225 	int last_id = -1;
3226 
3227 	for ( ct = cmd_tab; ct->name1 && dest < buf_end; ct++ )
3228 	{
3229 	    if ( ct->id != last_id )
3230 	    {
3231 		ccp ok = 0;
3232 		if (!strncasecmp(cmd_arg,ct->name1,arg_len))
3233 		    ok = ct->name1;
3234 		else if ( ct->name2 && !strncasecmp(cmd_arg,ct->name2,arg_len))
3235 		    ok = ct->name2;
3236 		if (ok)
3237 		{
3238 		    if (!n++)
3239 		    {
3240 			*dest++ = ' ';
3241 			*dest++ = '[';
3242 		    }
3243 		    else if ( n > 5 )
3244 		    {
3245 			dest = StringCopyE(dest,buf_end,",...");
3246 			break;
3247 		    }
3248 		    else
3249 			*dest++ = ',';
3250 		    dest = StringCopyE(dest,buf_end,ok);
3251 		    last_id = ct->id;
3252 		}
3253 	    }
3254 	}
3255 	if ( dest > buf+1 )
3256 	    *dest++ = ']';
3257 	else
3258 	    dest = buf;
3259     }
3260     *dest = 0;
3261     ERROR0(ERR_SYNTAX,"%c%s abbreviation is ambiguous: %s%s\n",
3262 		toupper((int)*object), object+1, cmd_arg, buf );
3263 }
3264 
3265 //
3266 ///////////////////////////////////////////////////////////////////////////////
3267 ///////////////			    sort mode			///////////////
3268 ///////////////////////////////////////////////////////////////////////////////
3269 
ScanSortMode(ccp arg)3270 SortMode ScanSortMode ( ccp arg )
3271 {
3272     static const CommandTab_t tab[] =
3273     {
3274 	{ SORT_NONE,	"NONE",		"-",		SORT__MASK },
3275 
3276 	{ SORT_ID,	"ID",		0,		SORT__MODE_MASK },
3277 	{ SORT_NAME,	"NAME",		"N",		SORT__MODE_MASK },
3278 	{ SORT_TITLE,	"TITLE",	"T",		SORT__MODE_MASK },
3279 	{ SORT_PATH,	"PATH",		"P",		SORT__MODE_MASK },
3280 	{ SORT_NINTENDO,"NINTENDO",	"NIN",		SORT__MODE_MASK },
3281 	{ SORT_FILE,	"FILE",		"F",		SORT__MODE_MASK },
3282 	{ SORT_SIZE,	"SIZE",		"SZ",		SORT__MODE_MASK },
3283 	{ SORT_OFFSET,	"OFFSET",	"OF",		SORT__MODE_MASK },
3284 	{ SORT_REGION,	"REGION",	"R",		SORT__MODE_MASK },
3285 	{ SORT_WBFS,	"WBFS",		0,		SORT__MODE_MASK },
3286 	{ SORT_NPART,	"NPART",	0,		SORT__MODE_MASK },
3287 	{ SORT_FRAG,	"FRAGMENTS",	0,		SORT__MODE_MASK },
3288 
3289 	{ SORT_ITIME,	"ITIME",	"IT",		SORT__MODE_MASK },
3290 	{ SORT_MTIME,	"MTIME",	"MT",		SORT__MODE_MASK },
3291 	{ SORT_CTIME,	"CTIME",	"CT",		SORT__MODE_MASK },
3292 	{ SORT_ATIME,	"ATIME",	"AT",		SORT__MODE_MASK },
3293 	{ SORT_TIME,	"TIME",		"TI",		SORT__MODE_MASK },
3294 	{ SORT_TIME,	"DATE",		"D",		SORT__MODE_MASK },
3295 
3296 	{ SORT_DEFAULT,	"DEFAULT",	0,		SORT__MODE_MASK },
3297 
3298 	{ 0,		"ASCENDING",	0,		SORT_REVERSE },
3299 	{ SORT_REVERSE,	"DESCENDING",	"REVERSE",	SORT_REVERSE },
3300 
3301 	{ 0,0,0,0 }
3302     };
3303 
3304     const int stat = ScanCommandListMask(arg,tab);
3305     if ( stat >= 0 )
3306 	return stat;
3307 
3308     ERROR0(ERR_SYNTAX,"Illegal sort mode (option --sort): '%s'\n",arg);
3309     return SORT__ERROR;
3310 }
3311 
3312 //-----------------------------------------------------------------------------
3313 
ScanOptSort(ccp arg)3314 int ScanOptSort ( ccp arg )
3315 {
3316     const SortMode new_mode = ScanSortMode(arg);
3317     if ( new_mode == SORT__ERROR )
3318 	return 1;
3319 
3320     TRACE("SORT-MODE set: %d -> %d\n",sort_mode,new_mode);
3321     sort_mode = new_mode;
3322     return 0;
3323 }
3324 
3325 //
3326 ///////////////////////////////////////////////////////////////////////////////
3327 ///////////////			options show + unit		///////////////
3328 ///////////////////////////////////////////////////////////////////////////////
3329 
ScanShowMode(ccp arg)3330 ShowMode ScanShowMode ( ccp arg )
3331 {
3332     enum
3333     {
3334 	DEC_ALL = SHOW_F_DEC1 | SHOW_F_DEC,
3335 	HEX_ALL = SHOW_F_HEX1 | SHOW_F_HEX,
3336     };
3337 
3338     static const CommandTab_t tab[] =
3339     {
3340 	{ SHOW__NONE,		"NONE",		"-",		SHOW__ALL },
3341 	{ SHOW__ALL,		"ALL",		0,		0 },
3342 
3343 	{ SHOW_INTRO,		"INTRO",	0,		0 },
3344 	{ SHOW_FHEADER,		"FHEADER",	0,		0 },
3345 	{ SHOW_SLOT,		"SLOTS",	0,		0 },
3346 	{ SHOW_GEOMETRY,	"GEOMETRY",	0,		0 },
3347 	{ SHOW_D_ID,		"D-ID",		"DID",		0 },
3348 	{ SHOW_P_ID,		"P-IDS",	"PIDS",		0 },
3349 	{ SHOW_P_TAB,		"P-TAB",	"PTAB",		0 },
3350 	{ SHOW_P_INFO,		"P-INFO",	"PINFO",	0 },
3351 	{ SHOW_P_MAP,		"P-MAP",	"PMAP",		0 },
3352 	{ SHOW_D_MAP,		"D-MAP",	"DMAP",		0 },
3353 	{ SHOW_W_MAP,		"W-MAP",	"WMAP",		0 },
3354 	{ SHOW_CERT,		"CERTIFICATES",	0,		0 },
3355 	{ SHOW_TICKET,		"TICKET",	"TIK",		0 },
3356 	{ SHOW_TMD,		"TMD",		0,		0 },
3357 	{ SHOW_USAGE,		"USAGE",	0,		0 },
3358 	{ SHOW_FILES,		"FILES",	0,		0 },
3359 	{ SHOW_PATCH,		"PATCH",	0,		0 },
3360 	{ SHOW_RELOCATE,	"RELOCATE",	0,		0 },
3361 	{ SHOW_PATH,		"PATH",		0,		0 },
3362 	{ SHOW_CHECK,		"CHECK",	0,		0 },
3363 
3364 	{ SHOW_UNUSED,		"UNUSED",	0,		0 },
3365 	{ SHOW_OFFSET,		"OFFSET",	0,		0 },
3366 	{ SHOW_SIZE,		"SIZE",		0,		0 },
3367 
3368 	{ SHOW__ID,		"IDS",		0,		0 },
3369 	{ SHOW__PART,		"PART",		0,		0 },
3370 	{ SHOW__DISC,		"DISC",		0,		0 },
3371 	{ SHOW__MAP,		"MAP",		0,		0 },
3372 
3373 	{ DEC_ALL,		"DEC",		0,		SHOW_F_HEX1 },
3374 	{ 0,			"-DEC",		0,		SHOW_F_DEC },
3375 	{ DEC_ALL,		"=DEC",		0,		HEX_ALL },
3376 	{ HEX_ALL,		"HEX",		0,		SHOW_F_DEC1 },
3377 	{ 0,			"-HEX",		0,		SHOW_F_HEX },
3378 	{ HEX_ALL,		"=HEX",		0,		DEC_ALL },
3379 
3380 	{ SHOW_F_HEAD,		"HEADER",	0,		0 },
3381 	{ SHOW_F_PRIMARY,	"PRIMARY",	"1",		0 },
3382 
3383 	{ 0,0,0,0 }
3384     };
3385 
3386     int stat = ScanCommandList(arg,tab,0,true,0,SHOW_F_HEAD);
3387     if ( stat != -1 )
3388 	return stat;
3389 
3390     ERROR0(ERR_SYNTAX,"Illegal show mode (option --show): '%s'\n",arg);
3391     return SHOW__ERROR;
3392 }
3393 
3394 //-----------------------------------------------------------------------------
3395 
ScanOptShow(ccp arg)3396 int ScanOptShow ( ccp arg )
3397 {
3398     const ShowMode new_mode = ScanShowMode(arg);
3399     if ( new_mode == SHOW__ERROR )
3400 	return 1;
3401 
3402     TRACE("SHOW-MODE set: %d -> %d\n",opt_show_mode,new_mode);
3403     opt_show_mode = new_mode;
3404     return 0;
3405 }
3406 
3407 //-----------------------------------------------------------------------------
3408 
ConvertShow2PFST(ShowMode show_mode,ShowMode def_mode)3409 int ConvertShow2PFST
3410 (
3411 	ShowMode show_mode,	// show mode
3412 	ShowMode def_mode	// default mode
3413 )
3414 {
3415     TRACE("ConvertShow2PFST(%x,%x)\n",show_mode,def_mode);
3416     const ShowMode MASK_OFF_SIZE = SHOW_OFFSET | SHOW_SIZE;
3417     if ( !(show_mode & MASK_OFF_SIZE) )
3418 	show_mode |= def_mode & MASK_OFF_SIZE;
3419 
3420     const ShowMode MASK_DEC_HEX = SHOW_F_DEC | SHOW_F_HEX;
3421     if ( !(show_mode & MASK_DEC_HEX) )
3422 	show_mode |= def_mode & MASK_DEC_HEX;
3423 
3424     noTRACE(" --> val=%x, off/size=%x, dec/hex=%x\n",
3425 	show_mode, show_mode & MASK_OFF_SIZE, show_mode & MASK_DEC_HEX );
3426 
3427     wd_pfst_t pfst = 0;
3428     if ( show_mode & SHOW_F_HEAD )
3429 	pfst |= WD_PFST_HEADER;
3430     if ( show_mode & SHOW_UNUSED )
3431 	pfst |= WD_PFST_UNUSED;
3432     if ( show_mode & SHOW_OFFSET )
3433 	pfst |= WD_PFST_OFFSET;
3434     if ( show_mode & SHOW_SIZE )
3435     {
3436 	switch ( show_mode & MASK_DEC_HEX )
3437 	{
3438 	    case SHOW_F_DEC:
3439 		pfst |= WD_PFST_SIZE_DEC;
3440 		break;
3441 
3442 	    case SHOW_F_HEX:
3443 		pfst |= WD_PFST_SIZE_HEX;
3444 		break;
3445 
3446 	    default:
3447 		pfst |= WD_PFST_SIZE_DEC|WD_PFST_SIZE_HEX; break;
3448 	}
3449     }
3450 
3451     TRACE(" --> PFST=%x\n",pfst);
3452     return pfst;
3453 }
3454 
3455 ///////////////////////////////////////////////////////////////////////////////
3456 ///////////////////////////////////////////////////////////////////////////////
3457 
ScanUnit(ccp arg)3458 wd_size_mode_t ScanUnit ( ccp arg )
3459 {
3460     enum
3461     {
3462 	F_1000 = WD_SIZE_F_1000,
3463 	F_1024 = WD_SIZE_F_1024,
3464     };
3465 
3466     static const CommandTab_t tab[] =
3467     {
3468 	{ WD_SIZE_DEFAULT,	"DEFAULT",0,	WD_SIZE_M_MODE|WD_SIZE_M_BASE },
3469 
3470 	{ WD_SIZE_F_1000,	"1000",	"10",	WD_SIZE_M_BASE },
3471 	{ WD_SIZE_F_1024,	"1024",	"2",	WD_SIZE_M_BASE },
3472 
3473 	{ WD_SIZE_AUTO,		"AUTO",	0,	WD_SIZE_M_MODE },
3474 	{ WD_SIZE_BYTES,	"BYTES",0,	WD_SIZE_M_MODE },
3475 	{ WD_SIZE_K,		"K",	0,	WD_SIZE_M_MODE },
3476 	{ WD_SIZE_M,		"M",	0,	WD_SIZE_M_MODE },
3477 	{ WD_SIZE_G,		"G",	0,	WD_SIZE_M_MODE },
3478 	{ WD_SIZE_T,		"T",	0,	WD_SIZE_M_MODE },
3479 	{ WD_SIZE_P,		"P",	0,	WD_SIZE_M_MODE },
3480 	{ WD_SIZE_E,		"E",	0,	WD_SIZE_M_MODE },
3481 
3482 	{ WD_SIZE_K | F_1000,	"KB",	0,	WD_SIZE_M_MODE|WD_SIZE_M_BASE },
3483 	{ WD_SIZE_M | F_1000,	"MB",	0,	WD_SIZE_M_MODE|WD_SIZE_M_BASE },
3484 	{ WD_SIZE_G | F_1000,	"GB",	0,	WD_SIZE_M_MODE|WD_SIZE_M_BASE },
3485 	{ WD_SIZE_T | F_1000,	"TB",	0,	WD_SIZE_M_MODE|WD_SIZE_M_BASE },
3486 	{ WD_SIZE_P | F_1000,	"PB",	0,	WD_SIZE_M_MODE|WD_SIZE_M_BASE },
3487 	{ WD_SIZE_E | F_1000,	"EB",	0,	WD_SIZE_M_MODE|WD_SIZE_M_BASE },
3488 
3489 	{ WD_SIZE_K | F_1024,	"KIB",	0,	WD_SIZE_M_MODE|WD_SIZE_M_BASE },
3490 	{ WD_SIZE_M | F_1024,	"MIB",	0,	WD_SIZE_M_MODE|WD_SIZE_M_BASE },
3491 	{ WD_SIZE_G | F_1024,	"GIB",	0,	WD_SIZE_M_MODE|WD_SIZE_M_BASE },
3492 	{ WD_SIZE_T | F_1024,	"TIB",	0,	WD_SIZE_M_MODE|WD_SIZE_M_BASE },
3493 	{ WD_SIZE_P | F_1024,	"PIB",	0,	WD_SIZE_M_MODE|WD_SIZE_M_BASE },
3494 	{ WD_SIZE_E | F_1024,	"EIB",	0,	WD_SIZE_M_MODE|WD_SIZE_M_BASE },
3495 
3496 	{ WD_SIZE_HD_SECT,	"HDSS",	"HSS",	WD_SIZE_M_MODE },
3497 	{ WD_SIZE_WD_SECT,	"WDSS",	"WSS",	WD_SIZE_M_MODE },
3498 	{ WD_SIZE_GC,		"GAMECUBE","GC",WD_SIZE_M_MODE },
3499 	{ WD_SIZE_WII,		"WII",	0,	WD_SIZE_M_MODE },
3500 
3501 	{ 0,0,0,0 }
3502     };
3503 
3504     int stat = ScanCommandList(arg,tab,0,true,0,0);
3505     if ( stat != -1 )
3506 	return stat;
3507 
3508     ERROR0(ERR_SYNTAX,"Illegal unit mode (option --unit): '%s'\n",arg);
3509     return -1;
3510 }
3511 
3512 //-----------------------------------------------------------------------------
3513 
ScanOptUnit(ccp arg)3514 int ScanOptUnit ( ccp arg )
3515 {
3516     const wd_size_mode_t new_mode = ScanUnit(arg);
3517     if ( new_mode == -1 )
3518 	return 1;
3519 
3520     TRACE("OPT --unit set: %x -> %x\n",opt_unit,new_mode);
3521     opt_unit = new_mode;
3522     return 0;
3523 }
3524 
3525 //
3526 ///////////////////////////////////////////////////////////////////////////////
3527 ///////////////			   repair mode			///////////////
3528 ///////////////////////////////////////////////////////////////////////////////
3529 
ScanRepairMode(ccp arg)3530 RepairMode ScanRepairMode ( ccp arg )
3531 {
3532     static const CommandTab_t tab[] =
3533     {
3534 	{ REPAIR_NONE,		"NONE",		"-",	REPAIR_ALL },
3535 
3536 	{ REPAIR_FBT,		"FBT",		"F",	0 },
3537 	{ REPAIR_INODES,	"INODES",	"I",	0 },
3538 	{ REPAIR_DEFAULT,	"STANDARD",	"STD",	0 },
3539 
3540 	{ REPAIR_RM_INVALID,	"RM-INVALID",	"RI",	0 },
3541 	{ REPAIR_RM_OVERLAP,	"RM-OVERLAP",	"RO",	0 },
3542 	{ REPAIR_RM_FREE,	"RM-FREE",	"RF",	0 },
3543 	{ REPAIR_RM_EMPTY,	"RM-EMPTY",	"RE",	0 },
3544 	{ REPAIR_RM_ALL,	"RM-ALL",	"RA",	0 },
3545 
3546 	{ REPAIR_ALL,		"ALL",		"*",	0 },
3547 	{ 0,0,0,0 }
3548     };
3549 
3550     int stat = ScanCommandList(arg,tab,0,true,0,0);
3551     if ( stat != -1 )
3552 	return stat;
3553 
3554     ERROR0(ERR_SYNTAX,"Illegal repair mode (option --repair): '%s'\n",arg);
3555     return REPAIR__ERROR;
3556 }
3557 
3558 //
3559 ///////////////////////////////////////////////////////////////////////////////
3560 ///////////////			  IdField_t			///////////////
3561 ///////////////////////////////////////////////////////////////////////////////
3562 
InitializeIdField(IdField_t * idf)3563 void InitializeIdField ( IdField_t * idf )
3564 {
3565     ASSERT(idf);
3566     memset(idf,0,sizeof(*idf));
3567 }
3568 
3569 ///////////////////////////////////////////////////////////////////////////////
3570 
ResetIdField(IdField_t * idf)3571 void ResetIdField ( IdField_t * idf )
3572 {
3573     ASSERT(idf);
3574     if ( idf && idf->field )
3575     {
3576 	IdItem_t **ptr = idf->field, **end;
3577 	for ( end = ptr + idf->used; ptr < end; ptr++ )
3578 	    FREE(*ptr);
3579 	FREE(idf->field);
3580     }
3581     InitializeIdField(idf);
3582 }
3583 
3584 ///////////////////////////////////////////////////////////////////////////////
3585 
FindIdFieldHelper(IdField_t * idf,bool * p_found,ccp key)3586 static uint FindIdFieldHelper ( IdField_t * idf, bool * p_found, ccp key )
3587 {
3588     ASSERT(idf);
3589 
3590     int beg = 0;
3591     if ( idf && key )
3592     {
3593 	int end = idf->used - 1;
3594 	while ( beg <= end )
3595 	{
3596 	    uint idx = (beg+end)/2;
3597 	    int stat = strcmp(key,idf->field[idx]->arg);
3598 	    if ( stat < 0 )
3599 		end = idx - 1 ;
3600 	    else if ( stat > 0 )
3601 		beg = idx + 1;
3602 	    else
3603 	    {
3604 		TRACE("FindIdFieldHelper(%s) FOUND=%d/%d/%d\n",
3605 			key, idx, idf->used, idf->size );
3606 		if (p_found)
3607 		    *p_found = true;
3608 		return idx;
3609 	    }
3610 	}
3611     }
3612 
3613     TRACE("FindIdFieldHelper(%s) failed=%d/%d/%d\n",
3614 		key, beg, idf->used, idf->size );
3615 
3616     if (p_found)
3617 	*p_found = false;
3618     return beg;
3619 }
3620 
3621 ///////////////////////////////////////////////////////////////////////////////
3622 
InsertIdFieldHelper(IdField_t * idf,int idx)3623 static IdItem_t ** InsertIdFieldHelper ( IdField_t * idf, int idx )
3624 {
3625     DASSERT(idf);
3626     DASSERT( idf->used <= idf->size );
3627     if ( idf->used == idf->size )
3628     {
3629 	idf->size += 0x100;
3630 	idf->field = REALLOC(idf->field,idf->size*sizeof(*idf->field));
3631     }
3632     DASSERT( idx <= idf->used );
3633     IdItem_t ** dest = idf->field + idx;
3634     memmove(dest+1,dest,(idf->used-idx)*sizeof(ccp));
3635     idf->used++;
3636     return dest;
3637 }
3638 
3639 ///////////////////////////////////////////////////////////////////////////////
3640 
FindIdField(IdField_t * idf,ccp key)3641 const IdItem_t * FindIdField ( IdField_t * idf, ccp key )
3642 {
3643     bool found;
3644     int idx = FindIdFieldHelper(idf,&found,key);
3645     return found ? idf->field[idx] : 0;
3646 }
3647 
3648 ///////////////////////////////////////////////////////////////////////////////
3649 
InsertIdField(IdField_t * idf,void * id6,char flag,time_t mtime,ccp key)3650 bool InsertIdField ( IdField_t * idf, void * id6, char flag, time_t mtime, ccp key )
3651 {
3652     bool found;
3653     int idx = FindIdFieldHelper(idf,&found,key);
3654     IdItem_t ** dest;
3655     if (found)
3656     {
3657 	DASSERT( idx < idf->used );
3658 	dest = idf->field + idx;
3659 	FREE((char*)*dest);
3660     }
3661     else
3662 	dest = InsertIdFieldHelper(idf,idx);
3663 
3664     const int key_len   = key ? strlen(key) : 0;
3665     const int item_size = sizeof(IdItem_t) + key_len + 1;
3666     IdItem_t * item = MALLOC(item_size);
3667     *dest = item;
3668 
3669     memset(item,0,item_size);
3670     if (id6)
3671 	strncpy(item->id6,id6,6);
3672     item->flag = flag;
3673     item->mtime = mtime;
3674     if (key)
3675 	memcpy(item->arg,key,key_len);
3676     return !found;
3677 }
3678 
3679 ///////////////////////////////////////////////////////////////////////////////
3680 
RemoveIdField(IdField_t * idf,ccp key)3681 bool RemoveIdField ( IdField_t * idf, ccp key )
3682 {
3683     bool found;
3684     uint idx = FindIdFieldHelper(idf,&found,key);
3685     if (found)
3686     {
3687 	idf->used--;
3688 	ASSERT( idx <= idf->used );
3689 	IdItem_t ** dest = idf->field + idx;
3690 	FREE(*dest);
3691 	memmove(dest,dest+1,(idf->used-idx)*sizeof(ccp));
3692     }
3693     return found;
3694 }
3695 
3696 ///////////////////////////////////////////////////////////////////////////////
3697 
DumpIdField(FILE * f,int indent,const IdField_t * idf)3698 void DumpIdField ( FILE *f, int indent, const IdField_t * idf )
3699 {
3700     DASSERT(f);
3701     DASSERT(idf);
3702     indent = NormalizeIndent(indent);
3703 
3704     int i;
3705     for ( i = 0; i < idf->used; i++ )
3706     {
3707 	const IdItem_t * item = idf->field[i];
3708 	char timbuf[40];
3709 	if (item->mtime)
3710 	{
3711 	    struct tm * tm = localtime(&item->mtime);
3712 	    strftime(timbuf,sizeof(timbuf),"%F %T",tm);
3713 	}
3714 	else
3715 	    strncpy(timbuf,"     -         -   ",sizeof(timbuf));
3716 
3717 	fprintf(f,"%*s%-6s %02x %s %s\n",
3718 		indent, "", item->id6, item->flag, timbuf, item->arg );
3719     }
3720 }
3721 
3722 //
3723 ///////////////////////////////////////////////////////////////////////////////
3724 ///////////////			  StringField_t			///////////////
3725 ///////////////////////////////////////////////////////////////////////////////
3726 
InitializeStringField(StringField_t * sf)3727 void InitializeStringField ( StringField_t * sf )
3728 {
3729     ASSERT(sf);
3730     memset(sf,0,sizeof(*sf));
3731 }
3732 
3733 ///////////////////////////////////////////////////////////////////////////////
3734 
ResetStringField(StringField_t * sf)3735 void ResetStringField ( StringField_t * sf )
3736 {
3737     ASSERT(sf);
3738     if ( sf && sf->used > 0 )
3739     {
3740 	ASSERT(sf->field);
3741 	ccp *ptr = sf->field, *end;
3742 	for ( end = ptr + sf->used; ptr < end; ptr++ )
3743 	    FREE((char*)*ptr);
3744 	FREE(sf->field);
3745     }
3746     InitializeStringField(sf);
3747 }
3748 
3749 ///////////////////////////////////////////////////////////////////////////////
3750 
FindStringField(StringField_t * sf,ccp key)3751 ccp FindStringField ( StringField_t * sf, ccp key )
3752 {
3753     bool found;
3754     int idx = FindStringFieldHelper(sf,&found,key);
3755     return found ? sf->field[idx] : 0;
3756 }
3757 
3758 ///////////////////////////////////////////////////////////////////////////////
3759 
InsertStringFieldHelper(StringField_t * sf,int idx)3760 static ccp * InsertStringFieldHelper ( StringField_t * sf, int idx )
3761 {
3762     DASSERT(sf);
3763     DASSERT( sf->used <= sf->size );
3764     if ( sf->used == sf->size )
3765     {
3766 	sf->size += 0x100;
3767 	sf->field = REALLOC(sf->field,sf->size*sizeof(*sf->field));
3768     }
3769     DASSERT( idx <= sf->used );
3770     ccp * dest = sf->field + idx;
3771     memmove(dest+1,dest,(sf->used-idx)*sizeof(ccp));
3772     sf->used++;
3773     return dest;
3774 }
3775 
3776 ///////////////////////////////////////////////////////////////////////////////
3777 
InsertStringField(StringField_t * sf,ccp key,bool move_key)3778 bool InsertStringField ( StringField_t * sf, ccp key, bool move_key )
3779 {
3780     if (!key)
3781 	return 0;
3782 
3783     bool found;
3784     int idx = FindStringFieldHelper(sf,&found,key);
3785     if (found)
3786     {
3787 	if (move_key)
3788 	    FREE((char*)key);
3789     }
3790     else
3791     {
3792 	ccp * dest = InsertStringFieldHelper(sf,idx);
3793 	*dest = move_key ? key : STRDUP(key);
3794     }
3795 
3796     return !found;
3797 }
3798 
3799 ///////////////////////////////////////////////////////////////////////////////
3800 
InsertStringID6(StringField_t * sf,void * id6,char flag,ccp arg)3801 IdItem_t * InsertStringID6 ( StringField_t * sf, void * id6, char flag, ccp arg )
3802 {
3803     if (!id6)
3804 	return 0;
3805 
3806     bool found;
3807     int idx = FindStringFieldHelper(sf,&found,id6);
3808     ccp * dest;
3809     if (found)
3810     {
3811 	DASSERT( idx < sf->used );
3812 	dest = sf->field + idx;
3813 	FREE((char*)*dest);
3814     }
3815     else
3816 	dest = InsertStringFieldHelper(sf,idx);
3817 
3818     const int arg_len   = arg ? strlen(arg) : 0;
3819     const int item_size = sizeof(IdItem_t) + arg_len + 1;
3820     IdItem_t * item = MALLOC(item_size);
3821 
3822     *dest = (ccp)item;
3823     memset(item,0,item_size);
3824     strncpy(item->id6,id6,6);
3825     item->flag = flag;
3826     if (arg)
3827 	memcpy(item->arg,arg,arg_len);
3828     return item;
3829 }
3830 
3831 ///////////////////////////////////////////////////////////////////////////////
3832 
RemoveStringField(StringField_t * sf,ccp key)3833 bool RemoveStringField ( StringField_t * sf, ccp key )
3834 {
3835     bool found;
3836     uint idx = FindStringFieldHelper(sf,&found,key);
3837     if (found)
3838     {
3839 	sf->used--;
3840 	ASSERT( idx <= sf->used );
3841 	ccp * dest = sf->field + idx;
3842 	FREE((char*)dest);
3843 	memmove(dest,dest+1,(sf->used-idx)*sizeof(ccp));
3844     }
3845     return found;
3846 }
3847 
3848 ///////////////////////////////////////////////////////////////////////////////
3849 
AppendStringField(StringField_t * sf,ccp key,bool move_key)3850 void AppendStringField ( StringField_t * sf, ccp key, bool move_key )
3851 {
3852     if (key)
3853     {
3854 	ASSERT( sf->used <= sf->size );
3855 	if ( sf->used == sf->size )
3856 	{
3857 	    sf->size += 0x100;
3858 	    sf->field = REALLOC(sf->field,sf->size*sizeof(*sf->field));
3859 	}
3860 	TRACE("AppendStringField(%s,%d) %d/%d\n",key,move_key,sf->used,sf->size);
3861 	ccp * dest = sf->field + sf->used++;
3862 	*dest = move_key ? key : STRDUP(key);
3863     }
3864 }
3865 
3866 ///////////////////////////////////////////////////////////////////////////////
3867 
FindStringFieldHelper(StringField_t * sf,bool * p_found,ccp key)3868 uint FindStringFieldHelper ( StringField_t * sf, bool * p_found, ccp key )
3869 {
3870     ASSERT(sf);
3871 
3872     int beg = 0;
3873     if ( sf && key )
3874     {
3875 	int end = sf->used - 1;
3876 	while ( beg <= end )
3877 	{
3878 	    uint idx = (beg+end)/2;
3879 	    int stat = strcmp(key,sf->field[idx]);
3880 	    if ( stat < 0 )
3881 		end = idx - 1 ;
3882 	    else if ( stat > 0 )
3883 		beg = idx + 1;
3884 	    else
3885 	    {
3886 		TRACE("FindStringFieldHelper(%s) FOUND=%d/%d/%d\n",
3887 			key, idx, sf->used, sf->size );
3888 		if (p_found)
3889 		    *p_found = true;
3890 		return idx;
3891 	    }
3892 	}
3893     }
3894 
3895     TRACE("FindStringFieldHelper(%s) failed=%d/%d/%d\n",
3896 		key, beg, sf->used, sf->size );
3897 
3898     if (p_found)
3899 	*p_found = false;
3900     return beg;
3901 }
3902 
3903 ///////////////////////////////////////////////////////////////////////////////
3904 
LoadStringField(StringField_t * sf,bool keep_order,ccp filename,bool silent)3905 enumError LoadStringField
3906 	( StringField_t * sf, bool keep_order, ccp filename, bool silent )
3907 {
3908     ASSERT(sf);
3909     ASSERT(filename);
3910     ASSERT(*filename);
3911 
3912     TRACE("LoadStringField(%p,%d,%s,%d)\n",sf,keep_order,filename,silent);
3913 
3914     FILE * f = fopen(filename,"rb");
3915     if (!f)
3916     {
3917 	if (!silent)
3918 	    ERROR1(ERR_CANT_OPEN,"Can't open file: %s\n",filename);
3919 	return ERR_CANT_OPEN;
3920     }
3921 
3922     while (fgets(iobuf,sizeof(iobuf)-1,f))
3923     {
3924 	char * ptr = iobuf;
3925 	while (*ptr)
3926 	    ptr++;
3927 	if ( ptr > iobuf && ptr[-1] == '\n' )
3928 	{
3929 	    ptr--;
3930 	    if ( ptr > iobuf && ptr[-1] == '\r' )
3931 		ptr--;
3932 	}
3933 
3934 	if ( ptr > iobuf )
3935 	{
3936 	    *ptr++ = 0;
3937 	    const size_t len = ptr-iobuf;
3938 	    ptr = MALLOC(len);
3939 	    memcpy(ptr,iobuf,len);
3940 	    if (keep_order)
3941 		AppendStringField(sf,ptr,true);
3942 	    else
3943 		InsertStringField(sf,ptr,true);
3944 	}
3945     }
3946 
3947     fclose(f);
3948     return ERR_OK;
3949 }
3950 
3951 
3952 ///////////////////////////////////////////////////////////////////////////////
3953 
SaveStringField(StringField_t * sf,ccp filename,bool rm_if_empty)3954 enumError SaveStringField
3955 	( StringField_t * sf, ccp filename, bool rm_if_empty )
3956 {
3957     ASSERT(sf);
3958     ASSERT(filename);
3959     ASSERT(*filename);
3960 
3961     TRACE("SaveStringField(%p,%s,%d)\n",sf,filename,rm_if_empty);
3962 
3963     if ( !sf->used && rm_if_empty )
3964     {
3965 	unlink(filename);
3966 	return ERR_OK;
3967     }
3968     FILE * f = fopen(filename,"wb");
3969     if (!f)
3970 	return ERROR1(ERR_CANT_CREATE,"Can't create file: %s\n",filename);
3971 
3972     ccp *ptr = sf->field, *end;
3973     for ( end = ptr + sf->used; ptr < end; ptr++ )
3974 	fprintf(f,"%s\n",*ptr);
3975     fclose(f);
3976     return ERR_OK;
3977 }
3978 
3979 //
3980 ///////////////////////////////////////////////////////////////////////////////
3981 ///////////////			ParamField_t			///////////////
3982 ///////////////////////////////////////////////////////////////////////////////
3983 
InitializeParamField(ParamField_t * pf,ParamFieldType_t pft)3984 void InitializeParamField ( ParamField_t * pf, ParamFieldType_t pft )
3985 {
3986     DASSERT(pf);
3987     memset(pf,0,sizeof(*pf));
3988     pf->pft = pft;
3989 }
3990 
3991 ///////////////////////////////////////////////////////////////////////////////
3992 
ResetParamField(ParamField_t * pf)3993 void ResetParamField ( ParamField_t * pf )
3994 {
3995     ASSERT(pf);
3996     if ( pf && pf->used > 0 )
3997     {
3998 	ASSERT(pf->list);
3999 	ParamFieldItem_t *ptr = pf->list, *end;
4000 	for ( end = ptr + pf->used; ptr < end; ptr++ )
4001 	    FreeString(ptr->key);
4002 	FREE(pf->list);
4003     }
4004     InitializeParamField(pf,pf->pft);
4005 }
4006 
4007 ///////////////////////////////////////////////////////////////////////////////
4008 
MoveParamField(ParamField_t * dest,ParamField_t * src)4009 void MoveParamField ( ParamField_t * dest, ParamField_t * src )
4010 {
4011     DASSERT(src);
4012     DASSERT(dest);
4013     if ( src != dest )
4014     {
4015 	ResetParamField(dest);
4016 	dest->list = src->list;
4017 	dest->used = src->used;
4018 	dest->size = src->size;
4019 	dest->pft  = src->pft;
4020 	InitializeParamField(src,src->pft);
4021     }
4022 }
4023 
4024 ///////////////////////////////////////////////////////////////////////////////
4025 
FindParamFieldHelper(const ParamField_t * pf,bool * p_found,ccp key)4026 static uint FindParamFieldHelper ( const ParamField_t * pf, bool * p_found, ccp key )
4027 {
4028     ASSERT(pf);
4029 
4030     int beg = 0;
4031     if ( pf && key )
4032     {
4033 	int end = pf->used - 1;
4034 	while ( beg <= end )
4035 	{
4036 	    uint idx = (beg+end)/2;
4037 	    int stat = strcmp(key,pf->list[idx].key);
4038 	    if ( stat < 0 )
4039 		end = idx - 1 ;
4040 	    else if ( stat > 0 )
4041 		beg = idx + 1;
4042 	    else
4043 	    {
4044 		TRACE("FindParamFieldHelper(%s) FOUND=%d/%d/%d\n",
4045 			key, idx, pf->used, pf->size );
4046 		if (p_found)
4047 		    *p_found = true;
4048 		return idx;
4049 	    }
4050 	}
4051     }
4052 
4053     TRACE("FindParamFieldHelper(%s) failed=%d/%d/%d\n",
4054 		key, beg, pf->used, pf->size );
4055 
4056     if (p_found)
4057 	*p_found = false;
4058     return beg;
4059 }
4060 
4061 ///////////////////////////////////////////////////////////////////////////////
4062 
FindParamFieldIndex(const ParamField_t * pf,ccp key,int not_found_value)4063 int FindParamFieldIndex ( const ParamField_t * pf, ccp key, int not_found_value )
4064 {
4065     bool found;
4066     const int idx = FindParamFieldHelper(pf,&found,key);
4067     return found ? idx : not_found_value;
4068 }
4069 
4070 ///////////////////////////////////////////////////////////////////////////////
4071 
FindParamField(const ParamField_t * pf,ccp key)4072 ParamFieldItem_t * FindParamField ( const ParamField_t * pf, ccp key )
4073 {
4074     bool found;
4075     const int idx = FindParamFieldHelper(pf,&found,key);
4076     return found ? pf->list + idx : 0;
4077 }
4078 
4079 ///////////////////////////////////////////////////////////////////////////////
4080 
InsertParamFieldHelper(ParamField_t * pf,int idx)4081 static ParamFieldItem_t * InsertParamFieldHelper ( ParamField_t * pf, int idx )
4082 {
4083     DASSERT(pf);
4084     DASSERT( pf->used <= pf->size );
4085     noPRINT("+FF: %u/%u/%u\n",idx,pf->used,pf->size);
4086     if ( pf->used == pf->size )
4087     {
4088 	pf->size += 0x100;
4089 	pf->list = REALLOC(pf->list,pf->size*sizeof(*pf->list));
4090     }
4091     DASSERT( idx <= pf->used );
4092     ParamFieldItem_t * dest = pf->list + idx;
4093     memmove(dest+1,dest,(pf->used-idx)*sizeof(*dest));
4094     pf->used++;
4095     memset(dest,0,sizeof(*dest));
4096     return dest;
4097 }
4098 
4099 ///////////////////////////////////////////////////////////////////////////////
4100 
RemoveParamField(ParamField_t * pf,ccp key)4101 bool RemoveParamField ( ParamField_t * pf, ccp key )
4102 {
4103     bool found;
4104     uint idx = FindParamFieldHelper(pf,&found,key);
4105     if (found)
4106     {
4107 	pf->used--;
4108 	ASSERT( idx <= pf->used );
4109 	ParamFieldItem_t * dest = pf->list + idx;
4110 	FREE((char*)dest);
4111 	memmove(dest,dest+1,(pf->used-idx)*sizeof(*dest));
4112     }
4113     return found;
4114 }
4115 
4116 ///////////////////////////////////////////////////////////////////////////////
4117 
InsertParamField(ParamField_t * pf,ccp key,uint num)4118 ParamFieldItem_t * InsertParamField
4119 (
4120     ParamField_t	* pf,		// valid param field
4121     ccp			key,		// key to insert
4122     uint		num		// value
4123 )
4124 {
4125     if (!key)
4126 	return 0;
4127 
4128     bool my_found;
4129     const int idx = FindParamFieldHelper(pf,&my_found,key);
4130 
4131     ParamFieldItem_t * item;
4132     if (my_found)
4133 	item = pf->list + idx;
4134     else
4135     {
4136 	item = InsertParamFieldHelper(pf,idx);
4137 	item->key = STRDUP(key);
4138     }
4139 
4140     item->count++;
4141 
4142     if ( pf->pft == PFT_ALIGN )
4143 	item->num |= num;
4144     else
4145 	item->num = num;
4146     return item;
4147 }
4148 
4149 ///////////////////////////////////////////////////////////////////////////////
4150 
LoadParamField(ParamField_t * pf,ParamFieldType_t init_pf,ccp filename,bool silent)4151 enumError LoadParamField
4152 (
4153     ParamField_t	* pf,		// param field
4154     ParamFieldType_t	init_pf,	// >0: initialize 'pf' with entered type
4155     ccp			filename,	// filename of source file
4156     bool		silent		// true: don't print open/read errors
4157 )
4158 {
4159     ASSERT(pf);
4160     ASSERT(filename);
4161     ASSERT(*filename);
4162 
4163     TRACE("LoadParamField(%p,%d,%s,%d)\n",pf,init_pf,filename,silent);
4164 
4165     if (init_pf)
4166 	InitializeParamField(pf,init_pf);
4167 
4168     FILE * f = fopen(filename,"rb");
4169     if (!f)
4170     {
4171 	if (!silent)
4172 	    ERROR1(ERR_CANT_OPEN,"Can't open file: %s\n",filename);
4173 	return ERR_CANT_OPEN;
4174     }
4175 
4176     while (fgets(iobuf,sizeof(iobuf)-1,f))
4177     {
4178 	char *ptr = iobuf;
4179 
4180 	u32 stat, num;
4181 	ptr = ScanNumU32(ptr+1,&stat,&num,0,~(u32)0);
4182 	if (!stat)
4183 	    continue;
4184 
4185 	while ( *ptr > 0 && *ptr <= ' ' )
4186 	    ptr++;
4187 	if ( *ptr != '=' )
4188 	    continue;
4189 	ptr++;
4190 
4191 	while ( *ptr > 0 && *ptr <= ' ' )
4192 	    ptr++;
4193 	char *key = ptr;
4194 
4195 	while (*ptr)
4196 	    ptr++;
4197 	while ( ptr > key && (uchar)ptr[-1] <= ' ' )
4198 	    ptr--;
4199 
4200 	if ( key < ptr )
4201 	{
4202 	    *ptr = 0;
4203 	    InsertParamField(pf,key,num);
4204 	}
4205     }
4206 
4207     fclose(f);
4208     return ERR_OK;
4209 }
4210 
4211 ///////////////////////////////////////////////////////////////////////////////
4212 
SaveParamField(ParamField_t * pf,ccp filename,bool rm_if_empty)4213 enumError SaveParamField
4214 (
4215     ParamField_t	* pf,		// valid param field
4216     ccp			filename,	// filename of dest file
4217     bool		rm_if_empty	// true: rm dest file if 'pf' is empty
4218 )
4219 {
4220     ASSERT(pf);
4221     ASSERT(filename);
4222     ASSERT(*filename);
4223 
4224     TRACE("SaveParamField(%p,%s,%d)\n",pf,filename,rm_if_empty);
4225 
4226     if ( !pf->used && rm_if_empty )
4227     {
4228 	unlink(filename);
4229 	return ERR_OK;
4230     }
4231 
4232     FILE * f = fopen(filename,"wb");
4233     if (!f)
4234 	return ERROR1(ERR_CANT_CREATE,"Can't create file: %s\n",filename);
4235 
4236     enumError err = WriteParamField(f,filename,pf,0,0,0);
4237     fclose(f);
4238     return err;
4239 }
4240 
4241 ///////////////////////////////////////////////////////////////////////////////
4242 
WriteParamField(FILE * f,ccp filename,ParamField_t * pf,ccp line_prefix,ccp key_prefix,ccp eol)4243 enumError WriteParamField
4244 (
4245     FILE		* f,		// open destination file
4246     ccp			filename,	// NULL or filename (needed on write error)
4247     ParamField_t	* pf,		// valid param field
4248     ccp			line_prefix,	// not NULL: insert prefix before each line
4249     ccp			key_prefix,	// not NULL: insert prefix before each key
4250     ccp			eol		// end of line text (if NULL: use LF)
4251 )
4252 {
4253     if (!key_prefix)
4254 	key_prefix = "";
4255     if (!eol)
4256 	eol = "\n";
4257 
4258     uint max_num = 0;
4259     ParamFieldItem_t *ptr, *end = pf->list + pf->used;
4260     for ( ptr = pf->list; ptr < end; ptr++ )
4261     {
4262 	if ( ptr->num && pf->pft == PFT_ALIGN )
4263 	{
4264 	    uint num = 1;
4265 	    while (!( ptr->num & num ))
4266 		num <<= 1;
4267 	    ptr->num = num;
4268 	}
4269 
4270 	if ( max_num < ptr->num )
4271 	     max_num = ptr->num;
4272     }
4273 
4274     char buf[20];
4275     if ( pf->pft == PFT_ALIGN )
4276     {
4277 	uint num_fw = snprintf(buf,sizeof(buf),"%#x",max_num);
4278 	for ( ptr = pf->list; ptr < end; ptr++ )
4279 	    fprintf(f," %#*x = %s\n", num_fw, ptr->num, ptr->key );
4280     }
4281     else
4282     {
4283 	uint num_fw = snprintf(buf,sizeof(buf),"%u",max_num);
4284 	for ( ptr = pf->list; ptr < end; ptr++ )
4285 	    fprintf(f," %*u = %s\n", num_fw, ptr->num, ptr->key );
4286     }
4287     return ERR_OK;
4288 }
4289 
4290 //
4291 ///////////////////////////////////////////////////////////////////////////////
4292 ///////////////			  string lists			///////////////
4293 ///////////////////////////////////////////////////////////////////////////////
4294 
AtFileHelper(ccp arg,int mode,int mode_expand,int (* func)(ccp arg,int mode))4295 int AtFileHelper
4296 (
4297     ccp arg,
4298     int mode,
4299     int mode_expand,
4300     int (*func) ( ccp arg, int mode )
4301 )
4302 {
4303     if ( !arg || !*arg || !func )
4304 	return 0;
4305 
4306     TRACE("AtFileHelper(%s,%x,%x)\n",arg,mode,mode_expand);
4307     if ( *arg != '@' )
4308 	return func(arg,mode);
4309 
4310     FILE * f;
4311     char buf[PATH_MAX];
4312     const bool use_stdin = arg[1] == '-' && !arg[2];
4313 
4314     if (use_stdin)
4315 	f = stdin;
4316     else
4317     {
4318      #ifdef __CYGWIN__
4319 	char buf[PATH_MAX];
4320 	NormalizeFilenameCygwin(buf,sizeof(buf),arg+1);
4321 	f = fopen(buf,"r");
4322      #else
4323 	f = fopen(arg+1,"r");
4324      #endif
4325 	if (!f)
4326 	    return func(arg,mode);
4327     }
4328 
4329     ASSERT(f);
4330 
4331     u32 max_stat = 0;
4332     while (fgets(buf,sizeof(buf)-1,f))
4333     {
4334 	char * ptr = buf;
4335 	while (*ptr)
4336 	    ptr++;
4337 	if ( ptr > buf && ptr[-1] == '\n' )
4338 	    ptr--;
4339 	if ( ptr > buf && ptr[-1] == '\r' )
4340 	    ptr--;
4341 	*ptr = 0;
4342 	const u32 stat = func(buf,mode_expand);
4343 	if ( max_stat < stat )
4344 	     max_stat = stat;
4345     }
4346     fclose(f);
4347     return max_stat;
4348 }
4349 
4350 ///////////////////////////////////////////////////////////////////////////////
4351 
4352 uint n_param = 0, id6_param_found = 0;
4353 ParamList_t * first_param = 0;
4354 ParamList_t ** append_param = &first_param;
4355 
4356 ///////////////////////////////////////////////////////////////////////////////
4357 
GetPoolParam()4358 static ParamList_t* GetPoolParam()
4359 {
4360     static ParamList_t * pool = 0;
4361     static int n_pool = 0;
4362 
4363     if (!n_pool)
4364     {
4365 	const int alloc_count = 100;
4366 	pool = (ParamList_t*) CALLOC(alloc_count,sizeof(ParamList_t));
4367 	n_pool = alloc_count;
4368     }
4369 
4370     n_pool--;
4371     return pool++;
4372 }
4373 
4374 ///////////////////////////////////////////////////////////////////////////////
4375 
AppendParam(ccp arg,int is_temp)4376 ParamList_t * AppendParam ( ccp arg, int is_temp )
4377 {
4378     if ( !arg || !*arg )
4379 	return 0;
4380 
4381     TRACE("ARG#%02d: %s\n",n_param,arg);
4382 
4383     ParamList_t * param = GetPoolParam();
4384     if (is_temp)
4385 	param->arg = STRDUP(arg);
4386     else
4387 	param->arg = (char*)arg;
4388 
4389     while (*append_param)
4390 	append_param = &(*append_param)->next;
4391 
4392     noTRACE("INS: A=%p->%p P=%p &N=%p->%p\n",
4393 	    append_param, *append_param,
4394 	    param, &param->next, param->next );
4395     *append_param = param;
4396     append_param = &param->next;
4397     noTRACE("  => A=%p->%p\n", append_param, *append_param );
4398     n_param++;
4399 
4400     return param;
4401 }
4402 
4403 ///////////////////////////////////////////////////////////////////////////////
4404 
AddParam(ccp arg,int is_temp)4405 int AddParam ( ccp arg, int is_temp )
4406 {
4407     return AppendParam(arg,is_temp) ? 0 : 1;
4408 }
4409 
4410 ///////////////////////////////////////////////////////////////////////////////
4411 
AtExpandParam(ParamList_t ** p_param)4412 void AtExpandParam ( ParamList_t ** p_param )
4413 {
4414     if ( !p_param || !*p_param )
4415 	return;
4416 
4417     ParamList_t * param = *p_param;
4418     if ( param->is_expanded || !param->arg || *param->arg != '@' )
4419 	return;
4420 
4421     FILE * f;
4422     char buf[PATH_MAX];
4423     const bool use_stdin = param->arg[1] == '-' && !param->arg[2];
4424     if (use_stdin)
4425 	f = stdin;
4426     else
4427     {
4428      #ifdef __CYGWIN__
4429 	NormalizeFilenameCygwin(buf,sizeof(buf),param->arg+1);
4430 	f = fopen(buf,"r");
4431      #else
4432 	f = fopen(param->arg+1,"r");
4433      #endif
4434 	if (!f)
4435 	    return;
4436     }
4437 
4438     ASSERT(f);
4439 
4440     u32 count = 0;
4441     while (fgets(buf,sizeof(buf)-1,f))
4442     {
4443 	char * ptr = buf;
4444 	while (*ptr)
4445 	    ptr++;
4446 	if ( ptr > buf && ptr[-1] == '\n' )
4447 	    ptr--;
4448 	if ( ptr > buf && ptr[-1] == '\r' )
4449 	    ptr--;
4450 	*ptr = 0;
4451 
4452 	if (count++)
4453 	{
4454 	    // insert a new item
4455 	    ParamList_t * new_param = GetPoolParam();
4456 	    new_param->next = param->next;
4457 	    param->next = new_param;
4458 	    param = new_param;
4459 	    n_param++;
4460 	}
4461 	param->arg = STRDUP(buf);
4462 	param->is_expanded = true;
4463     }
4464     fclose(f);
4465 
4466     if (!count)
4467     {
4468 	*p_param = param->next;
4469 	n_param--;
4470     }
4471 
4472     append_param = &first_param;
4473 }
4474 
4475 ///////////////////////////////////////////////////////////////////////////////
4476 
AtExpandAllParam(ParamList_t ** p_param)4477 void AtExpandAllParam ( ParamList_t ** p_param )
4478 {
4479     if (p_param)
4480 	for ( ; *p_param; p_param = &(*p_param)->next )
4481 	    AtExpandParam(p_param);
4482 }
4483 
4484 //
4485 ///////////////////////////////////////////////////////////////////////////////
4486 ///////////////		     string substitutions		///////////////
4487 ///////////////////////////////////////////////////////////////////////////////
4488 
SubstString(char * buf,size_t bufsize,SubstString_t * tab,ccp source,int * count)4489 char * SubstString
4490 	( char * buf, size_t bufsize, SubstString_t * tab, ccp source, int * count )
4491 {
4492     ASSERT(buf);
4493     ASSERT(bufsize > 1);
4494     ASSERT(tab);
4495     TRACE("SubstString(%s)\n",source);
4496 
4497     char tempbuf[PATH_MAX];
4498     int conv_count = 0;
4499 
4500     char *dest = buf;
4501     char *end = buf + bufsize + 1;
4502     if (source)
4503 	while ( dest < end && *source )
4504 	{
4505 	    if ( *source != escape_char && *source != 1 )
4506 	    {
4507 		*dest++ = *source++;
4508 		continue;
4509 	    }
4510 	    if ( source[0] == source[1] )
4511 	    {
4512 		source++;
4513 		*dest++ = *source++;
4514 		continue;
4515 	    }
4516 	    ccp start = source++;
4517 
4518 	    u32 p1, p2, stat;
4519 	    source = ScanRangeU32(source,&stat,&p1,&p2,0,~(u32)0);
4520 	    if ( stat == 1 )
4521 		p1 = 0;
4522 	    else if ( stat < 1 )
4523 	    {
4524 		p1 = 0;
4525 		p2 = ~(u32)0;
4526 	    }
4527 
4528 	    char ch = *source++;
4529 	    int convert = 0;
4530 	    if ( ch == 'u' || ch == 'U' )
4531 	    {
4532 		convert++;
4533 		ch = *source++;
4534 	    }
4535 	    else if ( ch == 'l' || ch == 'L' )
4536 	    {
4537 		convert--;
4538 		ch = *source++;
4539 	    }
4540 	    if (!ch)
4541 		break;
4542 
4543 	    size_t count = source - start;
4544 
4545 	    SubstString_t * ptr;
4546 	    for ( ptr = tab; ptr->c1; ptr++ )
4547 		if ( ch == ptr->c1 || ch == ptr->c2 )
4548 		{
4549 		    if (ptr->str)
4550 		    {
4551 			const size_t slen = strlen(ptr->str);
4552 			if ( p1 > slen )
4553 			    p1 = slen;
4554 			if ( p2 > slen )
4555 			    p2 = slen;
4556 			count = p2 - p1;
4557 			start = ptr->str+p1;
4558 			conv_count++;
4559 		    }
4560 		    else
4561 			count = 0;
4562 		    break;
4563 		}
4564 
4565 	    if (!ptr->c1) // invalid conversion
4566 		convert = 0;
4567 
4568 	    if ( count > sizeof(tempbuf)-1 )
4569 		 count = sizeof(tempbuf)-1;
4570 	    TRACE("COPY '%.*s' conv=%d\n",(int)count,start,convert);
4571 	    if ( convert > 0 )
4572 	    {
4573 		char * tp = tempbuf;
4574 		while ( count-- > 0 )
4575 		    *tp++ = toupper((int)*start++);
4576 		*tp = 0;
4577 	    }
4578 	    else if ( convert < 0 )
4579 	    {
4580 		char * tp = tempbuf;
4581 		while ( count-- > 0 )
4582 		    *tp++ = tolower((int)*start++); // cygwin needs the '(int)'
4583 		*tp = 0;
4584 	    }
4585 	    else
4586 	    {
4587 		memcpy(tempbuf,start,count);
4588 		tempbuf[count] = 0;
4589 	    }
4590 	    dest = NormalizeFileName(dest,end,tempbuf,ptr->allow_slash);
4591 	}
4592 
4593     if (count)
4594 	*count = conv_count;
4595     *dest = 0;
4596     return dest;
4597 }
4598 
4599 ///////////////////////////////////////////////////////////////////////////////
4600 
ScanEscapeChar(ccp arg)4601 int ScanEscapeChar ( ccp arg )
4602 {
4603     if ( arg && strlen(arg) > 1 )
4604     {
4605 	ERROR0(ERR_SYNTAX,"Illegal character (option --esc): '%s'\n",arg);
4606 	return -1;
4607     }
4608 
4609     escape_char = arg ? *arg : 0;
4610     return (unsigned char)escape_char;
4611 }
4612 
4613 ///////////////////////////////////////////////////////////////////////////////
4614 
HaveEscapeChar(ccp string)4615 bool HaveEscapeChar ( ccp string )
4616 {
4617     if (string)
4618 	while(*string)
4619 	{
4620 	    const char ch = *string++;
4621 	    if ( ch == escape_char || ch == '\1' )
4622 		return true;
4623 	}
4624     return false;
4625 }
4626 
4627 //
4628 ///////////////////////////////////////////////////////////////////////////////
4629 ///////////////			    Memory Maps			///////////////
4630 ///////////////////////////////////////////////////////////////////////////////
4631 
InitializeMemMap(MemMap_t * mm)4632 void InitializeMemMap ( MemMap_t * mm )
4633 {
4634     DASSERT(mm);
4635     memset(mm,0,sizeof(*mm));
4636 }
4637 
4638 ///////////////////////////////////////////////////////////////////////////////
4639 
ResetMemMap(MemMap_t * mm)4640 void ResetMemMap ( MemMap_t * mm )
4641 {
4642     DASSERT(mm);
4643 
4644     uint i;
4645     if (mm->field)
4646     {
4647 	for ( i = 0; i < mm->used; i++ )
4648 	    FREE(mm->field[i]);
4649 	FREE(mm->field);
4650     }
4651     memset(mm,0,sizeof(*mm));
4652 }
4653 
4654 ///////////////////////////////////////////////////////////////////////////////
4655 
FindMemMap(MemMap_t * mm,off_t off,off_t size)4656 MemMapItem_t * FindMemMap ( MemMap_t * mm, off_t off, off_t size )
4657 {
4658     DASSERT(mm);
4659 
4660     off_t off_end = off + size;
4661     int beg = 0;
4662     int end = mm->used - 1;
4663     while ( beg <= end )
4664     {
4665 	uint idx = (beg+end)/2;
4666 	MemMapItem_t * mi = mm->field[idx];
4667 	if ( off_end <= mi->off )
4668 	    end = idx - 1 ;
4669 	else if ( off >= mi->off + mi->size )
4670 	    beg = idx + 1;
4671 	else
4672 	    return mi;
4673     }
4674     return 0;
4675 }
4676 
4677 ///////////////////////////////////////////////////////////////////////////////
4678 
InsertMemMapIndex(MemMap_t * mm,off_t off,off_t size)4679 uint InsertMemMapIndex
4680 (
4681     // returns the index of the new item
4682 
4683     MemMap_t		* mm,		// mem map pointer
4684     off_t		off,		// offset of area
4685     off_t		size		// size of area
4686 )
4687 {
4688     DASSERT(mm);
4689     uint idx = FindMemMapHelper(mm,off,size);
4690 
4691     DASSERT( mm->used <= mm->size );
4692     if ( mm->used == mm->size )
4693     {
4694 	mm->size += 64;
4695 	mm->field = REALLOC(mm->field,mm->size*sizeof(*mm->field));
4696     }
4697 
4698     DASSERT( idx <= mm->used );
4699     MemMapItem_t ** dest = mm->field + idx;
4700     memmove(dest+1,dest,(mm->used-idx)*sizeof(MemMapItem_t*));
4701     mm->used++;
4702 
4703     MemMapItem_t * mi = MALLOC(sizeof(MemMapItem_t));
4704     mi->off  = off;
4705     mi->size = size;
4706     mi->overlap = 0;
4707     *dest = mi;
4708     return idx;
4709 }
4710 
4711 ///////////////////////////////////////////////////////////////////////////////
4712 
InsertMemMap(MemMap_t * mm,off_t off,off_t size)4713 MemMapItem_t * InsertMemMap
4714 (
4715     // returns a pointer to a new item (never NULL)
4716 
4717     MemMap_t		* mm,		// mem map pointer
4718     off_t		off,		// offset of area
4719     off_t		size		// size of area
4720 )
4721 {
4722     const uint idx = InsertMemMapIndex(mm,off,size);
4723     // a C sequence point is important here
4724     return mm->field[idx];
4725 }
4726 
4727 ///////////////////////////////////////////////////////////////////////////////
4728 
TieMemMap(MemMap_t * mm,uint idx,bool force)4729 static bool TieMemMap
4730 (
4731     // returns true if element are tied togehther
4732 
4733     MemMap_t		* mm,		// mem map pointer
4734     uint		idx,		// tie element 'idx' and 'idx+1'
4735     bool		force		// always tie and not only if overlapped
4736 )
4737 {
4738     DASSERT(mm);
4739     DASSERT( idx+1 < mm->used );
4740 
4741     MemMapItem_t * i1 = mm->field[idx];
4742     MemMapItem_t * i2 = mm->field[idx+1];
4743     if ( force || i1->off + i1->size >= i2->off )
4744     {
4745 	const off_t new_size = i2->off + i2->size - i1->off;
4746 	if ( i1->size < new_size )
4747 	     i1->size = new_size;
4748 	FREE(i2);
4749 	idx++;
4750 	mm->used--;
4751 	memmove( mm->field + idx,
4752 		 mm->field + idx + 1,
4753 		 ( mm->used - idx ) * sizeof(MemMapItem_t*) );
4754 
4755 	return true;
4756     }
4757     return false;
4758 }
4759 
4760 ///////////////////////////////////////////////////////////////////////////////
4761 
InsertMemMapTie(MemMap_t * mm,off_t off,off_t size)4762 MemMapItem_t * InsertMemMapTie
4763 (
4764     // returns a pointer to a new or existing item (never NULL)
4765 
4766     MemMap_t		* mm,		// mem map pointer
4767     off_t		off,		// offset of area
4768     off_t		size		// size of area
4769 )
4770 {
4771     uint idx = InsertMemMapIndex(mm,off,size);
4772 
4773     if ( idx > 0 && TieMemMap(mm,idx-1,false) )
4774 	idx--;
4775 
4776     while ( idx + 1 < mm->used && TieMemMap(mm,idx,false) )
4777 	;
4778 
4779     return mm->field[idx];
4780 }
4781 
4782 ///////////////////////////////////////////////////////////////////////////////
4783 
InsertMemMapWrapper(void * param,u64 offset,u64 size,ccp info)4784 void InsertMemMapWrapper
4785 (
4786 	void		* param,	// user defined parameter
4787 	u64		offset,		// offset of object
4788 	u64		size,		// size of object
4789 	ccp		info		// info about object
4790 )
4791 {
4792     noTRACE("InsertMemMapWrapper(%p,%llx,%llx,%s)\n",param,offset,size,info);
4793     DASSERT(param);
4794     MemMapItem_t * mi = InsertMemMap(param,offset,size);
4795     StringCopyS(mi->info,sizeof(mi->info),info);
4796 }
4797 
4798 ///////////////////////////////////////////////////////////////////////////////
4799 
InsertDiscMemMap(MemMap_t * mm,wd_disc_t * disc)4800 void InsertDiscMemMap
4801 (
4802 	MemMap_t	* mm,		// valid memore map pointer
4803 	wd_disc_t	* disc		// valid disc pointer
4804 )
4805 {
4806     noTRACE("InsertDiscMemMap(%p,%p)\n",mm,disc);
4807     DASSERT(mm);
4808     DASSERT(disc);
4809     wd_print_mem(disc,InsertMemMapWrapper,mm);
4810 }
4811 
4812 ///////////////////////////////////////////////////////////////////////////////
4813 
FindMemMapHelper(MemMap_t * mm,off_t off,off_t size)4814 uint FindMemMapHelper ( MemMap_t * mm, off_t off, off_t size )
4815 {
4816     DASSERT(mm);
4817 
4818     int beg = 0;
4819     int end = mm->used - 1;
4820     while ( beg <= end )
4821     {
4822 	uint idx = (beg+end)/2;
4823 	MemMapItem_t * mi = mm->field[idx];
4824 	if ( off < mi->off )
4825 	    end = idx - 1 ;
4826 	else if ( off > mi->off )
4827 	    beg = idx + 1;
4828 	else if ( size < mi->size )
4829 	    end = idx - 1 ;
4830 	else if ( size > mi->size )
4831 	    beg = idx + 1;
4832 	else
4833 	{
4834 	    TRACE("FindMemMapHelper(%llx,%llx) FOUND=%d/%d/%d\n",
4835 		    (u64)off, (u64)size, idx, mm->used, mm->size );
4836 	    return idx;
4837 	}
4838     }
4839 
4840     TRACE("FindStringFieldHelper(%llx,%llx) failed=%d/%d/%d\n",
4841 		(u64)off, (u64)size, beg, mm->used, mm->size );
4842     return beg;
4843 }
4844 
4845 ///////////////////////////////////////////////////////////////////////////////
4846 
CalCoverlapMemMap(MemMap_t * mm)4847 uint CalCoverlapMemMap ( MemMap_t * mm )
4848 {
4849     DASSERT(mm);
4850 
4851     uint i, count = 0;
4852     MemMapItem_t * prev = 0;
4853     for ( i = 0; i < mm->used; i++ )
4854     {
4855 	MemMapItem_t * ptr = mm->field[i];
4856 	ptr->overlap = 0;
4857 	if ( prev && ptr->off < prev->off + prev->size )
4858 	{
4859 	    ptr ->overlap |= 1;
4860 	    prev->overlap |= 2;
4861 	    count++;
4862 	}
4863 	prev = ptr;
4864     }
4865     return count;
4866 }
4867 
4868 ///////////////////////////////////////////////////////////////////////////////
4869 
PrintMemMap(MemMap_t * mm,FILE * f,int indent,ccp info_head)4870 void PrintMemMap ( MemMap_t * mm, FILE * f, int indent, ccp info_head )
4871 {
4872     DASSERT(mm);
4873     if ( !f || !mm->used )
4874 	return;
4875 
4876     CalCoverlapMemMap(mm);
4877     indent = NormalizeIndent(indent);
4878 
4879     static char ovl[][3] = { "  ", "!.", ".!", "!!" };
4880 
4881     if (!info_head)
4882 	info_head = "info";
4883     int i, max_ilen = strlen(info_head);
4884     for ( i = 0; i < mm->used; i++ )
4885     {
4886 	MemMapItem_t * ptr = mm->field[i];
4887 	ptr->info[sizeof(ptr->info)-1] = 0;
4888 	const int ilen = strlen(ptr->info);
4889 	if ( max_ilen < ilen )
4890 	    max_ilen = ilen;
4891     }
4892 
4893     fprintf(f,"%*s      unused :  off(beg) ..  off(end) :      size : %s\n%*s%.*s\n",
4894 	    indent, "", info_head,
4895 	    indent, "", max_ilen+52, wd_sep_200 );
4896 
4897     off_t max_end = mm->begin;
4898     for ( i = 0; i < mm->used; i++ )
4899     {
4900 	MemMapItem_t * ptr = mm->field[i];
4901 	if ( !i && max_end > ptr->off )
4902 	    max_end = ptr->off;
4903 	const off_t end = ptr->off + ptr->size;
4904 	if ( ptr->off > max_end )
4905 	    fprintf(f,"%*s%s%10llx :%10llx ..%10llx :%10llx : %s\n",
4906 		indent, "", ovl[ptr->overlap&3], (u64)( ptr->off - max_end ),
4907 		(u64)ptr->off, (u64)end, (u64)ptr->size, ptr->info );
4908 	else
4909 	    fprintf(f,"%*s%s           :%10llx ..%10llx :%10llx : %s\n",
4910 		indent, "", ovl[ptr->overlap&3],
4911 		(u64)ptr->off, (u64)end, (u64)ptr->size, ptr->info );
4912 	if ( max_end < end )
4913 	    max_end = end;
4914     }
4915 }
4916 
4917 //
4918 ///////////////////////////////////////////////////////////////////////////////
4919 ///////////////			   File Map			///////////////
4920 ///////////////////////////////////////////////////////////////////////////////
4921 
InitializeFileMap(FileMap_t * mm)4922 void InitializeFileMap ( FileMap_t * mm )
4923 {
4924     DASSERT(mm);
4925     memset(mm,0,sizeof(*mm));
4926 }
4927 
4928 ///////////////////////////////////////////////////////////////////////////////
4929 
ResetFileMap(FileMap_t * mm)4930 void ResetFileMap ( FileMap_t * mm )
4931 {
4932     DASSERT(mm);
4933     FREE(mm->field);
4934     memset(mm,0,sizeof(*mm));
4935 }
4936 
4937 ///////////////////////////////////////////////////////////////////////////////
4938 
AppendFileMap(FileMap_t * fm,u64 src_off,u64 dest_off,u64 size)4939 const FileMapItem_t * AppendFileMap
4940 (
4941     // returns the modified or appended item
4942 
4943     FileMap_t	* fm,		// file map pointer
4944     u64		src_off,	// offset of source
4945     u64		dest_off,	// offset of dest
4946     u64		size		// size
4947 )
4948 {
4949     DASSERT(fm);
4950     if (!size)
4951 	return 0;
4952 
4953     if (fm->used)
4954     {
4955 	FileMapItem_t *mi = fm->field + fm->used - 1;
4956 	if (   mi->src_off  + mi->size == src_off
4957 	    && mi->dest_off + mi->size == dest_off )
4958 	{
4959 	    mi->size += size;
4960 	    return mi;
4961 	}
4962     }
4963 
4964     DASSERT( fm->used <= fm->size );
4965     if ( fm->used == fm->size )
4966     {
4967 	fm->size += fm->size/2 + 50;
4968 	fm->field = REALLOC(fm->field,fm->size*sizeof(*fm->field));
4969     }
4970     DASSERT( fm->used < fm->size );
4971 
4972     FileMapItem_t *mi = fm->field + fm->used++;
4973     mi->src_off  = src_off;
4974     mi->dest_off = dest_off;
4975     mi->size     = size;
4976     return mi;
4977 }
4978 
4979 ///////////////////////////////////////////////////////////////////////////////
4980 
CombineFileMaps(FileMap_t * fm,bool init_fm,const FileMap_t * fm1,const FileMap_t * fm2)4981 uint CombineFileMaps
4982 (
4983     FileMap_t		*fm,	 // resulting filemap
4984     bool		init_fm, // true: initialize 'fm', false: reset 'fm'
4985     const FileMap_t	*fm1,	 // first source filemap
4986     const FileMap_t	*fm2	 // second source filemap
4987 )
4988 {
4989     DASSERT(fm);
4990     DASSERT(fm1);
4991     DASSERT(fm2);
4992 
4993     if (init_fm)
4994 	InitializeFileMap(fm);
4995     else
4996 	ResetFileMap(fm);
4997 
4998 
4999     const FileMapItem_t *i1 = fm1->field;
5000     const FileMapItem_t *e1 = i1 + fm1->used;
5001 
5002     const FileMapItem_t *i2 = fm2->field;
5003     const FileMapItem_t *e2 = i2 + fm2->used;
5004 
5005     u64 fm2_off1 = 0;
5006     u64 fm2_off2 = 0;
5007     u64 fm2_size = 0;
5008 
5009     while ( i1 < e1 )
5010     {
5011 	u64 fm1_off1 = i1->src_off;
5012 	u64 fm1_off2 = i1->dest_off;
5013 	u64 fm1_size = i1->size;
5014 	i1++;
5015 	noPRINT("%4zu %9llx %11llx %9llx | %4zu %9llx %11llx %9llx | NEXT FM-1\n",
5016 		i1-fm1->field, fm1_off1, fm1_off2, fm1_size,
5017 		i2-fm2->field, fm2_off1, fm2_off2, fm2_size );
5018 
5019 	while ( fm1_size )
5020 	{
5021 	    while ( fm1_off2 >= fm2_off1 + fm2_size )
5022 	    {
5023 		if ( i2 == e2 )
5024 		    return fm->used;
5025 
5026 		fm2_off1 = i2->src_off;
5027 		fm2_off2 = i2->dest_off;
5028 		fm2_size = i2->size;
5029 		i2++;
5030 		noPRINT("%4zu %9llx %11llx %9llx | %4zu %9llx %11llx %9llx | NEXT FM-2\n",
5031 			i1-fm1->field, fm1_off1, fm1_off2, fm1_size,
5032 			i2-fm2->field, fm2_off1, fm2_off2, fm2_size );
5033 	    }
5034 	    DASSERT( fm1_off2 < fm2_off1 + fm2_size );
5035 	    noPRINT("%9llx + %9llx = %9llx < %9llx\n",
5036 		fm1_off2, fm1_size, fm1_off2 + fm1_size, fm2_off1 );
5037 	    if ( fm1_off2 + fm1_size < fm2_off1 )
5038 		break;
5039 
5040 	    if ( fm1_off2 < fm2_off1 )
5041 	    {
5042 		const u64 delta = fm2_off1 - fm1_off2;
5043 		DASSERT( delta <= fm1_size );
5044 		fm1_off1 += delta;
5045 		fm1_off2 += delta;
5046 		fm1_size -= delta;
5047 	    }
5048 	    else
5049 	    {
5050 		const u64 delta = fm1_off2 - fm2_off1;
5051 		DASSERT( delta <= fm2_size );
5052 		fm2_off1 += delta;
5053 		fm2_off2 += delta;
5054 		fm2_size -= delta;
5055 	    }
5056 	    DASSERT( fm1_off2 == fm2_off1 );
5057 
5058 	    const u64 size = fm1_size < fm2_size ? fm1_size : fm2_size;
5059 	    AppendFileMap(fm,fm1_off1,fm2_off2,size);
5060 	    fm1_off1 += size;
5061 	    fm1_off2 += size;
5062 	    fm1_size -= size;
5063 	    fm2_off1 += size;
5064 	    fm2_off2 += size;
5065 	    fm2_size -= size;
5066 	    noPRINT("%4zu %9llx %11llx %9llx | %4zu %9llx %11llx %9llx | ADD %llx\n",
5067 		i1-fm1->field, fm1_off1, fm1_off2, fm1_size,
5068 		i2-fm2->field, fm2_off1, fm2_off2, fm2_size,
5069 		size );
5070 	}
5071     }
5072     return fm->used;
5073 }
5074 
5075 //
5076 ///////////////////////////////////////////////////////////////////////////////
5077 ///////////////			setup files			///////////////
5078 ///////////////////////////////////////////////////////////////////////////////
5079 
ResetSetup(SetupDef_t * list)5080 size_t ResetSetup
5081 (
5082 	SetupDef_t * list	// object list terminated with an element 'name=NULL'
5083 )
5084 {
5085     DASSERT(list);
5086     size_t count;
5087     for ( count = 0; list->name; list++, count++ )
5088     {
5089 	FreeString(list->param);
5090 	list->param = 0;
5091 	list->value = 0;
5092     }
5093     return count;
5094 }
5095 
5096 ///////////////////////////////////////////////////////////////////////////////
5097 
ScanSetupFile(SetupDef_t * list,ccp path1,ccp path2,bool silent)5098 enumError ScanSetupFile
5099 (
5100 	SetupDef_t * list,	// object list terminated with an element 'name=NULL'
5101 	ccp path1,		// filename of text file, part 1
5102 	ccp path2,		// filename of text file, part 2
5103 	bool silent		// true: suppress error message if file not found
5104 )
5105 {
5106     DASSERT(list);
5107     DASSERT(path1||path2);
5108 
5109     ResetSetup(list);
5110 
5111     char pathbuf[PATH_MAX];
5112     ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2);
5113     TRACE("ScanSetupFile(%s,%d)\n",path,silent);
5114 
5115     FILE * f = fopen(path,"rb");
5116     if (!f)
5117     {
5118 	if (!silent)
5119 	    ERROR1(ERR_CANT_OPEN,"Can't open file: %s\n",path);
5120 	return ERR_CANT_OPEN;
5121     }
5122 
5123     while (fgets(iobuf,sizeof(iobuf)-1,f))
5124     {
5125 	//----- skip spaces
5126 
5127 	char * ptr = iobuf;
5128 	while ( *ptr > 0 && *ptr <= ' ' )
5129 	    ptr++;
5130 
5131 	if ( *ptr == '!' || *ptr == '#' )
5132 	    continue;
5133 
5134 	//----- find end of name
5135 
5136 	char * name = ptr;
5137 	while ( isalnum((int)*ptr) || *ptr == '-' )
5138 	    ptr++;
5139 	if (!*ptr)
5140 	    continue;
5141 
5142 	char * name_end = ptr;
5143 
5144 	//----- skip spaces and check for '='
5145 
5146 	while ( *ptr > 0 && *ptr <= ' ' )
5147 	    ptr++;
5148 
5149 	if ( *ptr != '=' )
5150 	    continue;
5151 
5152 	*name_end = 0;
5153 
5154 	//----- check if name is a known parameter
5155 
5156 	SetupDef_t * item;
5157 	for ( item = list; item->name; item++ )
5158 	    if (!strcmp(item->name,name))
5159 		break;
5160 	if (!item->name)
5161 	    continue;
5162 
5163 	//----- trim parameter
5164 
5165 	ptr++; // skip '='
5166 	while ( *ptr > 0 && *ptr <= ' ' )
5167 	    ptr++;
5168 
5169 	char * param = ptr;
5170 
5171 	while (*ptr)
5172 	    ptr++;
5173 
5174 	ptr--;
5175 	while ( *ptr > 0 && *ptr <= ' ' )
5176 	    ptr--;
5177 	ptr[1] = 0;
5178 
5179 	item->param = STRDUP(param);
5180 	if (item->factor)
5181 	{
5182 	    ScanSizeU64(&item->value,param,1,1,0);
5183 	    if ( item->factor > 1 )
5184 		item->value = item->value / item->factor * item->factor;
5185 	}
5186     }
5187     fclose(f);
5188     return ERR_OK;
5189 }
5190 
5191 //
5192 ///////////////////////////////////////////////////////////////////////////////
5193 ///////////////			data area & list		///////////////
5194 ///////////////////////////////////////////////////////////////////////////////
5195 
SetupDataList(DataList_t * dl,const DataArea_t * da)5196 void SetupDataList
5197 (
5198     DataList_t		* dl,		// Object for setup
5199     const DataArea_t	* da		// Source list,
5200 					//  terminated with an element where addr==NULL
5201 					// The content of this area must not changed
5202 					//  while accessing the data list
5203 )
5204 {
5205     DASSERT(dl);
5206     memset(dl,0,sizeof(*dl));
5207     dl->area = da;
5208 }
5209 
5210 ///////////////////////////////////////////////////////////////////////////////
5211 
ReadDataList(DataList_t * dl,void * buf,size_t size)5212 size_t ReadDataList // returns number of writen bytes
5213 (
5214     DataList_t		* dl,		// NULL or pointer to data list
5215     void		* buf,		// destination buffer
5216     size_t		size		// size of destination buffer
5217 )
5218 {
5219     u8 * dest = buf;
5220     size_t written = 0;
5221     if ( dl && dl->area )
5222     {
5223 	while ( size > 0 )
5224 	{
5225 	    if (!dl->current.size)
5226 	    {
5227 		noPRINT("NEXT AREA: %p, %p, %zu\n",
5228 			dl->area, dl->area->data, dl->area->size );
5229 		if (!dl->area->data)
5230 		    break;
5231 		memcpy(&dl->current,dl->area++,sizeof(dl->current));
5232 	    }
5233 
5234 	    const size_t copy_size = size < dl->current.size ? size : dl->current.size;
5235 	    noPRINT("COPY AREA: %p <- %p, size = %zu=%zx\n",
5236 			dest,dl->current.data,copy_size,copy_size);
5237 	    memcpy(dest,dl->current.data,copy_size);
5238 	    written		+= copy_size;
5239 	    dest		+= copy_size;
5240 	    dl->current.data	+= copy_size;
5241 	    dl->current.size	-= copy_size;
5242 	    size		-= copy_size;
5243 	}
5244     }
5245     return written;
5246 }
5247 
5248 //
5249 ///////////////////////////////////////////////////////////////////////////////
5250 ///////////////			random mumbers			///////////////
5251 ///////////////////////////////////////////////////////////////////////////////
5252 // thanx to Donald Knuth
5253 
5254 const u32 RANDOM32_C_ADD = 2 * 197731421; // 2 Primzahlen
5255 const u32 RANDOM32_COUNT_BASE = 4294967; // Primzahl == ~UINT_MAX32/1000;
5256 
5257 static int random32_a_index = -1;	// Index in die a-Tabelle
5258 static u32 random32_count = 1;		// Abwaerts-Zähler bis zum Wechsel von a,c
5259 static u32 random32_a,
5260 	   random32_c,
5261 	   random32_X;			// Die letzten Werte
5262 
5263 static u32 random32_a_tab[] =		// Init-Tabelle
5264 {
5265     0xbb40e62d, 0x3dc8f2f5, 0xdc024635, 0x7a5b6c8d,
5266     0x583feb95, 0x91e06dbd, 0xa7ec03f5, 0
5267 };
5268 
5269 //-----------------------------------------------------------------------------
5270 
Random32(u32 max)5271 u32 Random32 ( u32 max )
5272 {
5273     if (!--random32_count)
5274     {
5275 	// Neue Berechnung von random32_a und random32_c faellig
5276 
5277 	if ( random32_a_index < 0 )
5278 	{
5279 	    // allererste Initialisierung auf Zeitbasis
5280 	    Seed32Time();
5281 	}
5282 	else
5283 	{
5284 	    random32_c += RANDOM32_C_ADD;
5285 	    random32_a = random32_a_tab[++random32_a_index];
5286 	    if (!random32_a)
5287 	    {
5288 		random32_a_index = 0;
5289 		random32_a = random32_a_tab[0];
5290 	    }
5291 
5292 	    random32_count = RANDOM32_COUNT_BASE;
5293 	}
5294     }
5295 
5296     // Jetzt erfolgt die eigentliche Berechnung
5297 
5298     random32_X = random32_a * random32_X + random32_c;
5299 
5300     if (!max)
5301 	return random32_X;
5302 
5303     return ( (u64)max * random32_X ) >> 32;
5304 }
5305 
5306 //-----------------------------------------------------------------------------
5307 
Seed32Time()5308 u64 Seed32Time ()
5309 {
5310     struct timeval tval;
5311     gettimeofday(&tval,NULL);
5312     const u64 random_time_bits = (u64) tval.tv_usec << 16 ^ tval.tv_sec;
5313     return Seed32( ( random_time_bits ^ getpid() ) * 197731421u );
5314 }
5315 
5316 //-----------------------------------------------------------------------------
5317 
Seed32(u64 base)5318 u64 Seed32 ( u64 base )
5319 {
5320     uint a_tab_len = 0;
5321     while (random32_a_tab[a_tab_len])
5322 	a_tab_len++;
5323     const u32 base32 = base / a_tab_len;
5324 
5325     random32_a_index	= base % a_tab_len;
5326     random32_a		= random32_a_tab[random32_a_index];
5327     random32_c		= ( base32 & 15 ) * RANDOM32_C_ADD + 1;
5328     random32_X		= base32 ^ ( base >> 32 );
5329     random32_count	= RANDOM32_COUNT_BASE;
5330 
5331     return base;
5332 }
5333 
5334 //-----------------------------------------------------------------------------
5335 
RandomFill(void * buf,size_t size)5336 void RandomFill ( void * buf, size_t size )
5337 {
5338     size_t xsize = size / sizeof(u32);
5339     if (xsize)
5340     {
5341 	size -= xsize * sizeof(u32);
5342 	u32 * ptr = buf;
5343 	while ( xsize-- > 0 )
5344 	    *ptr++ = Random32(0);
5345 	buf = ptr;
5346     }
5347 
5348     u8 * ptr = buf;
5349     while ( size-- > 0 )
5350 	*ptr++ = Random32(0);
5351 }
5352 
5353 //
5354 ///////////////////////////////////////////////////////////////////////////////
5355 ///////////////			    bit handling		///////////////
5356 ///////////////////////////////////////////////////////////////////////////////
5357 
5358 const uchar TableBitCount[0x100] =
5359 {
5360 	0,1,1,2, 1,2,2,3, 1,2,2,3, 2,3,3,4,
5361 	1,2,2,3, 2,3,3,4, 2,3,3,4, 3,4,4,5,
5362 	1,2,2,3, 2,3,3,4, 2,3,3,4, 3,4,4,5,
5363 	2,3,3,4, 3,4,4,5, 3,4,4,5, 4,5,5,6,
5364 
5365 	1,2,2,3, 2,3,3,4, 2,3,3,4, 3,4,4,5,
5366 	2,3,3,4, 3,4,4,5, 3,4,4,5, 4,5,5,6,
5367 	2,3,3,4, 3,4,4,5, 3,4,4,5, 4,5,5,6,
5368 	3,4,4,5, 4,5,5,6, 4,5,5,6, 5,6,6,7,
5369 
5370 	1,2,2,3, 2,3,3,4, 2,3,3,4, 3,4,4,5,
5371 	2,3,3,4, 3,4,4,5, 3,4,4,5, 4,5,5,6,
5372 	2,3,3,4, 3,4,4,5, 3,4,4,5, 4,5,5,6,
5373 	3,4,4,5, 4,5,5,6, 4,5,5,6, 5,6,6,7,
5374 
5375 	2,3,3,4, 3,4,4,5, 3,4,4,5, 4,5,5,6,
5376 	3,4,4,5, 4,5,5,6, 4,5,5,6, 5,6,6,7,
5377 	3,4,4,5, 4,5,5,6, 4,5,5,6, 5,6,6,7,
5378 	4,5,5,6, 5,6,6,7, 5,6,6,7, 6,7,7,8
5379 };
5380 
5381 ///////////////////////////////////////////////////////////////////////////////
5382 
Count1Bits(const void * data,size_t len)5383 uint Count1Bits ( const void * data, size_t len )
5384 {
5385     uint count = 0;
5386     const uchar * d = data;
5387     while ( len-- > 0 )
5388 	count += TableBitCount[*d++];
5389     return count;
5390 }
5391 
5392 ///////////////////////////////////////////////////////////////////////////////
5393 
Count1Bits8(u8 data)5394 uint Count1Bits8 ( u8 data )
5395 {
5396     return TableBitCount[data];
5397 }
5398 
5399 ///////////////////////////////////////////////////////////////////////////////
5400 
Count1Bits16(u16 data)5401 uint Count1Bits16 ( u16 data )
5402 {
5403     const u8 * d = (u8*)&data;
5404     return TableBitCount[d[0]]
5405 	 + TableBitCount[d[1]];
5406 }
5407 
5408 ///////////////////////////////////////////////////////////////////////////////
5409 
Count1Bits32(u32 data)5410 uint Count1Bits32 ( u32 data )
5411 {
5412     const u8 * d = (u8*)&data;
5413     return TableBitCount[d[0]]
5414 	 + TableBitCount[d[1]]
5415 	 + TableBitCount[d[2]]
5416 	 + TableBitCount[d[3]];
5417 }
5418 
5419 ///////////////////////////////////////////////////////////////////////////////
5420 
Count1Bits64(u64 data)5421 uint Count1Bits64 ( u64 data )
5422 {
5423     const u8 * d = (u8*)&data;
5424     return TableBitCount[d[0]]
5425 	 + TableBitCount[d[1]]
5426 	 + TableBitCount[d[2]]
5427 	 + TableBitCount[d[3]]
5428 	 + TableBitCount[d[4]]
5429 	 + TableBitCount[d[5]]
5430 	 + TableBitCount[d[6]]
5431 	 + TableBitCount[d[7]];
5432 }
5433 
5434 ///////////////////////////////////////////////////////////////////////////////
5435 
FindLowest1Bit64(u64 data)5436 int FindLowest1Bit64 ( u64 data )
5437 {
5438     if (!data)
5439 	return -1;
5440 
5441     uint index;
5442     for ( index = 0; !(data&1); data >>= 1, index++ )
5443 	;
5444     return index;
5445 }
5446 
5447 ///////////////////////////////////////////////////////////////////////////////
5448 
GetAlign64(u64 data)5449 u64 GetAlign64 ( u64 data )
5450 {
5451     const int index = FindLowest1Bit64 (data);
5452     return index < 0 ? 0 : 1 << index;
5453 }
5454 
5455 //
5456 ///////////////////////////////////////////////////////////////////////////////
5457 ///////////////			    etc				///////////////
5458 ///////////////////////////////////////////////////////////////////////////////
5459 
AllocTempBuffer(size_t needed_size)5460 size_t AllocTempBuffer ( size_t needed_size )
5461 {
5462     // 'tempbuf' is only for short usage
5463     //     ==> don't call other functions while using tempbuf
5464 
5465     // align to 4K
5466     needed_size = needed_size + 0xfff & ~(size_t)0xfff;
5467 
5468     if ( tempbuf_size < needed_size )
5469     {
5470 	noPRINT("$$$ ALLOC TEMPBUF, SIZE: %zx > %zx (%s -> %s)\n",
5471 		tempbuf_size, needed_size,
5472 		wd_print_size_1024(0,0,tempbuf_size,false),
5473 		wd_print_size_1024(0,0,needed_size,false) );
5474 	tempbuf_size = needed_size;
5475 	FREE(tempbuf);
5476 	tempbuf = MALLOC(needed_size);
5477     }
5478     return tempbuf_size;
5479 }
5480 
5481 ///////////////////////////////////////////////////////////////////////////////
5482 
ScanPreallocMode(ccp arg)5483 int ScanPreallocMode ( ccp arg )
5484 {
5485  #ifdef NO_PREALLOC
5486     static char errmsg[] = "Preallocation not supported and option --prealloc is ignored!\n";
5487  #endif
5488 
5489     if ( !arg || !*arg )
5490     {
5491      #ifdef NO_PREALLOC
5492 	ERROR0(ERR_WARNING,errmsg);
5493      #else
5494 	prealloc_mode = PREALLOC_OPT_DEFAULT;
5495      #endif
5496 	return 0;
5497     }
5498 
5499     static const CommandTab_t tab[] =
5500     {
5501 	{ PREALLOC_OFF,		"OFF",		"0",	0 },
5502 	{ PREALLOC_SMART,	"SMART",	"1",	0 },
5503 	{ PREALLOC_ALL,		"ALL",		"2",	0 },
5504 
5505 	{ 0,0,0,0 }
5506     };
5507 
5508     const CommandTab_t * cmd = ScanCommand(0,arg,tab);
5509     if (cmd)
5510     {
5511      #ifdef NO_PREALLOC
5512 	if ( cmd->id != PREALLOC_OFF )
5513 	    ERROR0(ERR_WARNING,errmsg);
5514      #else
5515 	prealloc_mode = cmd->id;
5516      #endif
5517 	return 0;
5518     }
5519 
5520     ERROR0(ERR_SYNTAX,"Illegal preallocation mode (option --prealloc): '%s'\n",arg);
5521     return 1;
5522 }
5523 
5524 ///////////////////////////////////////////////////////////////////////////////
5525 
AllocRealPath(ccp source)5526 char * AllocRealPath ( ccp source )
5527 {
5528     // Mac does not support: realpath(src,0)
5529 
5530     char fname[PATH_MAX];
5531     realpath(source,fname);
5532     return STRDUP(fname);
5533 }
5534 
5535 ///////////////////////////////////////////////////////////////////////////////
5536 
mark_used(ccp name,...)5537 void mark_used ( ccp name, ... )
5538 {
5539 }
5540 
5541 ///////////////////////////////////////////////////////////////////////////////
5542 
option_deprecated(ccp name)5543 void option_deprecated ( ccp name )
5544 {
5545     ERROR0(ERR_WARNING,
5546 	"Option --%s is deprecated! Don't use it any longer!\n",
5547 	name);
5548 }
5549 
5550 ///////////////////////////////////////////////////////////////////////////////
5551 
option_ignored(ccp name)5552 void option_ignored ( ccp name )
5553 {
5554     ERROR0(ERR_WARNING,
5555 	"Option --%s is deprecated and ignored! Don't use it any longer!\n",
5556 	name);
5557 }
5558 
5559 //
5560 ///////////////////////////////////////////////////////////////////////////////
5561 ///////////////			    END				///////////////
5562 ///////////////////////////////////////////////////////////////////////////////
5563 
5564