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