static void print_totals_robot(void) { char checks[CHECKS_STR_SIZE]; get_check_names(checks, totals.checks, false); printf("totals\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%s\t%s\t%" PRIu64 "\t%" PRIu64, totals.streams, totals.blocks, totals.compressed_size, totals.uncompressed_size, get_ratio(totals.compressed_size, totals.uncompressed_size), checks, totals.stream_padding, totals.files); if (message_verbosity_get() >= V_DEBUG) printf("\t%" PRIu64 "\t%s\t%" PRIu32, totals.memusage_max, totals.all_have_sizes ? "yes" : "no", totals.min_version); putchar('\n'); return; }
extern void list_file(const char *filename) { if (opt_format != FORMAT_XZ && opt_format != FORMAT_AUTO) message_fatal(_("--list works only on .xz files " "(--format=xz or --format=auto)")); message_filename(filename); if (filename == stdin_filename) { message_error(_("--list does not support reading from " "standard input")); return; } // Unset opt_stdout so that io_open_src() won't accept special files. // Set opt_force so that io_open_src() will follow symlinks. opt_stdout = false; opt_force = true; file_pair *pair = io_open_src(filename); if (pair == NULL) return; xz_file_info xfi = XZ_FILE_INFO_INIT; if (!parse_indexes(&xfi, pair)) { bool fail; // We have three main modes: // - --robot, which has submodes if --verbose is specified // once or twice // - Normal --list without --verbose // - --list with one or two --verbose if (opt_robot) fail = print_info_robot(&xfi, pair); else if (message_verbosity_get() <= V_WARNING) fail = print_info_basic(&xfi, pair); else fail = print_info_adv(&xfi, pair); // Update the totals that are displayed after all // the individual files have been listed. Don't count // broken files. if (!fail) update_totals(&xfi); lzma_index_end(xfi.idx, NULL); } io_close(pair, false); return; }
extern void list_totals(void) { if (opt_robot) { // Always print totals in --robot mode. It can be convenient // in some cases and doesn't complicate usage of the // single-file case much. print_totals_robot(); } else if (totals.files > 1) { // For non-robot mode, totals are printed only if there // is more than one file. if (message_verbosity_get() <= V_WARNING) print_totals_basic(); else print_totals_adv(); } return; }
static void print_totals_adv(void) { putchar('\n'); puts(_("Totals:")); printf(_(" Number of files: %s\n"), uint64_to_str(totals.files, 0)); print_adv_helper(totals.streams, totals.blocks, totals.compressed_size, totals.uncompressed_size, totals.checks, totals.stream_padding); if (message_verbosity_get() >= V_DEBUG) { printf(_(" Memory needed: %s MiB\n"), uint64_to_str( round_up_to_mib(totals.memusage_max), 0)); printf(_(" Sizes in headers: %s\n"), totals.all_have_sizes ? _("Yes") : _("No")); } return; }
static bool print_info_robot(xz_file_info *xfi, file_pair *pair) { char checks[CHECKS_STR_SIZE]; get_check_names(checks, lzma_index_checks(xfi->idx), false); printf("name\t%s\n", pair->src_name); printf("file\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%s\t%s\t%" PRIu64 "\n", lzma_index_stream_count(xfi->idx), lzma_index_block_count(xfi->idx), lzma_index_file_size(xfi->idx), lzma_index_uncompressed_size(xfi->idx), get_ratio(lzma_index_file_size(xfi->idx), lzma_index_uncompressed_size(xfi->idx)), checks, xfi->stream_padding); if (message_verbosity_get() >= V_VERBOSE) { lzma_index_iter iter; lzma_index_iter_init(&iter, xfi->idx); while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)) printf("stream\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%s\t%s\t%" PRIu64 "\n", iter.stream.number, iter.stream.block_count, iter.stream.compressed_offset, iter.stream.uncompressed_offset, iter.stream.compressed_size, iter.stream.uncompressed_size, get_ratio(iter.stream.compressed_size, iter.stream.uncompressed_size), check_names[iter.stream.flags->check], iter.stream.padding); lzma_index_iter_rewind(&iter); block_header_info bhi; while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)) { if (message_verbosity_get() >= V_DEBUG && parse_details( pair, &iter, &bhi, xfi)) return true; printf("block\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%s\t%s", iter.stream.number, iter.block.number_in_stream, iter.block.number_in_file, iter.block.compressed_file_offset, iter.block.uncompressed_file_offset, iter.block.total_size, iter.block.uncompressed_size, get_ratio(iter.block.total_size, iter.block.uncompressed_size), check_names[iter.stream.flags->check]); if (message_verbosity_get() >= V_DEBUG) printf("\t%s\t%" PRIu32 "\t%s\t%" PRIu64 "\t%" PRIu64 "\t%s", check_value, bhi.header_size, bhi.flags, bhi.compressed_size, bhi.memusage, bhi.filter_chain); putchar('\n'); } } if (message_verbosity_get() >= V_DEBUG) printf("summary\t%" PRIu64 "\t%s\t%" PRIu32 "\n", xfi->memusage_max, xfi->all_have_sizes ? "yes" : "no", xfi->min_version); return false; }
static bool print_info_adv(xz_file_info *xfi, file_pair *pair) { // Print the overall information. print_adv_helper(lzma_index_stream_count(xfi->idx), lzma_index_block_count(xfi->idx), lzma_index_file_size(xfi->idx), lzma_index_uncompressed_size(xfi->idx), lzma_index_checks(xfi->idx), xfi->stream_padding); // Size of the biggest Check. This is used to calculate the width // of the CheckVal field. The table would get insanely wide if // we always reserved space for 64-byte Check (128 chars as hex). uint32_t check_max = 0; // Print information about the Streams. // // TRANSLATORS: The second line is column headings. All except // Check are right aligned; Check is left aligned. Test with // "xz -lv foo.xz". puts(_(" Streams:\n Stream Blocks" " CompOffset UncompOffset" " CompSize UncompSize Ratio" " Check Padding")); lzma_index_iter iter; lzma_index_iter_init(&iter, xfi->idx); while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)) { const char *cols1[4] = { uint64_to_str(iter.stream.number, 0), uint64_to_str(iter.stream.block_count, 1), uint64_to_str(iter.stream.compressed_offset, 2), uint64_to_str(iter.stream.uncompressed_offset, 3), }; printf(" %*s %*s %*s %*s ", tuklib_mbstr_fw(cols1[0], 6), cols1[0], tuklib_mbstr_fw(cols1[1], 9), cols1[1], tuklib_mbstr_fw(cols1[2], 15), cols1[2], tuklib_mbstr_fw(cols1[3], 15), cols1[3]); const char *cols2[5] = { uint64_to_str(iter.stream.compressed_size, 0), uint64_to_str(iter.stream.uncompressed_size, 1), get_ratio(iter.stream.compressed_size, iter.stream.uncompressed_size), _(check_names[iter.stream.flags->check]), uint64_to_str(iter.stream.padding, 2), }; printf("%*s %*s %*s %-*s %*s\n", tuklib_mbstr_fw(cols2[0], 15), cols2[0], tuklib_mbstr_fw(cols2[1], 15), cols2[1], tuklib_mbstr_fw(cols2[2], 5), cols2[2], tuklib_mbstr_fw(cols2[3], 10), cols2[3], tuklib_mbstr_fw(cols2[4], 7), cols2[4]); // Update the maximum Check size. if (lzma_check_size(iter.stream.flags->check) > check_max) check_max = lzma_check_size(iter.stream.flags->check); } // Cache the verbosity level to a local variable. const bool detailed = message_verbosity_get() >= V_DEBUG; // Information collected from Block Headers block_header_info bhi; // Print information about the Blocks but only if there is // at least one Block. if (lzma_index_block_count(xfi->idx) > 0) { // Calculate the width of the CheckVal field. const int checkval_width = my_max(8, 2 * check_max); // TRANSLATORS: The second line is column headings. All // except Check are right aligned; Check is left aligned. printf(_(" Blocks:\n Stream Block" " CompOffset UncompOffset" " TotalSize UncompSize Ratio Check")); if (detailed) { // TRANSLATORS: These are additional column headings // for the most verbose listing mode. CheckVal // (Check value), Flags, and Filters are left aligned. // Header (Block Header Size), CompSize, and MemUsage // are right aligned. %*s is replaced with 0-120 // spaces to make the CheckVal column wide enough. // Test with "xz -lvv foo.xz". printf(_(" CheckVal %*s Header Flags " "CompSize MemUsage Filters"), checkval_width - 8, ""); } putchar('\n'); lzma_index_iter_init(&iter, xfi->idx); // Iterate over the Blocks. while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)) { if (detailed && parse_details(pair, &iter, &bhi, xfi)) return true; const char *cols1[4] = { uint64_to_str(iter.stream.number, 0), uint64_to_str( iter.block.number_in_stream, 1), uint64_to_str( iter.block.compressed_file_offset, 2), uint64_to_str( iter.block.uncompressed_file_offset, 3) }; printf(" %*s %*s %*s %*s ", tuklib_mbstr_fw(cols1[0], 6), cols1[0], tuklib_mbstr_fw(cols1[1], 9), cols1[1], tuklib_mbstr_fw(cols1[2], 15), cols1[2], tuklib_mbstr_fw(cols1[3], 15), cols1[3]); const char *cols2[4] = { uint64_to_str(iter.block.total_size, 0), uint64_to_str(iter.block.uncompressed_size, 1), get_ratio(iter.block.total_size, iter.block.uncompressed_size), _(check_names[iter.stream.flags->check]) }; printf("%*s %*s %*s %-*s", tuklib_mbstr_fw(cols2[0], 15), cols2[0], tuklib_mbstr_fw(cols2[1], 15), cols2[1], tuklib_mbstr_fw(cols2[2], 5), cols2[2], tuklib_mbstr_fw(cols2[3], detailed ? 11 : 1), cols2[3]); if (detailed) { const lzma_vli compressed_size = iter.block.unpadded_size - bhi.header_size - lzma_check_size( iter.stream.flags->check); const char *cols3[6] = { check_value, uint64_to_str(bhi.header_size, 0), bhi.flags, uint64_to_str(compressed_size, 1), uint64_to_str( round_up_to_mib(bhi.memusage), 2), bhi.filter_chain }; // Show MiB for memory usage, because it // is the only size which is not in bytes. printf("%-*s %*s %-5s %*s %*s MiB %s", checkval_width, cols3[0], tuklib_mbstr_fw(cols3[1], 6), cols3[1], cols3[2], tuklib_mbstr_fw(cols3[3], 15), cols3[3], tuklib_mbstr_fw(cols3[4], 7), cols3[4], cols3[5]); } putchar('\n'); } } if (detailed) { printf(_(" Memory needed: %s MiB\n"), uint64_to_str( round_up_to_mib(xfi->memusage_max), 0)); printf(_(" Sizes in headers: %s\n"), xfi->all_have_sizes ? _("Yes") : _("No")); printf(_(" Minimum XZ Utils version: %s\n"), xz_ver_to_str(xfi->min_version)); } return false; }
int main(int argc, char **argv) { #if defined(_WIN32) && !defined(__CYGWIN__) InitializeCriticalSection(&exit_status_cs); #endif // Set up the progname variable. tuklib_progname_init(argv); // Initialize the file I/O. This makes sure that // stdin, stdout, and stderr are something valid. io_init(); // Set up the locale and message translations. tuklib_gettext_init(PACKAGE, LOCALEDIR); // Initialize handling of error/warning/other messages. message_init(); // Set hardware-dependent default values. These can be overriden // on the command line, thus this must be done before args_parse(). hardware_init(); // Parse the command line arguments and get an array of filenames. // This doesn't return if something is wrong with the command line // arguments. If there are no arguments, one filename ("-") is still // returned to indicate stdin. args_info args; args_parse(&args, argc, argv); if (opt_mode != MODE_LIST && opt_robot) message_fatal(_("Compression and decompression with --robot " "are not supported yet.")); // Tell the message handling code how many input files there are if // we know it. This way the progress indicator can show it. if (args.files_name != NULL) message_set_files(0); else message_set_files(args.arg_count); // Refuse to write compressed data to standard output if it is // a terminal. if (opt_mode == MODE_COMPRESS) { if (opt_stdout || (args.arg_count == 1 && strcmp(args.arg_names[0], "-") == 0)) { if (is_tty_stdout()) { message_try_help(); tuklib_exit(E_ERROR, E_ERROR, false); } } } // Set up the signal handlers. We don't need these before we // start the actual action and not in --list mode, so this is // done after parsing the command line arguments. // // It's good to keep signal handlers in normal compression and // decompression modes even when only writing to stdout, because // we might need to restore O_APPEND flag on stdout before exiting. // In --test mode, signal handlers aren't really needed, but let's // keep them there for consistency with normal decompression. if (opt_mode != MODE_LIST) signals_init(); // coder_run() handles compression, decompression, and testing. // list_file() is for --list. void (*run)(const char *filename) = opt_mode == MODE_LIST ? &list_file : &coder_run; // Process the files given on the command line. Note that if no names // were given, args_parse() gave us a fake "-" filename. for (size_t i = 0; i < args.arg_count && !user_abort; ++i) { if (strcmp("-", args.arg_names[i]) == 0) { // Processing from stdin to stdout. Check that we // aren't writing compressed data to a terminal or // reading it from a terminal. if (opt_mode == MODE_COMPRESS) { if (is_tty_stdout()) continue; } else if (is_tty_stdin()) { continue; } // It doesn't make sense to compress data from stdin // if we are supposed to read filenames from stdin // too (enabled with --files or --files0). if (args.files_name == stdin_filename) { message_error(_("Cannot read data from " "standard input when " "reading filenames " "from standard input")); continue; } // Replace the "-" with a special pointer, which is // recognized by coder_run() and other things. // This way error messages get a proper filename // string and the code still knows that it is // handling the special case of stdin. args.arg_names[i] = (char *)stdin_filename; } // Do the actual compression or decompression. run(args.arg_names[i]); } // If --files or --files0 was used, process the filenames from the // given file or stdin. Note that here we don't consider "-" to // indicate stdin like we do with the command line arguments. if (args.files_name != NULL) { // read_name() checks for user_abort so we don't need to // check it as loop termination condition. while (true) { const char *name = read_name(&args); if (name == NULL) break; // read_name() doesn't return empty names. assert(name[0] != '\0'); run(name); } if (args.files_name != stdin_filename) (void)fclose(args.files_file); } // All files have now been handled. If in --list mode, display // the totals before exiting. We don't have signal handlers // enabled in --list mode, so we don't need to check user_abort. if (opt_mode == MODE_LIST) { assert(!user_abort); list_totals(); } #ifndef NDEBUG coder_free(); #endif // If we have got a signal, raise it to kill the program instead // of calling tuklib_exit(). signals_exit(); // Make a local copy of exit_status to keep the Windows code // thread safe. At this point it is fine if we miss the user // pressing C-c and don't set the exit_status to E_ERROR on // Windows. #if defined(_WIN32) && !defined(__CYGWIN__) EnterCriticalSection(&exit_status_cs); #endif enum exit_status_type es = exit_status; #if defined(_WIN32) && !defined(__CYGWIN__) LeaveCriticalSection(&exit_status_cs); #endif // Suppress the exit status indicating a warning if --no-warn // was specified. if (es == E_WARNING && no_warn) es = E_SUCCESS; tuklib_exit(es, E_ERROR, message_verbosity_get() != V_SILENT); }