1 /* GNU ddrescue - Data recovery tool
2 Copyright (C) 2004-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 int verbosity = 0;
19
20 namespace {
21
22 const char * const program_year = "2020";
23 std::string command_line;
24
25
show_version()26 void show_version()
27 {
28 std::printf( "GNU %s %s\n", program_name, PROGVERSION );
29 std::printf( "Copyright (C) %s Antonio Diaz Diaz.\n", program_year );
30 std::printf( "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n"
31 "This is free software: you are free to change and redistribute it.\n"
32 "There is NO WARRANTY, to the extent permitted by law.\n" );
33 }
34
35
36 // Recognized formats: <num>[YZEPTGM][i][Bs], <num>k[Bs], <num>Ki[Bs]
37 //
getnum(const char * const ptr,const int hardbs,const long long llimit=-LLONG_MAX,const long long ulimit=LLONG_MAX,const char ** const tailp=0)38 long long getnum( const char * const ptr, const int hardbs,
39 const long long llimit = -LLONG_MAX,
40 const long long ulimit = LLONG_MAX,
41 const char ** const tailp = 0 )
42 {
43 char * tail;
44 errno = 0;
45 long long result = strtoll( ptr, &tail, 0 );
46 if( tail == ptr )
47 {
48 show_error( "Bad or missing numerical argument.", 0, true );
49 std::exit( 1 );
50 }
51
52 if( !errno && tail[0] )
53 {
54 char * const p = tail++;
55 int factor = 1000; // default factor
56 int exponent = -1; // -1 = bad multiplier
57 char usuf = 0; // 'B' or 's' unit suffix is present
58 switch( *p )
59 {
60 case 'Y': exponent = 8; break;
61 case 'Z': exponent = 7; break;
62 case 'E': exponent = 6; break;
63 case 'P': exponent = 5; break;
64 case 'T': exponent = 4; break;
65 case 'G': exponent = 3; break;
66 case 'M': exponent = 2; break;
67 case 'K': if( tail[0] == 'i' ) { ++tail; factor = 1024; exponent = 1; } break;
68 case 'k': if( tail[0] != 'i' ) exponent = 1; break;
69 case 'B':
70 case 's': usuf = *p; exponent = 0; break;
71 default : if( tailp ) { tail = p; exponent = 0; } break;
72 }
73 if( exponent > 1 && tail[0] == 'i' ) { ++tail; factor = 1024; }
74 if( exponent > 0 && usuf == 0 && ( tail[0] == 'B' || tail[0] == 's' ) )
75 { usuf = tail[0]; ++tail; }
76 if( exponent < 0 || ( usuf == 's' && hardbs <= 0 ) ||
77 ( !tailp && tail[0] != 0 ) )
78 {
79 show_error( "Bad multiplier in numerical argument.", 0, true );
80 std::exit( 1 );
81 }
82 for( int i = 0; i < exponent; ++i )
83 {
84 if( LLONG_MAX / factor >= llabs( result ) ) result *= factor;
85 else { errno = ERANGE; break; }
86 }
87 if( usuf == 's' )
88 {
89 if( LLONG_MAX / hardbs >= llabs( result ) ) result *= hardbs;
90 else errno = ERANGE;
91 }
92 }
93 if( !errno && ( result < llimit || result > ulimit ) ) errno = ERANGE;
94 if( errno )
95 {
96 show_error( "Numerical argument out of limits." );
97 std::exit( 1 );
98 }
99 if( tailp ) *tailp = tail;
100 return result;
101 }
102
103
check_types(std::string & types,const char * const opt_name,const bool allow_l=false)104 bool check_types( std::string & types, const char * const opt_name,
105 const bool allow_l = false )
106 {
107 bool error = false;
108 bool write_location_data = false;
109 for( int i = types.size(); i > 0; )
110 {
111 if( types[--i] == 'l' )
112 { if( !allow_l ) { error = true; break; }
113 write_location_data = true; types.erase( i, 1 ); continue; }
114 if( !Sblock::isstatus( types[i] ) )
115 { error = true; break; }
116 }
117 if( types.empty() || error )
118 {
119 char buf[80];
120 snprintf( buf, sizeof buf, "Invalid type for option '%s'.", opt_name );
121 show_error( buf, 0, true );
122 std::exit( 1 );
123 }
124 return write_location_data;
125 }
126
127
set_mode(Mode & program_mode,const Mode new_mode)128 void set_mode( Mode & program_mode, const Mode new_mode )
129 {
130 if( program_mode != m_none && program_mode != new_mode )
131 {
132 show_error( "Only one operation can be specified.", 0, true );
133 std::exit( 1 );
134 }
135 program_mode = new_mode;
136 }
137
138
set_name(const char ** name,const char * new_name,const char opt)139 void set_name( const char ** name, const char * new_name, const char opt )
140 {
141 if( *name )
142 {
143 std::string msg( "Option '- ' can be specified only once." );
144 msg[9] = opt;
145 show_error( msg.c_str(), 0, true );
146 std::exit( 1 );
147 }
148 *name = new_name;
149 }
150
151
get_timestamp(const long t=0)152 const char * get_timestamp( const long t = 0 )
153 {
154 static char buf[80];
155 const time_t tt = t ? t : std::time( 0 );
156 const struct tm * const tm = std::localtime( &tt );
157 if( !tm || std::strftime( buf, sizeof buf, "%Y-%m-%d %H:%M:%S", tm ) == 0 )
158 buf[0] = 0;
159 return buf;
160 }
161
162 } // end namespace
163
164
show_error(const char * const msg,const int errcode,const bool help)165 void show_error( const char * const msg, const int errcode, const bool help )
166 {
167 if( verbosity < 0 ) return;
168 if( msg && msg[0] )
169 std::fprintf( stderr, "%s: %s%s%s\n", program_name, msg,
170 ( errcode > 0 ) ? ": " : "",
171 ( errcode > 0 ) ? std::strerror( errcode ) : "" );
172 if( help )
173 std::fprintf( stderr, "Try '%s --help' for more information.\n",
174 invocation_name );
175 }
176
177
internal_error(const char * const msg)178 void internal_error( const char * const msg )
179 {
180 if( verbosity >= 0 )
181 std::fprintf( stderr, "%s: internal error: %s\n", program_name, msg );
182 std::exit( 3 );
183 }
184
185
empty_domain()186 int empty_domain()
187 { show_error( "Nothing to do; domain is empty." ); return 0; }
188
189
not_readable(const char * const mapname)190 int not_readable( const char * const mapname )
191 {
192 char buf[80];
193 snprintf( buf, sizeof buf,
194 "Mapfile '%s' does not exist or is not readable.", mapname );
195 show_error( buf );
196 return 1;
197 }
198
199
not_writable(const char * const mapname)200 int not_writable( const char * const mapname )
201 {
202 char buf[80];
203 snprintf( buf, sizeof buf, "Mapfile '%s' is not writable.", mapname );
204 show_error( buf );
205 return 1;
206 }
207
208
initial_time()209 long initial_time()
210 {
211 static long initial_time_ = 0;
212
213 if( initial_time_ == 0 ) initial_time_ = std::time( 0 );
214 return initial_time_;
215 }
216
217
write_file_header(FILE * const f,const char * const filetype)218 bool write_file_header( FILE * const f, const char * const filetype )
219 {
220 static std::string timestamp;
221
222 if( timestamp.empty() ) timestamp = get_timestamp( initial_time() );
223 return ( std::fprintf( f, "# %s. Created by %s version %s\n"
224 "# Command line: %s\n"
225 "# Start time: %s\n",
226 filetype, Program_name, PROGVERSION, command_line.c_str(),
227 timestamp.c_str() ) >= 0 );
228 }
229
230
write_timestamp(FILE * const f)231 bool write_timestamp( FILE * const f )
232 {
233 const char * const timestamp = get_timestamp();
234
235 return ( !timestamp || !timestamp[0] ||
236 std::fprintf( f, "# Current time: %s\n", timestamp ) >= 0 );
237 }
238
239
write_final_timestamp(FILE * const f)240 bool write_final_timestamp( FILE * const f )
241 {
242 static std::string timestamp;
243
244 if( timestamp.empty() ) timestamp = get_timestamp();
245 return ( std::fprintf( f, "# End time: %s\n", timestamp.c_str() ) >= 0 );
246 }
247
248
format_num(long long num,long long limit,const int set_prefix)249 const char * format_num( long long num, long long limit,
250 const int set_prefix )
251 {
252 const char * const si_prefix[8] =
253 { "k", "M", "G", "T", "P", "E", "Z", "Y" };
254 const char * const binary_prefix[8] =
255 { "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi" };
256 enum { buffers = 8, bufsize = 16 };
257 static char buffer[buffers][bufsize]; // circle of static buffers for printf
258 static int current = 0;
259 static bool si = true;
260
261 if( set_prefix ) si = ( set_prefix > 0 );
262 const int factor = si ? 1000 : 1024;
263 char * const buf = buffer[current++]; current %= buffers;
264 const char * const * prefix = si ? si_prefix : binary_prefix;
265 const char * p = "";
266 limit = std::max( 999LL, std::min( 999999LL, limit ) );
267
268 for( int i = 0; i < 8 && llabs( num ) > limit; ++i )
269 { num /= factor; p = prefix[i]; }
270 snprintf( buf, bufsize, "%lld %s", num, p );
271 return buf;
272 }
273
274
275 // Shows the fraction "num/den" as a percentage with "prec" decimals.
276 // If 'prec' is negative, only the decimals needed are shown.
277 //
format_percentage(long long num,long long den,const int iwidth,int prec)278 const char * format_percentage( long long num, long long den,
279 const int iwidth, int prec )
280 {
281 static char buf[80];
282
283 if( den < 0 ) { num = -num; den = -den; }
284 if( llabs( num ) <= LLONG_MAX / 100 && den <= LLONG_MAX / 10 ) num *= 100;
285 else if( llabs( num ) <= LLONG_MAX / 10 ) { num *= 10; den /= 10; }
286 else den /= 100;
287 if( den == 0 )
288 {
289 if( num > 0 ) return "+INF";
290 else if( num < 0 ) return "-INF";
291 else return "NAN";
292 }
293 const bool trunc = ( prec < 0 );
294 if( prec < 0 ) prec = -prec;
295
296 unsigned i;
297 if( num < 0 && num / den == 0 )
298 i = snprintf( buf, sizeof( buf ), "%*s", iwidth, "-0" );
299 else i = snprintf( buf, sizeof( buf ), "%*lld", iwidth, num / den );
300 if( i < sizeof( buf ) - 2 )
301 {
302 long long rest = llabs( num ) % den;
303 if( prec > 0 && ( rest > 0 || !trunc ) )
304 {
305 buf[i++] = '.';
306 while( prec > 0 && ( rest > 0 || !trunc ) && i < sizeof( buf ) - 2 )
307 { rest *= 10; buf[i++] = ( rest / den ) + '0';
308 rest %= den; --prec; }
309 }
310 }
311 else i = sizeof( buf ) - 2;
312 buf[i++] = '%';
313 buf[i] = 0;
314 return buf;
315 }
316