1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
|
/* output.c - output of results, errors and percents */
#include "output.h"
#include "calc_sums.h"
#include "parse_cmdline.h"
#include "platform.h"
#include "rhash_main.h"
#include "win_utils.h"
#include "librhash/rhash.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h> /* exit() */
#include <assert.h>
#include <errno.h>
#ifdef _WIN32
# include <windows.h>
# include <io.h> /* for isatty */
#endif
/* global pointer to the selected method of percents output */
struct percents_output_info_t* percents_output = NULL;
#ifdef _WIN32
# define IS_UTF8() (opt.flags & OPT_UTF8)
#else
# define IS_UTF8() 1
#endif
/**
* Calculate the number of symbols printed by fprintf_file_t(...) for a given formated message.
* @patam format (nullable) message format string
* @param path file path
* @param output_flags bitmask specifying path output mode
* @return the number of printed symbols
*/
static int count_printed_size(const char* format, const char* path, unsigned output_flags)
{
size_t format_length = 0;
if (format) {
assert(strstr(format, "%s") != NULL);
format_length = (IS_UTF8() ? count_utf8_symbols(format) : strlen(format)) - 2;
}
assert(path != NULL);
return format_length + (IS_UTF8() || (output_flags & OutForceUtf8) ? count_utf8_symbols(path) : strlen(path));
}
/**
* Print formatted file path to the specified stream.
*
* @param out the stream to write to
* @param format (nullable) format string
* @param file the file, which path will be formatted
* @param output_flags bitmask containing mix of OutForceUtf8, OutBaseName, OutCountSymbols flags
* @return the number of characters printed, -1 on fail with error code stored in errno
*/
int fprintf_file_t(FILE* out, const char* format, struct file_t* file, unsigned output_flags)
{
unsigned basename_bit = output_flags & FPathBaseName;
#ifdef _WIN32
const char* print_path;
if (!file->real_path) {
print_path = file_get_print_path(file, FPathPrimaryEncoding | FPathNotNull | basename_bit);
} else {
unsigned ppf = ((output_flags & OutForceUtf8) || (opt.flags & OPT_UTF8) ? FPathUtf8 | FPathNotNull : FPathPrimaryEncoding);
assert(file->real_path != NULL);
assert((int)OutBaseName == (int)FPathBaseName);
print_path = file_get_print_path(file, ppf | basename_bit);
if (!print_path) {
print_path = file_get_print_path(file, FPathUtf8 | FPathNotNull | basename_bit);
assert(print_path);
assert(!(opt.flags & OPT_UTF8));
}
}
#else
const char* print_path = file_get_print_path(file, FPathPrimaryEncoding | FPathNotNull | basename_bit);
assert((int)OutBaseName == (int)FPathBaseName);
assert(print_path);
#endif
if (rsh_fprintf(out, (format ? format : "%s"), print_path) < 0)
return -1;
if ((output_flags & OutCountSymbols) != 0)
return count_printed_size(format, print_path, output_flags);
return 0;
}
/* RFC 3986: safe url characters are ascii alpha-numeric and "-._~", other characters should be percent-encoded */
static unsigned url_safe_char_mask[4] = { 0, 0x03ff6000, 0x87fffffe, 0x47fffffe };
#define IS_URL_GOOD_CHAR(c) ((unsigned)(c) < 128 && (url_safe_char_mask[c >> 5] & (1 << (c & 31))))
/**
* Print to a stram an url-encoded representation of the given string.
*
* @param out the stream to print the result to
* @param str string to encode
* @param upper_case flag to print hex-codes in uppercase
* @return 0 on success, -1 on fail with error code stored in errno
*/
int fprint_urlencoded(FILE* out, const char* str, int upper_case)
{
char buffer[1024];
char* buffer_limit = buffer + (sizeof(buffer) - 3);
char *p;
const char hex_add = (upper_case ? 'A' - 10 : 'a' - 10);
while (*str) {
for (p = buffer; p < buffer_limit && *str; str++) {
if (IS_URL_GOOD_CHAR(*str)) {
*(p++) = *str;
} else {
unsigned char hi = ((unsigned char)(*str) >> 4) & 0x0f;
unsigned char lo = (unsigned char)(*str) & 0x0f;
*(p++) = '%';
*(p++) = (hi > 9 ? hi + hex_add : hi + '0');
*(p++) = (lo > 9 ? lo + hex_add : lo + '0');
}
}
*p = 0;
if (rsh_fprintf(out, "%s", buffer) < 0)
return -1;
}
return 0;
}
/*=========================================================================
* Logging functions
*=========================================================================*/
/**
* Print message prefix before printing an error/warning message.
*
* @return the number of characters printed, -1 on error
*/
static int print_log_prefix(void)
{
return rsh_fprintf(rhash_data.log, "%s: ", PROGRAM_NAME);
}
/**
* Print a formatted message to the program log, and flush the log stream.
*
* @param format print a formatted message to the program log
* @param args
*/
static void log_va_msg(const char* format, va_list args)
{
rsh_vfprintf(rhash_data.log, format, args);
fflush(rhash_data.log);
}
/**
* Print a formatted message to the program log, and flush the log stream.
*
* @param format print a formatted message to the program log
*/
void log_msg(const char* format, ...)
{
va_list ap;
va_start(ap, format);
log_va_msg(format, ap);
va_end(ap);
}
/**
* Print a formatted message, where a single %s substring is replaced with a filepath, and flush the log stream.
* This function aims to correctly process utf8 conversion on windows.
* Note: on windows the format string must be in utf8 encoding.
*
* @param format the format string of a formatted message
* @param file the file, which path will be formatted
*/
void log_msg_file_t(const char* format, struct file_t* file)
{
fprintf_file_t(rhash_data.log, format, file, OutDefaultFlags);
fflush(rhash_data.log);
}
/**
* Print a formatted error message to the program log.
*
* @param format the format string
*/
void log_error(const char* format, ...)
{
va_list ap;
va_start(ap, format);
print_log_prefix();
log_va_msg(format, ap);
va_end(ap);
}
/**
* Print file error to the program log.
*
* @param file the file, caused the error
*/
void log_error_file_t(struct file_t* file)
{
int file_errno = errno;
print_log_prefix();
fprintf_file_t(rhash_data.log, "%s", file, OutDefaultFlags);
rsh_fprintf(rhash_data.log, ": %s\n", strerror(file_errno));
fflush(rhash_data.log);
}
/**
* Print a formated error message with file path.
*
* @param file the file, caused the error
*/
void log_error_msg_file_t(const char* format, struct file_t* file)
{
print_log_prefix();
fprintf_file_t(rhash_data.log, format, file, OutDefaultFlags);
fflush(rhash_data.log);
}
/**
* Log the message, that the program was interrupted.
* The function should be called only once.
*/
void report_interrupted(void)
{
static int is_interrupted_reported = 0;
assert(rhash_data.stop_flags != 0);
if (rhash_data.stop_flags == InterruptedFlag && !is_interrupted_reported) {
is_interrupted_reported = 1;
log_msg(_("Interrupted by user...\n"));
}
}
/**
* Information about printed percents.
*/
struct percents_t
{
int points;
int same_output;
unsigned ticks;
};
static struct percents_t percents;
/* the hash functions, which needs to be reported first on mismatch */
#define REPORT_FIRST_MASK (RHASH_MD5 | RHASH_SHA256 | RHASH_SHA512)
/**
* Print verbose error on a message digest mismatch.
*
* @param info file information with path and its message digests
* @return 0 on success, -1 on error
*/
static int print_verbose_hash_check_error(struct file_info* info)
{
char actual[130], expected[130];
assert(HP_FAILED(info->hp->bit_flags));
/* TRANSLATORS: printed in the verbose mode on a message digest mismatch */
if (rsh_fprintf(rhash_data.out, _("ERROR")) < 0)
return -1;
if ((HpWrongFileSize & info->hp->bit_flags)) {
sprintI64(actual, info->rctx->msg_size, 0);
sprintI64(expected, info->hp->file_size, 0);
if (rsh_fprintf(rhash_data.out, _(", size is %s should be %s"), actual, expected) < 0)
return -1;
}
if (HpWrongEmbeddedCrc32 & info->hp->bit_flags) {
rhash_print(expected, info->rctx, RHASH_CRC32, RHPR_UPPERCASE);
if (rsh_fprintf(rhash_data.out, _(", embedded CRC32 should be %s"), expected) < 0)
return -1;
}
if (HpWrongHashes & info->hp->bit_flags) {
unsigned reported = 0;
int i;
for (i = 0; i < info->hp->hashes_num; i++) {
struct hash_value* hv = &info->hp->hashes[i];
char* expected_hash = info->hp->line_begin + hv->offset;
unsigned hid = hv->hash_id;
int pflags;
if ((info->hp->wrong_hashes & (1 << i)) == 0)
continue;
assert(hid != 0);
/* if can't detect precise hash */
if ((hid & (hid - 1)) != 0) {
/* guess the hash id */
if (hid & opt.sum_flags) hid &= opt.sum_flags;
if (hid & ~info->hp->found_hash_ids) hid &= ~info->hp->found_hash_ids;
if (hid & ~reported) hid &= ~reported; /* avoid repeating */
if (hid & REPORT_FIRST_MASK) hid &= REPORT_FIRST_MASK;
hid &= -(int)hid; /* take the lowest bit */
}
assert(hid != 0 && (hid & (hid - 1)) == 0); /* single bit only */
reported |= hid;
pflags = (hv->length == (rhash_get_digest_size(hid) * 2) ?
(RHPR_HEX | RHPR_UPPERCASE) : (RHPR_BASE32 | RHPR_UPPERCASE));
rhash_print(actual, info->rctx, hid, pflags);
/* TRANSLATORS: print a message like "CRC32 is ABC12345 should be BCA54321" */
if (rsh_fprintf(rhash_data.out, _(", %s is %s should be %s"),
rhash_get_name(hid), actual, expected_hash) < 0)
return -1;
}
}
return PRINTF_RES(rsh_fprintf(rhash_data.out, "\n"));
}
/**
* Print file path and no more than 52 spaces.
*
* @param out stream to print the filepath
* @param info pointer to the file-info structure
* @return 0 on success, -1 on i/o error
*/
static int print_aligned_filepath(FILE* out, struct file_info* info)
{
int symbols_count = fprintf_file_t(out, NULL, info->file, OutCountSymbols);
if (symbols_count >= 0) {
char buf[56];
int spaces_count = (symbols_count <= 51 ? 52 - symbols_count : 1);
return PRINTF_RES(rsh_fprintf(out, "%s", str_set(buf, ' ', spaces_count)));
}
return -1;
}
/**
* Print file path and result of its verification against message digests.
* Also if error occurred, print error message.
*
* @param info pointer to the file-info structure
* @param print_name set to non-zero to print file path
* @param print_result set to non-zero to print verification result
* @return 0 on success, -1 on i/o error
*/
static int print_check_result(struct file_info* info, int print_name, int print_result)
{
int saved_errno = errno;
int res = 0;
if (print_name)
res = print_aligned_filepath(rhash_data.out, info);
if (print_result && res == 0) {
if (info->processing_result < 0) {
/* print error to stdout */
res = PRINTF_RES(rsh_fprintf(rhash_data.out, "%s\n", strerror(saved_errno)));
} else if (!HP_FAILED(info->hp->bit_flags) || !(opt.flags & OPT_VERBOSE)) {
res = PRINTF_RES(rsh_fprintf(rhash_data.out, (!HP_FAILED(info->hp->bit_flags) ?
/* TRANSLATORS: printed when all message digests match, use at least 3 characters to overwrite "99%" */
_("OK \n") :
/* TRANSLATORS: ERR (short for 'error') is printed on a message digest mismatch */
_("ERR\n"))));
} else {
res = print_verbose_hash_check_error(info);
}
}
if (fflush(rhash_data.out) < 0)
res = -1;
return res;
}
/**
* Prepare or print result of file processing.
*
* @param info pointer to the file-info structure
* @param init non-zero on initialization before message digests calculation,
* and zero after message digests calculation finished.
* @return 0 on success, -1 on i/o error
*/
static int print_results_on_check(struct file_info* info, int init)
{
if (IS_MODE(MODE_CHECK | MODE_CHECK_EMBEDDED)) {
int print_name = (opt.flags & (OPT_PERCENTS | OPT_SKIP_OK) ? !init : init);
/* print result, but skip OK messages if required */
if (init || info->processing_result != 0 || !(opt.flags & OPT_SKIP_OK) || HP_FAILED(info->hp->bit_flags))
return print_check_result(info, print_name, !init);
}
return 0;
}
/* functions to output file info without percents */
/**
* Print file name in the verification mode.
* No information is printed in other modes.
*
* @param info pointer to the file-info structure
* @return 0 on success, -1 if output to rhash_data.out failed
*/
static int dummy_init_percents(struct file_info* info)
{
return print_results_on_check(info, 1);
}
/**
* Print file check results without printing percents.
* Information is printed only in the verification mode.
*
* @param info pointer to the file-info structure
* @param process_res non-zero if error occurred while hashing/checking
* @return 0 on success, -1 if output to rhash_data.out failed
*/
static int dummy_finish_percents(struct file_info* info, int process_res)
{
info->processing_result = process_res;
return print_results_on_check(info, 0);
}
/* functions to output file info with simple multi-line wget-like percents */
/**
* Initialize dots percent mode.
*
* @param info pointer to the file-info structure
* @return 0 on success, -1 if output to rhash_data.out failed
*/
static int dots_init_percents(struct file_info* info)
{
int res = fflush(rhash_data.out);
fflush(rhash_data.log);
percents.points = 0;
if (print_results_on_check(info, 1) < 0)
res = -1;
return res;
}
/**
* Finish dots percent mode. If in the verification mode,
* then print the results of file check.
*
* @param info pointer to the file-info structure
* @param process_res non-zero if error occurred while hashing/checking
* @return 0 on success, -1 if output to rhash_data.out failed
*/
static int dots_finish_percents(struct file_info* info, int process_res)
{
char buf[80];
info->processing_result = process_res;
if ((percents.points % 74) != 0) {
log_msg("%s 100%%\n", str_set(buf, ' ', 74 - (percents.points % 74)));
}
return print_results_on_check(info, 0);
}
/**
* Output percents by printing one dot per each processed 1MiB.
*
* @param info pointer to the file-info structure
* @param offset the number of hashed bytes
*/
static void dots_update_percents(struct file_info* info, uint64_t offset)
{
const int pt_size = 1024 * 1024; /* 1MiB */
offset -= info->msg_offset; /* get real file offset */
if ( (offset % pt_size) != 0 ) return;
if (percents.points == 0) {
if (IS_MODE(MODE_CHECK | MODE_CHECK_EMBEDDED)) {
rsh_fprintf(rhash_data.log, _("\nChecking %s\n"), file_get_print_path(info->file, FPathPrimaryEncoding | FPathNotNull));
} else {
rsh_fprintf(rhash_data.log, _("\nProcessing %s\n"), file_get_print_path(info->file, FPathPrimaryEncoding | FPathNotNull));
}
fflush(rhash_data.log);
}
putc('.', rhash_data.log);
if (((++percents.points) % 74) == 0) {
if (info->size > 0) {
int perc = (int)( offset * 100.0 / (uint64_t)info->size + 0.5 );
rsh_fprintf(rhash_data.log, " %2u%%\n", perc);
fflush(rhash_data.log);
} else {
putc('\n', rhash_data.log);
}
}
}
/* console one-line percents */
/**
* Initialize one-line percent mode.
*
* @param info pointer to the file-info structure
* @return 0 on success, -1 if output to rhash_data.out failed
*/
static int p_init_percents(struct file_info* info)
{
int res = fflush(rhash_data.out);
fflush(rhash_data.log);
/* ingnore output errors, while logging to rhash_data.log */
print_aligned_filepath(rhash_data.log, info);
percents.points = 0;
percents.same_output = (rhash_data.out == stdout && isatty(0) &&
rhash_data.log == stderr && isatty(1));
percents.ticks = rhash_get_ticks();
return res;
}
/**
* Output one-line percents by printing them after file path.
* If the total file length is unknow (i.e. hashing stdin),
* then output a rotating stick.
*
* @param info pointer to the file-info structure
* @param offset the number of hashed bytes
*/
static void p_update_percents(struct file_info* info, uint64_t offset)
{
static const char rotated_bar[4] = {'-', '\\', '|', '/'};
int perc = 0;
unsigned ticks;
if (info->size > 0) {
offset -= info->msg_offset;
/* use only two digits to display percents: 0%-99% */
perc = (int)( offset * 99.9 / (uint64_t)info->size );
if (percents.points == perc) return;
}
/* update percents no more than 20 times per second */
ticks = rhash_get_ticks(); /* clock ticks count in milliseconds */
if ((unsigned)(ticks - percents.ticks) < 50)
return;
/* output percents or a rotated bar */
if (info->size > 0) {
rsh_fprintf(rhash_data.log, "%u%%\r", perc);
percents.points = perc;
} else {
rsh_fprintf(rhash_data.log, "%c\r", rotated_bar[(percents.points++) & 3]);
}
print_aligned_filepath(rhash_data.log, info);
fflush(rhash_data.log);
percents.ticks = ticks;
}
/**
* Finish one-line percent mode. If in the verification mode,
* then print the results of file check.
*
* @param info pointer to the file-info structure
* @param process_res non-zero if error occurred while hashing/checking
* @return 0 on success, -1 if output to rhash_data.out failed
*/
static int p_finish_percents(struct file_info* info, int process_res)
{
int need_check_result = IS_MODE(MODE_CHECK | MODE_CHECK_EMBEDDED) &&
!((opt.flags & OPT_SKIP_OK) && process_res == 0 && !HP_FAILED(info->hp->bit_flags));
info->processing_result = process_res;
if (percents.same_output && need_check_result) {
return print_check_result(info, 0, 1);
} else {
rsh_fprintf(rhash_data.log, "100%%\n");
fflush(rhash_data.log);
if (need_check_result)
return print_check_result(info, 1, 1);
}
return 0;
}
/* three methods of percents output */
struct percents_output_info_t dummy_perc = {
dummy_init_percents, 0, dummy_finish_percents, "dummy"
};
struct percents_output_info_t dots_perc = {
dots_init_percents, dots_update_percents, dots_finish_percents, "dots"
};
struct percents_output_info_t p_perc = {
p_init_percents, p_update_percents, p_finish_percents, "digits"
};
/**
* Initialize given output stream.
*
* @param p_stream the stream to initialize
* @param stream_path the path to the log file, or NULL, to use the default stream
* @param default_stream the default stream value, for the case of invalid stream_path
*/
static void setup_log_stream(FILE** p_stream, file_t* file, const opt_tchar* stream_path, FILE* default_stream)
{
FILE* result;
/* set to the default stream, to enable error reporting via log_error_file_t() */
*p_stream = default_stream;
if (!stream_path) {
file_init_by_print_path(file, NULL, (default_stream == stdout ? "(stdout)" : "(stderr)"), FileIsStdStream);
return;
}
file_init(file, stream_path, FileInitReusePath);
result = file_fopen(file, FOpenWrite);
if (!result) {
log_error_file_t(file);
rsh_exit(2);
}
*p_stream = result;
}
/**
* Initialize pointers to output functions.
*/
void setup_output(void)
{
setup_log_stream(&rhash_data.log, &rhash_data.log_file, opt.log, stderr);
setup_log_stream(&rhash_data.out, &rhash_data.out_file, opt.output, stdout);
}
void setup_percents(void)
{
if (opt.flags & OPT_PERCENTS) {
/* NB: we don't use _fileno() cause it is not in ISO C90, and so
* is incompatible with the GCC -ansi option */
if (rhash_data.log == stderr && isatty(2)) {
/* use one-line percents by default on console */
percents_output = &p_perc;
IF_WINDOWS(hide_cursor());
} else {
/* print percents as dots */
percents_output = &dots_perc;
}
} else {
percents_output = &dummy_perc; /* no percents */
}
}
/* misc output functions */
/**
* Print the "Verifying <FILE>" heading line.
*
* @param file the file containing message digests to verify
* @return 0 on success, -1 on fail with error code stored in errno
*/
int print_verifying_header(file_t* file)
{
if ((opt.flags & OPT_BRIEF) == 0)
{
char dash_line[84];
/* TRANSLATORS: the line printed before a hash file is verified */
int count = fprintf_file_t(rhash_data.out, _("\n--( Verifying %s )"), file, OutCountSymbols);
int tail_dash_len = (0 < count && count < 81 ? 81 - count : 2);
int res = rsh_fprintf(rhash_data.out, "%s\n", str_set(dash_line, '-', tail_dash_len));
if (res >= 0)
res = fflush(rhash_data.out);
return (count < 0 ? count : res);
}
return 0;
}
/**
* Print a line consisting of 80 dashes.
*
* @return 0 on success, -1 on fail with error code stored in errno
*/
int print_verifying_footer(void)
{
char dash_line[84];
return (opt.flags & OPT_BRIEF ? 0 :
rsh_fprintf(rhash_data.out, "%s\n", str_set(dash_line, '-', 80)));
}
/**
* Print total statistics of hash file checking.
*
* @return 0 on success, -1 on i/o error with error code stored in errno
*/
int print_check_stats(void)
{
int res;
if (rhash_data.processed == rhash_data.ok) {
/* NOTE: don't use puts() here cause it messes up with fprintf stdout buffering */
const char* message = (rhash_data.processed > 0 ?
_("Everything OK\n") :
_("Nothing to verify\n"));
res = PRINTF_RES(rsh_fprintf(rhash_data.out, "%s", message));
} else {
res = PRINTF_RES(rsh_fprintf(rhash_data.out, _("Errors Occurred: Errors:%-3u Miss:%-3u Success:%-3u Total:%-3u\n"),
rhash_data.processed - rhash_data.ok - rhash_data.miss, rhash_data.miss, rhash_data.ok, rhash_data.processed));
}
return (fflush(rhash_data.out) < 0 ? -1 : res);
}
/**
* Print file processing times.
*/
void print_file_time_stats(struct file_info* info)
{
print_time_stats(info->time, info->size, 0);
}
/**
* Print processing time statistics.
*
* @param time time in milliseconds
* @param size total size of the processed data
* @param total boolean flag, to print total or per-file result
*/
void print_time_stats(uint64_t time, uint64_t size, int total)
{
double seconds = (double)(int64_t)time / 1000.0;
double speed = (time == 0 ? 0 : (double)(int64_t)size / 1048576.0 / seconds);
if (total) {
rsh_fprintf(rhash_data.log, _("Total %.3f sec, %4.2f MBps\n"), seconds, speed);
} else {
rsh_fprintf(rhash_data.log, _("Calculated in %.3f sec, %4.2f MBps\n"), seconds, speed);
}
fflush(rhash_data.log);
}
|