1 /* GNU ddrescuelog - Tool for ddrescue mapfiles
2 Copyright (C) 2011-2020 Antonio Diaz Diaz.
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17 /*
18 Exit status: 0 for a normal exit, 1 for environmental problems
19 (file not found, invalid flags, I/O errors, etc), 2 to indicate a
20 corrupt or invalid input file, 3 for an internal consistency error
21 (eg, bug) which caused ddrescuelog to panic.
22 */
23
24 #include <algorithm>
25 #include <cerrno>
26 #include <climits>
27 #include <cstdio>
28 #include <cstdlib>
29 #include <cstring>
30 #include <ctime>
31 #include <string>
32 #include <vector>
33
34 #include "arg_parser.h"
35 #include "block.h"
36
37
38 namespace {
39
40 const char * const Program_name = "GNU ddrescuelog";
41 const char * const program_name = "ddrescuelog";
42 const char * invocation_name = program_name; // default value
43
44 enum Mode { m_none, m_and, m_annotate, m_change, m_compare, m_complete,
45 m_create, m_delete, m_done_st, m_invert, m_list, m_or,
46 m_shift, m_status, m_xor };
47
48
show_help(const int hardbs)49 void show_help( const int hardbs )
50 {
51 std::printf( "GNU ddrescuelog is a tool that manipulates ddrescue mapfiles, shows mapfile\n"
52 "contents, converts mapfiles to/from other formats, compares mapfiles, tests\n"
53 "rescue status, and can delete a mapfile if the rescue is done. Ddrescuelog\n"
54 "operations can be restricted to one or several parts of the mapfile if the\n"
55 "domain setting options are used.\n"
56 "\nUse '-' as mapfile to read the mapfile from standard input\n"
57 "(also in the options taking a mapfile argument) or to write the mapfile\n"
58 "created by '--create-mapfile' to standard output.\n"
59 "\nNOTE: In versions of ddrescue prior to 1.20 the mapfile was called\n"
60 "'logfile'. The format is the same; only the name has changed.\n"
61 "\nUsage: %s [options] mapfile\n", invocation_name );
62 std::printf( "\nOptions:\n"
63 " -h, --help display this help and exit\n"
64 " -V, --version output version information and exit\n"
65 " -a, --change-types=<ot>,<nt> change the block types of mapfile\n"
66 " -A, --annotate-mapfile add comments with human-readable pos/sizes\n"
67 " -b, --block-size=<bytes> block size in bytes [default %d]\n", hardbs );
68 std::printf( " -B, --binary-prefixes show binary multipliers in numbers [SI]\n"
69 " -c, --create-mapfile[=<tt>] create mapfile from list of blocks [+-]\n"
70 " -C, --complete-mapfile[=<t>] complete mapfile adding blocks of type t [?]\n"
71 " -d, --delete-if-done delete the mapfile if rescue is finished\n"
72 " -D, --done-status return 0 if rescue is finished\n"
73 " -f, --force overwrite existing output files\n"
74 " -i, --input-position=<bytes> starting position of rescue domain [0]\n"
75 " -l, --list-blocks=<types> print block numbers of given types (?*/-+)\n"
76 " -L, --loose-domain accept an incomplete domain mapfile\n"
77 " -m, --domain-mapfile=<file> restrict domain to finished blocks in file\n"
78 " -n, --invert-mapfile invert block types (finished <--> others)\n"
79 " -o, --output-position=<bytes> starting position in output file [ipos]\n"
80 " -p, --compare-mapfile=<file> compare block types in domain of both files\n"
81 " -P, --compare-as-domain=<file> like -p but compare finished blocks only\n"
82 " -q, --quiet suppress all messages\n"
83 " -s, --size=<bytes> maximum size of rescue domain to be processed\n"
84 " -t, --show-status show a summary of mapfile contents\n"
85 " -v, --verbose be verbose (a 2nd -v gives more)\n"
86 " -x, --xor-mapfile=<file> XOR the finished blocks in file with mapfile\n"
87 " -y, --and-mapfile=<file> AND the finished blocks in file with mapfile\n"
88 " -z, --or-mapfile=<file> OR the finished blocks in file with mapfile\n"
89 " --shift shift all block positions by (opos - ipos)\n"
90 "\nNumbers may be in decimal, hexadecimal, or octal, and may be followed by a\n"
91 "multiplier: s = sectors, k = 1000, Ki = 1024, M = 10^6, Mi = 2^20, etc...\n"
92 "\nExit status: 0 for a normal exit, 1 for environmental problems (file\n"
93 "not found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or\n"
94 "invalid input file, 3 for an internal consistency error (eg, bug) which\n"
95 "caused ddrescuelog to panic.\n"
96 "\nReport bugs to bug-ddrescue@gnu.org\n"
97 "Ddrescue home page: http://www.gnu.org/software/ddrescue/ddrescue.html\n"
98 "General help using GNU software: http://www.gnu.org/gethelp\n" );
99 }
100
101
parse_types(const std::string & arg,std::string & types1,std::string & types2)102 void parse_types( const std::string & arg,
103 std::string & types1, std::string & types2 )
104 {
105 std::string * p = &types1;
106 bool error = false, comma_found = false;
107 types1.clear();
108 types2.clear();
109
110 for( unsigned i = 0; i < arg.size(); ++i )
111 {
112 const char ch = arg[i];
113 if( ch == ',' )
114 {
115 if( comma_found ) { error = true; break; }
116 else { comma_found = true; p = &types2; continue; }
117 }
118 if( !Sblock::isstatus( ch ) ) { error = true; break; }
119 *p += ch;
120 }
121 if( types1.empty() || types2.empty() ) error = true;
122 if( error )
123 {
124 show_error( "Invalid type for option 'change-types'.", 0, true );
125 std::exit( 1 );
126 }
127 if( types1.size() > types2.size() )
128 types2.append( types1.size() - types2.size(), types2[types2.size()-1] );
129 }
130
131
parse_2types(const std::string & arg,Sblock::Status & type1,Sblock::Status & type2)132 void parse_2types( const std::string & arg,
133 Sblock::Status & type1, Sblock::Status & type2 )
134 {
135 if( arg.empty() ) return;
136 if( arg.size() != 2 || arg[0] == arg[1] ||
137 !Sblock::isstatus( arg[0] ) || !Sblock::isstatus( arg[1] ) )
138 {
139 show_error( "Invalid type for option 'create-mapfile'.", 0, true );
140 std::exit( 1 );
141 }
142 type1 = Sblock::Status( arg[0] );
143 type2 = Sblock::Status( arg[1] );
144 }
145
146
parse_type(const std::string & arg,Sblock::Status & complete_type)147 void parse_type( const std::string & arg, Sblock::Status & complete_type )
148 {
149 if( arg.empty() ) return;
150 if( arg.size() != 1 || !Sblock::isstatus( arg[0] ) )
151 {
152 show_error( "Invalid type for option 'complete-mapfile'.", 0, true );
153 std::exit( 1 );
154 }
155 complete_type = Sblock::Status( arg[0] );
156 }
157
158
do_logic_ops(Domain & domain,const char * const mapname,const char * const second_mapname,const Mode program_mode)159 int do_logic_ops( Domain & domain, const char * const mapname,
160 const char * const second_mapname, const Mode program_mode )
161 {
162 Mapfile mapfile( mapname );
163 if( !mapfile.read_mapfile() ) return not_readable( mapname );
164 mapfile.compact_sblock_vector();
165
166 Mapfile mapfile2( second_mapname );
167 if( !mapfile2.read_mapfile() ) return not_readable( second_mapname );
168 mapfile2.compact_sblock_vector();
169
170 domain.crop( mapfile.extent() );
171 domain.crop( mapfile2.extent() );
172 if( domain.empty() ) return empty_domain();
173 mapfile.split_by_domain_borders( domain );
174 mapfile2.split_by_domain_borders( domain );
175 mapfile.split_by_mapfile_borders( mapfile2 );
176 mapfile2.split_by_mapfile_borders( mapfile );
177
178 for( long i = 0, j = 0; ; ++i, ++j )
179 {
180 while( i < mapfile.sblocks() && !domain.includes( mapfile.sblock( i ) ) )
181 ++i;
182 while( j < mapfile2.sblocks() && !domain.includes( mapfile2.sblock( j ) ) )
183 ++j;
184 if( i >= mapfile.sblocks() || j >= mapfile2.sblocks() ) break;
185 const Sblock & sb1 = mapfile.sblock( i );
186 const Sblock & sb2 = mapfile2.sblock( j );
187 if( sb1.pos() != sb2.pos() || sb1.size() != sb2.size() )
188 internal_error( "blocks got out of sync." );
189 const bool f1 = ( sb1.status() == Sblock::finished );
190 const bool f2 = ( sb2.status() == Sblock::finished );
191 switch( program_mode )
192 {
193 case m_and:
194 if( f1 && !f2 ) mapfile.change_sblock_status( i, Sblock::bad_sector );
195 break;
196 case m_or:
197 if( !f1 && f2 ) mapfile.change_sblock_status( i, Sblock::finished );
198 break;
199 case m_xor:
200 if( f2 )
201 mapfile.change_sblock_status( i, f1 ? Sblock::bad_sector : Sblock::finished );
202 break;
203 default: internal_error( "invalid program_mode." );
204 }
205 }
206 mapfile.compact_sblock_vector();
207 mapfile.write_mapfile( stdout );
208 if( std::fclose( stdout ) != 0 )
209 { show_error( "Error closing stdout", errno ); return 1; }
210 return 0;
211 }
212
213
annotate_mapfile(Domain & domain,const char * const mapname)214 int annotate_mapfile( Domain & domain, const char * const mapname )
215 {
216 Mapfile mapfile( mapname );
217 if( !mapfile.read_mapfile() ) return not_readable( mapname );
218 domain.crop( mapfile.extent() );
219 if( domain.empty() ) return empty_domain();
220 mapfile.split_by_domain_borders( domain );
221 mapfile.write_mapfile( stdout, false, false, &domain );
222 if( std::fclose( stdout ) != 0 )
223 { show_error( "Error closing stdout", errno ); return 1; }
224 return 0;
225 }
226
227
change_types(Domain & domain,const char * const mapname,const std::string & types1,const std::string & types2)228 int change_types( Domain & domain, const char * const mapname,
229 const std::string & types1, const std::string & types2 )
230 {
231 Mapfile mapfile( mapname );
232 if( !mapfile.read_mapfile() ) return not_readable( mapname );
233 domain.crop( mapfile.extent() );
234 if( domain.empty() ) return empty_domain();
235 mapfile.split_by_domain_borders( domain );
236
237 for( long i = 0; i < mapfile.sblocks(); ++i )
238 {
239 const Sblock & sb = mapfile.sblock( i );
240 if( !domain.includes( sb ) )
241 { if( domain < sb ) break; else continue; }
242 const unsigned j = types1.find( sb.status() );
243 if( j < types1.size() )
244 mapfile.change_sblock_status( i, Sblock::Status( types2[j] ) );
245 }
246 mapfile.compact_sblock_vector();
247 mapfile.write_mapfile( stdout );
248 if( std::fclose( stdout ) != 0 )
249 { show_error( "Error closing stdout", errno ); return 1; }
250 return 0;
251 }
252
253
set_for_compare(Domain & domain,Mapfile & mapfile,const bool as_domain,const bool loose)254 int set_for_compare( Domain & domain, Mapfile & mapfile,
255 const bool as_domain, const bool loose )
256 {
257 if( !mapfile.read_mapfile( ( as_domain && loose ) ? '?' : 0 ) )
258 return not_readable( mapfile.filename() );
259 mapfile.compact_sblock_vector();
260 domain.crop( mapfile.extent() );
261 if( domain.empty() ) return empty_domain();
262 mapfile.split_by_domain_borders( domain );
263 return -1;
264 }
265
compare_mapfiles(Domain & domain,const char * const mapname,const char * const second_mapname,const bool as_domain,const bool loose)266 int compare_mapfiles( Domain & domain, const char * const mapname,
267 const char * const second_mapname,
268 const bool as_domain, const bool loose )
269 {
270 Domain domain2( domain );
271 Mapfile mapfile( mapname );
272 int retval = set_for_compare( domain, mapfile, as_domain, loose );
273 if( retval >= 0 ) return retval;
274
275 Mapfile mapfile2( second_mapname );
276 retval = set_for_compare( domain2, mapfile2, as_domain, loose );
277 if( retval >= 0 ) return retval;
278
279 retval = 0;
280 if( !as_domain && domain != domain2 ) retval = 1;
281 else
282 {
283 long i = 0, j = 0;
284 while( true )
285 {
286 while( i < mapfile.sblocks() &&
287 ( !domain.includes( mapfile.sblock( i ) ) ||
288 ( as_domain && mapfile.sblock( i ).status() != Sblock::finished ) ) )
289 ++i;
290 while( j < mapfile2.sblocks() &&
291 ( !domain2.includes( mapfile2.sblock( j ) ) ||
292 ( as_domain && mapfile2.sblock( j ).status() != Sblock::finished ) ) )
293 ++j;
294 if( ( i < mapfile.sblocks() ) != ( j < mapfile2.sblocks() ) )
295 { retval = 1; break; } // one file has more blocks
296 if( i >= mapfile.sblocks() ) break; // successful compare
297 if( mapfile.sblock( i++ ) != mapfile2.sblock( j++ ) )
298 { retval = 1; break; }
299 }
300 }
301 if( retval )
302 {
303 char buf[80];
304 snprintf( buf, sizeof buf, "Mapfiles '%s' and '%s' differ.",
305 mapfile.filename(), mapfile2.filename() );
306 show_error( buf );
307 }
308 return retval;
309 }
310
311
complete_mapfile(const char * const mapname,const Sblock::Status complete_type)312 int complete_mapfile( const char * const mapname,
313 const Sblock::Status complete_type )
314 {
315 Mapfile mapfile( mapname );
316 if( !mapfile.read_mapfile( complete_type ) )
317 return not_readable( mapname );
318 mapfile.compact_sblock_vector();
319 mapfile.write_mapfile( stdout );
320 if( std::fclose( stdout ) != 0 )
321 { show_error( "Error closing stdout", errno ); return 1; }
322 return 0;
323 }
324
325
create_mapfile(Domain & domain,const char * const mapname,const int hardbs,const Sblock::Status type1,const Sblock::Status type2,const bool force)326 int create_mapfile( Domain & domain, const char * const mapname,
327 const int hardbs, const Sblock::Status type1,
328 const Sblock::Status type2, const bool force )
329 {
330 if( domain.empty() ) return empty_domain();
331 char buf[80];
332 Mapfile mapfile( mapname );
333 const bool to_stdout = ( std::strcmp( mapname, "-" ) == 0 );
334 if( !to_stdout && !force && mapfile.read_mapfile( 0, false ) )
335 {
336 snprintf( buf, sizeof buf,
337 "Mapfile '%s' exists. Use '--force' to overwrite it.", mapname );
338 show_error( buf );
339 return 1;
340 }
341 mapfile.set_to_status( type2 ); // mark all mapfile as type2
342 mapfile.split_by_domain_borders( domain );
343
344 // mark every block read from stdin and in domain as type1
345 for( int linenum = 1; ; ++linenum )
346 {
347 long long block;
348 const int n = std::scanf( "%lli\n", &block );
349 if( n < 0 ) break; // EOF
350 if( n != 1 || block < 0 || block > LLONG_MAX / hardbs )
351 {
352 snprintf( buf, sizeof buf,
353 "error reading block number from stdin, line %d", linenum );
354 show_error( buf );
355 return 2;
356 }
357 const Block b( block * hardbs, hardbs );
358 if( domain.includes( b ) )
359 mapfile.change_chunk_status( b, type1, domain );
360 }
361 mapfile.truncate_vector( domain.end(), true );
362 if( !mapfile.write_mapfile( to_stdout ? stdout : 0 ) ) return 1;
363 if( to_stdout && std::fclose( stdout ) != 0 )
364 { show_error( "Error closing stdout", errno ); return 1; }
365 return 0;
366 }
367
368
test_if_done(Domain & domain,const char * const mapname,const bool del)369 int test_if_done( Domain & domain, const char * const mapname, const bool del )
370 {
371 char buf[80];
372 Mapfile mapfile( mapname );
373 if( !mapfile.read_mapfile( 0, !del ) ) return not_readable( mapname );
374 domain.crop( mapfile.extent() );
375 if( domain.empty() ) return empty_domain();
376 mapfile.split_by_domain_borders( domain );
377
378 for( long i = 0; i < mapfile.sblocks(); ++i )
379 {
380 const Sblock & sb = mapfile.sblock( i );
381 if( !domain.includes( sb ) )
382 { if( domain < sb ) break; else continue; }
383 if( sb.status() != Sblock::finished )
384 {
385 if( verbosity >= 1 )
386 {
387 snprintf( buf, sizeof buf, "Mapfile '%s' not done.", mapname );
388 show_error( buf );
389 }
390 return 1;
391 }
392 }
393 if( !del ) return 0;
394 if( std::remove( mapname ) != 0 )
395 {
396 snprintf( buf, sizeof buf, "Error deleting mapfile '%s'", mapname );
397 show_error( buf, errno );
398 return 1;
399 }
400 if( verbosity >= 1 )
401 {
402 snprintf( buf, sizeof buf, "Mapfile '%s' successfully deleted.", mapname );
403 show_error( buf );
404 }
405 return 0;
406 }
407
408
shift_blocks(const long long ipos,const long long opos,Domain & domain,const char * const mapname)409 int shift_blocks( const long long ipos, const long long opos,
410 Domain & domain, const char * const mapname )
411 {
412 if( ipos != 0 && opos != 0 )
413 { show_error( "Either '-i' or '-o' must be 0" ); return 1; }
414 const long long offset = opos - ipos;
415 Mapfile mapfile( mapname );
416 if( !mapfile.read_mapfile() ) return not_readable( mapname );
417 domain.crop( mapfile.extent() );
418 if( domain.empty() ) return empty_domain();
419 mapfile.truncate_vector( domain.end(), true );
420 mapfile.shift_blocks( offset );
421 mapfile.compact_sblock_vector();
422 mapfile.write_mapfile( stdout );
423 if( std::fclose( stdout ) != 0 )
424 { show_error( "Error closing stdout", errno ); return 1; }
425 return 0;
426 }
427
428
to_badblocks(const long long offset,Domain & domain,const char * const mapname,const int hardbs,const std::string & blocktypes)429 int to_badblocks( const long long offset, Domain & domain,
430 const char * const mapname, const int hardbs,
431 const std::string & blocktypes )
432 {
433 long long last_block = -1;
434 Mapfile mapfile( mapname );
435 if( !mapfile.read_mapfile() ) return not_readable( mapname );
436 domain.crop( mapfile.extent() );
437 if( domain.empty() ) return empty_domain();
438 mapfile.split_by_domain_borders( domain );
439
440 for( long i = 0; i < mapfile.sblocks(); ++i )
441 {
442 const Sblock & sb = mapfile.sblock( i );
443 if( !domain.includes( sb ) )
444 { if( domain < sb ) break; else continue; }
445 if( blocktypes.find( sb.status() ) >= blocktypes.size() ) continue;
446 for( long long block = ( sb.pos() + offset ) / hardbs;
447 block * hardbs < sb.end() + offset; ++block )
448 {
449 if( block > last_block )
450 {
451 last_block = block;
452 std::printf( "%lld\n", block );
453 }
454 else if( block < last_block ) internal_error( "block out of order." );
455 }
456 }
457 return 0;
458 }
459
460
do_show_status(Domain & domain,const char * const mapname,const bool loose)461 int do_show_status( Domain & domain, const char * const mapname,
462 const bool loose )
463 {
464 long long non_tried_size = 0, non_trimmed_size = 0;
465 long long non_scraped_size = 0, bad_size = 0, finished_size = 0;
466 unsigned long non_tried_areas = 0, non_trimmed_areas = 0;
467 unsigned long non_scraped_areas = 0, bad_areas = 0, finished_areas = 0;
468 Mapfile mapfile( mapname );
469 if( !mapfile.read_mapfile( loose ? '?' : 0 ) ) return not_readable( mapname );
470 mapfile.compact_sblock_vector();
471 const Block extent = mapfile.extent();
472 domain.crop( extent );
473 if( domain.empty() ) return empty_domain();
474 const long true_sblocks = mapfile.sblocks();
475 mapfile.split_by_domain_borders( domain );
476
477 for( long i = 0; i < mapfile.sblocks(); ++i )
478 {
479 const Sblock & sb = mapfile.sblock( i );
480 if( !domain.includes( sb ) )
481 { if( domain < sb ) break; else continue; }
482 switch( sb.status() )
483 {
484 case Sblock::non_tried: non_tried_size += sb.size();
485 ++non_tried_areas; break;
486 case Sblock::non_trimmed: non_trimmed_size += sb.size();
487 ++non_trimmed_areas; break;
488 case Sblock::non_scraped: non_scraped_size += sb.size();
489 ++non_scraped_areas; break;
490 case Sblock::bad_sector: bad_size += sb.size();
491 ++bad_areas; break;
492 case Sblock::finished: finished_size += sb.size();
493 ++finished_areas; break;
494 }
495 }
496
497 const long long domain_size = domain.in_size();
498 if( verbosity >= 1 ) std::printf( "\n%s", mapname );
499 std::printf( "\n current pos: %9sB, current status: %s\n",
500 format_num( mapfile.current_pos() ),
501 Mapfile::status_name( mapfile.current_status() ) );
502 std::printf( "mapfile extent: %9sB, in %6ld area(s)\n",
503 format_num( extent.size() ), true_sblocks );
504 if( domain.pos() > 0 || domain.end() < extent.end() || domain.blocks() > 1 )
505 {
506 std::printf( " domain begin: %9sB, domain end: %9sB\n",
507 format_num( domain.pos() ), format_num( domain.end() ) );
508 std::printf( " domain size: %9sB, in %6ld area(s)\n",
509 format_num( domain_size ), domain.blocks() );
510 }
511 std::printf( "\n non-tried: %9sB, in %6lu area(s) (%s)\n",
512 format_num( non_tried_size ), non_tried_areas,
513 format_percentage( non_tried_size, domain_size ) );
514 std::printf( " rescued: %9sB, in %6lu area(s) (%s)\n",
515 format_num( finished_size ), finished_areas,
516 format_percentage( finished_size, domain_size ) );
517 std::printf( " non-trimmed: %9sB, in %6lu area(s) (%s)\n",
518 format_num( non_trimmed_size ), non_trimmed_areas,
519 format_percentage( non_trimmed_size, domain_size ) );
520 std::printf( " non-scraped: %9sB, in %6lu area(s) (%s)\n",
521 format_num( non_scraped_size ), non_scraped_areas,
522 format_percentage( non_scraped_size, domain_size ) );
523 std::printf( " bad-sector: %9sB, in %6lu area(s) (%s)\n",
524 format_num( bad_size ), bad_areas,
525 format_percentage( bad_size, domain_size ) );
526 return 0;
527 }
528
529 } // end namespace
530
531
532 #include "main_common.cc"
533
534
main(const int argc,const char * const argv[])535 int main( const int argc, const char * const argv[] )
536 {
537 long long ipos = 0;
538 long long opos = -1;
539 long long max_size = -1;
540 const char * domain_mapfile_name = 0;
541 const char * second_mapname = 0;
542 const int default_hardbs = 512;
543 int hardbs = default_hardbs;
544 Mode program_mode = m_none;
545 bool as_domain = false;
546 bool force = false;
547 bool loose = false;
548 std::string types1, types2;
549 Sblock::Status type1 = Sblock::finished, type2 = Sblock::bad_sector;
550 Sblock::Status complete_type = Sblock::non_tried;
551 if( argc > 0 ) invocation_name = argv[0];
552 command_line = invocation_name;
553 for( int i = 1; i < argc; ++i )
554 { command_line += ' '; command_line += argv[i]; }
555
556 enum Optcode { opt_shi = 256 };
557 const Arg_parser::Option options[] =
558 {
559 { 'a', "change-types", Arg_parser::yes },
560 { 'A', "annotate-mapfile", Arg_parser::no },
561 { 'b', "block-size", Arg_parser::yes },
562 { 'b', "sector-size", Arg_parser::yes },
563 { 'B', "binary-prefixes", Arg_parser::no },
564 { 'c', "create-mapfile", Arg_parser::maybe },
565 { 'c', "create-logfile", Arg_parser::maybe },
566 { 'C', "complete-mapfile", Arg_parser::maybe },
567 { 'C', "complete-logfile", Arg_parser::maybe },
568 { 'd', "delete-if-done", Arg_parser::no },
569 { 'D', "done-status", Arg_parser::no },
570 { 'f', "force", Arg_parser::no },
571 { 'h', "help", Arg_parser::no },
572 { 'i', "input-position", Arg_parser::yes },
573 { 'l', "list-blocks", Arg_parser::yes },
574 { 'L', "loose-domain", Arg_parser::no },
575 { 'm', "domain-mapfile", Arg_parser::yes },
576 { 'm', "domain-logfile", Arg_parser::yes },
577 { 'n', "invert-mapfile", Arg_parser::no },
578 { 'n', "invert-logfile", Arg_parser::no },
579 { 'o', "output-position", Arg_parser::yes },
580 { 'p', "compare-mapfile", Arg_parser::yes },
581 { 'p', "compare-logfile", Arg_parser::yes },
582 { 'P', "compare-as-domain", Arg_parser::yes },
583 { 'q', "quiet", Arg_parser::no },
584 { 's', "size", Arg_parser::yes },
585 { 's', "max-size", Arg_parser::yes },
586 { 't', "show-status", Arg_parser::no },
587 { 'v', "verbose", Arg_parser::no },
588 { 'V', "version", Arg_parser::no },
589 { 'x', "xor-mapfile", Arg_parser::yes },
590 { 'x', "xor-logfile", Arg_parser::yes },
591 { 'y', "and-mapfile", Arg_parser::yes },
592 { 'y', "and-logfile", Arg_parser::yes },
593 { 'z', "or-mapfile", Arg_parser::yes },
594 { 'z', "or-logfile", Arg_parser::yes },
595 { opt_shi, "shift", Arg_parser::no },
596 { 0 , 0, Arg_parser::no } };
597
598 const Arg_parser parser( argc, argv, options );
599 if( parser.error().size() ) // bad option
600 { show_error( parser.error().c_str(), 0, true ); return 1; }
601
602 int argind = 0;
603 for( ; argind < parser.arguments(); ++argind )
604 {
605 const int code = parser.code( argind );
606 if( !code ) break; // no more options
607 const std::string & arg = parser.argument( argind );
608 const char * const ptr = arg.c_str();
609 switch( code )
610 {
611 case 'a': set_mode( program_mode, m_change );
612 parse_types( arg, types1, types2 ); break;
613 case 'A': set_mode( program_mode, m_annotate ); break;
614 case 'b': hardbs = getnum( ptr, 0, 1, INT_MAX ); break;
615 case 'B': format_num( 0, 0, -1 ); break; // set binary prefixes
616 case 'c': set_mode( program_mode, m_create );
617 parse_2types( arg, type1, type2 ); break;
618 case 'C': set_mode( program_mode, m_complete );
619 parse_type( arg, complete_type ); break;
620 case 'd': set_mode( program_mode, m_delete ); break;
621 case 'D': set_mode( program_mode, m_done_st ); break;
622 case 'f': force = true; break;
623 case 'h': show_help( default_hardbs ); return 0;
624 case 'i': ipos = getnum( ptr, hardbs, 0 ); break;
625 case 'l': set_mode( program_mode, m_list ); types1 = arg;
626 check_types( types1, "list-blocks" ); break;
627 case 'L': loose = true; break;
628 case 'm': set_name( &domain_mapfile_name, ptr, code ); break;
629 case 'n': set_mode( program_mode, m_invert ); break;
630 case 'o': opos = getnum( ptr, hardbs, 0 ); break;
631 case 'p':
632 case 'P': set_mode( program_mode, m_compare );
633 second_mapname = ptr; as_domain = ( code == 'P' ); break;
634 case 'q': verbosity = -1; break;
635 case 's': max_size = getnum( ptr, hardbs, -1 ); break;
636 case 't': set_mode( program_mode, m_status ); break;
637 case 'v': if( verbosity < 4 ) ++verbosity; break;
638 case 'V': show_version(); return 0;
639 case 'x': set_mode( program_mode, m_xor );
640 second_mapname = ptr; break;
641 case 'y': set_mode( program_mode, m_and );
642 second_mapname = ptr; break;
643 case 'z': set_mode( program_mode, m_or );
644 second_mapname = ptr; break;
645 case opt_shi: set_mode( program_mode, m_shift ); break;
646 default : internal_error( "uncaught option." );
647 }
648 } // end process options
649
650 if( program_mode == m_none )
651 {
652 show_error( "You must specify the operation to be performed.", 0, true );
653 return 1;
654 }
655
656 if( opos < 0 ) opos = ipos;
657
658 if( program_mode == m_status )
659 {
660 if( argind >= parser.arguments() )
661 { show_error( "At least one mapfile must be specified.", 0, true );
662 return 1; }
663 }
664 else if( argind + 1 != parser.arguments() )
665 {
666 if( argind < parser.arguments() )
667 show_error( "Too many files.", 0, true );
668 else
669 show_error( "A mapfile must be specified.", 0, true );
670 return 1;
671 }
672
673 int retval = 0;
674 for( ; argind < parser.arguments(); ++argind )
675 {
676 const char * const mapname = parser.argument( argind ).c_str();
677 Domain domain( ipos, max_size, domain_mapfile_name, loose );
678
679 switch( program_mode )
680 {
681 case m_none: internal_error( "invalid operation." ); break;
682 case m_and:
683 case m_or:
684 case m_xor:
685 return do_logic_ops( domain, mapname, second_mapname, program_mode );
686 case m_annotate: return annotate_mapfile( domain, mapname );
687 case m_change: return change_types( domain, mapname, types1, types2 );
688 case m_compare: return compare_mapfiles( domain, mapname, second_mapname,
689 as_domain, loose );
690 case m_complete: return complete_mapfile( mapname, complete_type );
691 case m_create: return create_mapfile( domain, mapname, hardbs,
692 type1, type2, force );
693 case m_delete: return test_if_done( domain, mapname, true );
694 case m_done_st: return test_if_done( domain, mapname, false );
695 case m_invert: return change_types( domain, mapname, "?*/-+", "++++-" );
696 case m_list:
697 return to_badblocks( opos - ipos, domain, mapname, hardbs, types1 );
698 case m_shift:
699 return shift_blocks( ipos, opos, domain, mapname );
700 case m_status:
701 retval = std::max( retval, do_show_status( domain, mapname, loose ) );
702 }
703 }
704 return retval;
705 }
706