1207753Smm/////////////////////////////////////////////////////////////////////////////// 2207753Smm// 3207753Smm/// \file message.c 4207753Smm/// \brief Printing messages 5207753Smm// 6207753Smm// Author: Lasse Collin 7207753Smm// 8207753Smm// This file has been put into the public domain. 9207753Smm// You can do whatever you want with this file. 10207753Smm// 11207753Smm/////////////////////////////////////////////////////////////////////////////// 12207753Smm 13207753Smm#include "private.h" 14207753Smm 15207753Smm#include <stdarg.h> 16207753Smm 17207753Smm 18207753Smm/// Number of the current file 19207753Smmstatic unsigned int files_pos = 0; 20207753Smm 21207753Smm/// Total number of input files; zero if unknown. 22207753Smmstatic unsigned int files_total; 23207753Smm 24207753Smm/// Verbosity level 25207753Smmstatic enum message_verbosity verbosity = V_WARNING; 26207753Smm 27207753Smm/// Filename which we will print with the verbose messages 28207753Smmstatic const char *filename; 29207753Smm 30207753Smm/// True once the a filename has been printed to stderr as part of progress 31207753Smm/// message. If automatic progress updating isn't enabled, this becomes true 32207753Smm/// after the first progress message has been printed due to user sending 33207753Smm/// SIGINFO, SIGUSR1, or SIGALRM. Once this variable is true, we will print 34207753Smm/// an empty line before the next filename to make the output more readable. 35207753Smmstatic bool first_filename_printed = false; 36207753Smm 37207753Smm/// This is set to true when we have printed the current filename to stderr 38207753Smm/// as part of a progress message. This variable is useful only if not 39207753Smm/// updating progress automatically: if user sends many SIGINFO, SIGUSR1, or 40207753Smm/// SIGALRM signals, we won't print the name of the same file multiple times. 41207753Smmstatic bool current_filename_printed = false; 42207753Smm 43207753Smm/// True if we should print progress indicator and update it automatically 44207753Smm/// if also verbose >= V_VERBOSE. 45207753Smmstatic bool progress_automatic; 46207753Smm 47207753Smm/// True if message_progress_start() has been called but 48207753Smm/// message_progress_end() hasn't been called yet. 49207753Smmstatic bool progress_started = false; 50207753Smm 51207753Smm/// This is true when a progress message was printed and the cursor is still 52207753Smm/// on the same line with the progress message. In that case, a newline has 53207753Smm/// to be printed before any error messages. 54207753Smmstatic bool progress_active = false; 55207753Smm 56207753Smm/// Pointer to lzma_stream used to do the encoding or decoding. 57207753Smmstatic lzma_stream *progress_strm; 58207753Smm 59207753Smm/// Expected size of the input stream is needed to show completion percentage 60207753Smm/// and estimate remaining time. 61207753Smmstatic uint64_t expected_in_size; 62207753Smm 63207753Smm 64207753Smm// Use alarm() and SIGALRM when they are supported. This has two minor 65207753Smm// advantages over the alternative of polling gettimeofday(): 66207753Smm// - It is possible for the user to send SIGINFO, SIGUSR1, or SIGALRM to 67207753Smm// get intermediate progress information even when --verbose wasn't used 68207753Smm// or stderr is not a terminal. 69207753Smm// - alarm() + SIGALRM seems to have slightly less overhead than polling 70207753Smm// gettimeofday(). 71207753Smm#ifdef SIGALRM 72207753Smm 73215187Smmconst int message_progress_sigs[] = { 74215187Smm SIGALRM, 75215187Smm#ifdef SIGINFO 76215187Smm SIGINFO, 77215187Smm#endif 78215187Smm#ifdef SIGUSR1 79215187Smm SIGUSR1, 80215187Smm#endif 81215187Smm 0 82215187Smm}; 83215187Smm 84207753Smm/// The signal handler for SIGALRM sets this to true. It is set back to false 85207753Smm/// once the progress message has been updated. 86207753Smmstatic volatile sig_atomic_t progress_needs_updating = false; 87207753Smm 88207753Smm/// Signal handler for SIGALRM 89207753Smmstatic void 90223935Smmprogress_signal_handler(int sig lzma_attribute((__unused__))) 91207753Smm{ 92207753Smm progress_needs_updating = true; 93207753Smm return; 94207753Smm} 95207753Smm 96207753Smm#else 97207753Smm 98207753Smm/// This is true when progress message printing is wanted. Using the same 99207753Smm/// variable name as above to avoid some ifdefs. 100207753Smmstatic bool progress_needs_updating = false; 101207753Smm 102207753Smm/// Elapsed time when the next progress message update should be done. 103207753Smmstatic uint64_t progress_next_update; 104207753Smm 105207753Smm#endif 106207753Smm 107207753Smm 108207753Smmextern void 109207753Smmmessage_init(void) 110207753Smm{ 111207753Smm // If --verbose is used, we use a progress indicator if and only 112207753Smm // if stderr is a terminal. If stderr is not a terminal, we print 113207753Smm // verbose information only after finishing the file. As a special 114207753Smm // exception, even if --verbose was not used, user can send SIGALRM 115207753Smm // to make us print progress information once without automatic 116207753Smm // updating. 117207753Smm progress_automatic = isatty(STDERR_FILENO); 118207753Smm 119207753Smm // Commented out because COLUMNS is rarely exported to environment. 120207753Smm // Most users have at least 80 columns anyway, let's think something 121207753Smm // fancy here if enough people complain. 122207753Smm/* 123207753Smm if (progress_automatic) { 124207753Smm // stderr is a terminal. Check the COLUMNS environment 125207753Smm // variable to see if the terminal is wide enough. If COLUMNS 126207753Smm // doesn't exist or it has some unparsable value, we assume 127207753Smm // that the terminal is wide enough. 128207753Smm const char *columns_str = getenv("COLUMNS"); 129207753Smm if (columns_str != NULL) { 130207753Smm char *endptr; 131207753Smm const long columns = strtol(columns_str, &endptr, 10); 132207753Smm if (*endptr != '\0' || columns < 80) 133207753Smm progress_automatic = false; 134207753Smm } 135207753Smm } 136207753Smm*/ 137207753Smm 138207753Smm#ifdef SIGALRM 139207753Smm // Establish the signal handlers which set a flag to tell us that 140215187Smm // progress info should be updated. 141207753Smm struct sigaction sa; 142207753Smm sigemptyset(&sa.sa_mask); 143215187Smm sa.sa_flags = 0; 144207753Smm sa.sa_handler = &progress_signal_handler; 145207753Smm 146215187Smm for (size_t i = 0; message_progress_sigs[i] != 0; ++i) 147215187Smm if (sigaction(message_progress_sigs[i], &sa, NULL)) 148207753Smm message_signal_handler(); 149207753Smm#endif 150207753Smm 151207753Smm return; 152207753Smm} 153207753Smm 154207753Smm 155207753Smmextern void 156207753Smmmessage_verbosity_increase(void) 157207753Smm{ 158207753Smm if (verbosity < V_DEBUG) 159207753Smm ++verbosity; 160207753Smm 161207753Smm return; 162207753Smm} 163207753Smm 164207753Smm 165207753Smmextern void 166207753Smmmessage_verbosity_decrease(void) 167207753Smm{ 168207753Smm if (verbosity > V_SILENT) 169207753Smm --verbosity; 170207753Smm 171207753Smm return; 172207753Smm} 173207753Smm 174207753Smm 175207753Smmextern enum message_verbosity 176207753Smmmessage_verbosity_get(void) 177207753Smm{ 178207753Smm return verbosity; 179207753Smm} 180207753Smm 181207753Smm 182207753Smmextern void 183207753Smmmessage_set_files(unsigned int files) 184207753Smm{ 185207753Smm files_total = files; 186207753Smm return; 187207753Smm} 188207753Smm 189207753Smm 190207753Smm/// Prints the name of the current file if it hasn't been printed already, 191207753Smm/// except if we are processing exactly one stream from stdin to stdout. 192207753Smm/// I think it looks nicer to not print "(stdin)" when --verbose is used 193207753Smm/// in a pipe and no other files are processed. 194207753Smmstatic void 195207753Smmprint_filename(void) 196207753Smm{ 197244601Smm if (!opt_robot && (files_total != 1 || filename != stdin_filename)) { 198207753Smm signals_block(); 199207753Smm 200207753Smm FILE *file = opt_mode == MODE_LIST ? stdout : stderr; 201207753Smm 202207753Smm // If a file was already processed, put an empty line 203207753Smm // before the next filename to improve readability. 204207753Smm if (first_filename_printed) 205207753Smm fputc('\n', file); 206207753Smm 207207753Smm first_filename_printed = true; 208207753Smm current_filename_printed = true; 209207753Smm 210207753Smm // If we don't know how many files there will be due 211207753Smm // to usage of --files or --files0. 212207753Smm if (files_total == 0) 213207753Smm fprintf(file, "%s (%u)\n", filename, 214207753Smm files_pos); 215207753Smm else 216207753Smm fprintf(file, "%s (%u/%u)\n", filename, 217207753Smm files_pos, files_total); 218207753Smm 219207753Smm signals_unblock(); 220207753Smm } 221207753Smm 222207753Smm return; 223207753Smm} 224207753Smm 225207753Smm 226207753Smmextern void 227207753Smmmessage_filename(const char *src_name) 228207753Smm{ 229207753Smm // Start numbering the files starting from one. 230207753Smm ++files_pos; 231207753Smm filename = src_name; 232207753Smm 233207753Smm if (verbosity >= V_VERBOSE 234207753Smm && (progress_automatic || opt_mode == MODE_LIST)) 235207753Smm print_filename(); 236207753Smm else 237207753Smm current_filename_printed = false; 238207753Smm 239207753Smm return; 240207753Smm} 241207753Smm 242207753Smm 243207753Smmextern void 244207753Smmmessage_progress_start(lzma_stream *strm, uint64_t in_size) 245207753Smm{ 246207753Smm // Store the pointer to the lzma_stream used to do the coding. 247207753Smm // It is needed to find out the position in the stream. 248207753Smm progress_strm = strm; 249207753Smm 250292588Sdelphij // Store the expected size of the file. If we aren't printing any 251292588Sdelphij // statistics, then is will be unused. But since it is possible 252292588Sdelphij // that the user sends us a signal to show statistics, we need 253292588Sdelphij // to have it available anyway. 254207753Smm expected_in_size = in_size; 255207753Smm 256207753Smm // Indicate that progress info may need to be printed before 257207753Smm // printing error messages. 258207753Smm progress_started = true; 259207753Smm 260207753Smm // If progress indicator is wanted, print the filename and possibly 261207753Smm // the file count now. 262207753Smm if (verbosity >= V_VERBOSE && progress_automatic) { 263207753Smm // Start the timer to display the first progress message 264207753Smm // after one second. An alternative would be to show the 265207753Smm // first message almost immediately, but delaying by one 266207753Smm // second looks better to me, since extremely early 267207753Smm // progress info is pretty much useless. 268207753Smm#ifdef SIGALRM 269207753Smm // First disable a possibly existing alarm. 270207753Smm alarm(0); 271207753Smm progress_needs_updating = false; 272207753Smm alarm(1); 273207753Smm#else 274207753Smm progress_needs_updating = true; 275292588Sdelphij progress_next_update = 1000; 276207753Smm#endif 277207753Smm } 278207753Smm 279207753Smm return; 280207753Smm} 281207753Smm 282207753Smm 283207753Smm/// Make the string indicating completion percentage. 284207753Smmstatic const char * 285207753Smmprogress_percentage(uint64_t in_pos) 286207753Smm{ 287207753Smm // If the size of the input file is unknown or the size told us is 288207753Smm // clearly wrong since we have processed more data than the alleged 289207753Smm // size of the file, show a static string indicating that we have 290207753Smm // no idea of the completion percentage. 291207753Smm if (expected_in_size == 0 || in_pos > expected_in_size) 292207753Smm return "--- %"; 293207753Smm 294207753Smm // Never show 100.0 % before we actually are finished. 295207753Smm double percentage = (double)(in_pos) / (double)(expected_in_size) 296207753Smm * 99.9; 297207753Smm 298213700Smm // Use big enough buffer to hold e.g. a multibyte decimal point. 299213700Smm static char buf[16]; 300207753Smm snprintf(buf, sizeof(buf), "%.1f %%", percentage); 301207753Smm 302207753Smm return buf; 303207753Smm} 304207753Smm 305207753Smm 306207753Smm/// Make the string containing the amount of input processed, amount of 307207753Smm/// output produced, and the compression ratio. 308207753Smmstatic const char * 309207753Smmprogress_sizes(uint64_t compressed_pos, uint64_t uncompressed_pos, bool final) 310207753Smm{ 311213700Smm // Use big enough buffer to hold e.g. a multibyte thousand separators. 312213700Smm static char buf[128]; 313207753Smm char *pos = buf; 314207753Smm size_t left = sizeof(buf); 315207753Smm 316207753Smm // Print the sizes. If this the final message, use more reasonable 317207753Smm // units than MiB if the file was small. 318207753Smm const enum nicestr_unit unit_min = final ? NICESTR_B : NICESTR_MIB; 319207753Smm my_snprintf(&pos, &left, "%s / %s", 320207753Smm uint64_to_nicestr(compressed_pos, 321207753Smm unit_min, NICESTR_TIB, false, 0), 322207753Smm uint64_to_nicestr(uncompressed_pos, 323207753Smm unit_min, NICESTR_TIB, false, 1)); 324207753Smm 325207753Smm // Avoid division by zero. If we cannot calculate the ratio, set 326207753Smm // it to some nice number greater than 10.0 so that it gets caught 327207753Smm // in the next if-clause. 328207753Smm const double ratio = uncompressed_pos > 0 329207753Smm ? (double)(compressed_pos) / (double)(uncompressed_pos) 330207753Smm : 16.0; 331207753Smm 332207753Smm // If the ratio is very bad, just indicate that it is greater than 333207753Smm // 9.999. This way the length of the ratio field stays fixed. 334207753Smm if (ratio > 9.999) 335207753Smm snprintf(pos, left, " > %.3f", 9.999); 336207753Smm else 337207753Smm snprintf(pos, left, " = %.3f", ratio); 338207753Smm 339207753Smm return buf; 340207753Smm} 341207753Smm 342207753Smm 343207753Smm/// Make the string containing the processing speed of uncompressed data. 344207753Smmstatic const char * 345207753Smmprogress_speed(uint64_t uncompressed_pos, uint64_t elapsed) 346207753Smm{ 347207753Smm // Don't print the speed immediately, since the early values look 348207753Smm // somewhat random. 349292588Sdelphij if (elapsed < 3000) 350207753Smm return ""; 351207753Smm 352207753Smm static const char unit[][8] = { 353207753Smm "KiB/s", 354207753Smm "MiB/s", 355207753Smm "GiB/s", 356207753Smm }; 357207753Smm 358207753Smm size_t unit_index = 0; 359207753Smm 360207753Smm // Calculate the speed as KiB/s. 361207753Smm double speed = (double)(uncompressed_pos) 362292588Sdelphij / ((double)(elapsed) * (1024.0 / 1000.0)); 363207753Smm 364207753Smm // Adjust the unit of the speed if needed. 365207753Smm while (speed > 999.0) { 366207753Smm speed /= 1024.0; 367207753Smm if (++unit_index == ARRAY_SIZE(unit)) 368207753Smm return ""; // Way too fast ;-) 369207753Smm } 370207753Smm 371207753Smm // Use decimal point only if the number is small. Examples: 372207753Smm // - 0.1 KiB/s 373207753Smm // - 9.9 KiB/s 374207753Smm // - 99 KiB/s 375207753Smm // - 999 KiB/s 376213700Smm // Use big enough buffer to hold e.g. a multibyte decimal point. 377213700Smm static char buf[16]; 378207753Smm snprintf(buf, sizeof(buf), "%.*f %s", 379207753Smm speed > 9.9 ? 0 : 1, speed, unit[unit_index]); 380207753Smm return buf; 381207753Smm} 382207753Smm 383207753Smm 384292588Sdelphij/// Make a string indicating elapsed time. The format is either 385207753Smm/// M:SS or H:MM:SS depending on if the time is an hour or more. 386207753Smmstatic const char * 387292588Sdelphijprogress_time(uint64_t mseconds) 388207753Smm{ 389207753Smm // 9999 hours = 416 days 390207753Smm static char buf[sizeof("9999:59:59")]; 391207753Smm 392292588Sdelphij // 32-bit variable is enough for elapsed time (136 years). 393292588Sdelphij uint32_t seconds = (uint32_t)(mseconds / 1000); 394207753Smm 395207753Smm // Don't show anything if the time is zero or ridiculously big. 396207753Smm if (seconds == 0 || seconds > ((9999 * 60) + 59) * 60 + 59) 397207753Smm return ""; 398207753Smm 399207753Smm uint32_t minutes = seconds / 60; 400207753Smm seconds %= 60; 401207753Smm 402207753Smm if (minutes >= 60) { 403207753Smm const uint32_t hours = minutes / 60; 404207753Smm minutes %= 60; 405207753Smm snprintf(buf, sizeof(buf), 406207753Smm "%" PRIu32 ":%02" PRIu32 ":%02" PRIu32, 407207753Smm hours, minutes, seconds); 408207753Smm } else { 409207753Smm snprintf(buf, sizeof(buf), "%" PRIu32 ":%02" PRIu32, 410207753Smm minutes, seconds); 411207753Smm } 412207753Smm 413207753Smm return buf; 414207753Smm} 415207753Smm 416207753Smm 417207753Smm/// Return a string containing estimated remaining time when 418207753Smm/// reasonably possible. 419207753Smmstatic const char * 420207753Smmprogress_remaining(uint64_t in_pos, uint64_t elapsed) 421207753Smm{ 422207753Smm // Don't show the estimated remaining time when it wouldn't 423207753Smm // make sense: 424207753Smm // - Input size is unknown. 425207753Smm // - Input has grown bigger since we started (de)compressing. 426207753Smm // - We haven't processed much data yet, so estimate would be 427207753Smm // too inaccurate. 428207753Smm // - Only a few seconds has passed since we started (de)compressing, 429207753Smm // so estimate would be too inaccurate. 430207753Smm if (expected_in_size == 0 || in_pos > expected_in_size 431292588Sdelphij || in_pos < (UINT64_C(1) << 19) || elapsed < 8000) 432207753Smm return ""; 433207753Smm 434207753Smm // Calculate the estimate. Don't give an estimate of zero seconds, 435207753Smm // since it is possible that all the input has been already passed 436207753Smm // to the library, but there is still quite a bit of output pending. 437207753Smm uint32_t remaining = (double)(expected_in_size - in_pos) 438292588Sdelphij * ((double)(elapsed) / 1000.0) / (double)(in_pos); 439207753Smm if (remaining < 1) 440207753Smm remaining = 1; 441207753Smm 442207753Smm static char buf[sizeof("9 h 55 min")]; 443207753Smm 444207753Smm // Select appropriate precision for the estimated remaining time. 445207753Smm if (remaining <= 10) { 446207753Smm // A maximum of 10 seconds remaining. 447207753Smm // Show the number of seconds as is. 448207753Smm snprintf(buf, sizeof(buf), "%" PRIu32 " s", remaining); 449207753Smm 450207753Smm } else if (remaining <= 50) { 451207753Smm // A maximum of 50 seconds remaining. 452207753Smm // Round up to the next multiple of five seconds. 453207753Smm remaining = (remaining + 4) / 5 * 5; 454207753Smm snprintf(buf, sizeof(buf), "%" PRIu32 " s", remaining); 455207753Smm 456207753Smm } else if (remaining <= 590) { 457207753Smm // A maximum of 9 minutes and 50 seconds remaining. 458207753Smm // Round up to the next multiple of ten seconds. 459207753Smm remaining = (remaining + 9) / 10 * 10; 460207753Smm snprintf(buf, sizeof(buf), "%" PRIu32 " min %" PRIu32 " s", 461207753Smm remaining / 60, remaining % 60); 462207753Smm 463207753Smm } else if (remaining <= 59 * 60) { 464207753Smm // A maximum of 59 minutes remaining. 465207753Smm // Round up to the next multiple of a minute. 466207753Smm remaining = (remaining + 59) / 60; 467207753Smm snprintf(buf, sizeof(buf), "%" PRIu32 " min", remaining); 468207753Smm 469207753Smm } else if (remaining <= 9 * 3600 + 50 * 60) { 470207753Smm // A maximum of 9 hours and 50 minutes left. 471207753Smm // Round up to the next multiple of ten minutes. 472207753Smm remaining = (remaining + 599) / 600 * 10; 473207753Smm snprintf(buf, sizeof(buf), "%" PRIu32 " h %" PRIu32 " min", 474207753Smm remaining / 60, remaining % 60); 475207753Smm 476207753Smm } else if (remaining <= 23 * 3600) { 477207753Smm // A maximum of 23 hours remaining. 478207753Smm // Round up to the next multiple of an hour. 479207753Smm remaining = (remaining + 3599) / 3600; 480207753Smm snprintf(buf, sizeof(buf), "%" PRIu32 " h", remaining); 481207753Smm 482207753Smm } else if (remaining <= 9 * 24 * 3600 + 23 * 3600) { 483207753Smm // A maximum of 9 days and 23 hours remaining. 484207753Smm // Round up to the next multiple of an hour. 485207753Smm remaining = (remaining + 3599) / 3600; 486207753Smm snprintf(buf, sizeof(buf), "%" PRIu32 " d %" PRIu32 " h", 487207753Smm remaining / 24, remaining % 24); 488207753Smm 489207753Smm } else if (remaining <= 999 * 24 * 3600) { 490207753Smm // A maximum of 999 days remaining. ;-) 491207753Smm // Round up to the next multiple of a day. 492207753Smm remaining = (remaining + 24 * 3600 - 1) / (24 * 3600); 493207753Smm snprintf(buf, sizeof(buf), "%" PRIu32 " d", remaining); 494207753Smm 495207753Smm } else { 496207753Smm // The estimated remaining time is too big. Don't show it. 497207753Smm return ""; 498207753Smm } 499207753Smm 500207753Smm return buf; 501207753Smm} 502207753Smm 503207753Smm 504292588Sdelphij/// Get how much uncompressed and compressed data has been processed. 505207753Smmstatic void 506207753Smmprogress_pos(uint64_t *in_pos, 507207753Smm uint64_t *compressed_pos, uint64_t *uncompressed_pos) 508207753Smm{ 509292588Sdelphij uint64_t out_pos; 510292588Sdelphij lzma_get_progress(progress_strm, in_pos, &out_pos); 511207753Smm 512292588Sdelphij // It cannot have processed more input than it has been given. 513292588Sdelphij assert(*in_pos <= progress_strm->total_in); 514292588Sdelphij 515292588Sdelphij // It cannot have produced more output than it claims to have ready. 516292588Sdelphij assert(out_pos >= progress_strm->total_out); 517292588Sdelphij 518207753Smm if (opt_mode == MODE_COMPRESS) { 519292588Sdelphij *compressed_pos = out_pos; 520292588Sdelphij *uncompressed_pos = *in_pos; 521207753Smm } else { 522292588Sdelphij *compressed_pos = *in_pos; 523292588Sdelphij *uncompressed_pos = out_pos; 524207753Smm } 525207753Smm 526207753Smm return; 527207753Smm} 528207753Smm 529207753Smm 530207753Smmextern void 531207753Smmmessage_progress_update(void) 532207753Smm{ 533207753Smm if (!progress_needs_updating) 534207753Smm return; 535207753Smm 536207753Smm // Calculate how long we have been processing this file. 537292588Sdelphij const uint64_t elapsed = mytime_get_elapsed(); 538207753Smm 539207753Smm#ifndef SIGALRM 540207753Smm if (progress_next_update > elapsed) 541207753Smm return; 542207753Smm 543292588Sdelphij progress_next_update = elapsed + 1000; 544207753Smm#endif 545207753Smm 546207753Smm // Get our current position in the stream. 547207753Smm uint64_t in_pos; 548207753Smm uint64_t compressed_pos; 549207753Smm uint64_t uncompressed_pos; 550207753Smm progress_pos(&in_pos, &compressed_pos, &uncompressed_pos); 551207753Smm 552207753Smm // Block signals so that fprintf() doesn't get interrupted. 553207753Smm signals_block(); 554207753Smm 555207753Smm // Print the filename if it hasn't been printed yet. 556207753Smm if (!current_filename_printed) 557207753Smm print_filename(); 558207753Smm 559207753Smm // Print the actual progress message. The idea is that there is at 560207753Smm // least three spaces between the fields in typical situations, but 561207753Smm // even in rare situations there is at least one space. 562213700Smm const char *cols[5] = { 563207753Smm progress_percentage(in_pos), 564207753Smm progress_sizes(compressed_pos, uncompressed_pos, false), 565207753Smm progress_speed(uncompressed_pos, elapsed), 566207753Smm progress_time(elapsed), 567213700Smm progress_remaining(in_pos, elapsed), 568213700Smm }; 569213700Smm fprintf(stderr, "\r %*s %*s %*s %10s %10s\r", 570213700Smm tuklib_mbstr_fw(cols[0], 6), cols[0], 571213700Smm tuklib_mbstr_fw(cols[1], 35), cols[1], 572213700Smm tuklib_mbstr_fw(cols[2], 9), cols[2], 573213700Smm cols[3], 574213700Smm cols[4]); 575207753Smm 576207753Smm#ifdef SIGALRM 577207753Smm // Updating the progress info was finished. Reset 578207753Smm // progress_needs_updating to wait for the next SIGALRM. 579207753Smm // 580207753Smm // NOTE: This has to be done before alarm(1) or with (very) bad 581207753Smm // luck we could be setting this to false after the alarm has already 582207753Smm // been triggered. 583207753Smm progress_needs_updating = false; 584207753Smm 585207753Smm if (verbosity >= V_VERBOSE && progress_automatic) { 586207753Smm // Mark that the progress indicator is active, so if an error 587207753Smm // occurs, the error message gets printed cleanly. 588207753Smm progress_active = true; 589207753Smm 590207753Smm // Restart the timer so that progress_needs_updating gets 591207753Smm // set to true after about one second. 592207753Smm alarm(1); 593207753Smm } else { 594207753Smm // The progress message was printed because user had sent us 595207753Smm // SIGALRM. In this case, each progress message is printed 596207753Smm // on its own line. 597207753Smm fputc('\n', stderr); 598207753Smm } 599207753Smm#else 600207753Smm // When SIGALRM isn't supported and we get here, it's always due to 601207753Smm // automatic progress update. We set progress_active here too like 602207753Smm // described above. 603207753Smm assert(verbosity >= V_VERBOSE); 604207753Smm assert(progress_automatic); 605207753Smm progress_active = true; 606207753Smm#endif 607207753Smm 608207753Smm signals_unblock(); 609207753Smm 610207753Smm return; 611207753Smm} 612207753Smm 613207753Smm 614207753Smmstatic void 615207753Smmprogress_flush(bool finished) 616207753Smm{ 617207753Smm if (!progress_started || verbosity < V_VERBOSE) 618207753Smm return; 619207753Smm 620207753Smm uint64_t in_pos; 621207753Smm uint64_t compressed_pos; 622207753Smm uint64_t uncompressed_pos; 623207753Smm progress_pos(&in_pos, &compressed_pos, &uncompressed_pos); 624207753Smm 625207753Smm // Avoid printing intermediate progress info if some error occurs 626207753Smm // in the beginning of the stream. (If something goes wrong later in 627207753Smm // the stream, it is sometimes useful to tell the user where the 628207753Smm // error approximately occurred, especially if the error occurs 629207753Smm // after a time-consuming operation.) 630207753Smm if (!finished && !progress_active 631207753Smm && (compressed_pos == 0 || uncompressed_pos == 0)) 632207753Smm return; 633207753Smm 634207753Smm progress_active = false; 635207753Smm 636292588Sdelphij const uint64_t elapsed = mytime_get_elapsed(); 637207753Smm 638207753Smm signals_block(); 639207753Smm 640207753Smm // When using the auto-updating progress indicator, the final 641207753Smm // statistics are printed in the same format as the progress 642207753Smm // indicator itself. 643207753Smm if (progress_automatic) { 644213700Smm const char *cols[5] = { 645207753Smm finished ? "100 %" : progress_percentage(in_pos), 646207753Smm progress_sizes(compressed_pos, uncompressed_pos, true), 647207753Smm progress_speed(uncompressed_pos, elapsed), 648207753Smm progress_time(elapsed), 649213700Smm finished ? "" : progress_remaining(in_pos, elapsed), 650213700Smm }; 651213700Smm fprintf(stderr, "\r %*s %*s %*s %10s %10s\n", 652213700Smm tuklib_mbstr_fw(cols[0], 6), cols[0], 653213700Smm tuklib_mbstr_fw(cols[1], 35), cols[1], 654213700Smm tuklib_mbstr_fw(cols[2], 9), cols[2], 655213700Smm cols[3], 656213700Smm cols[4]); 657207753Smm } else { 658207753Smm // The filename is always printed. 659207753Smm fprintf(stderr, "%s: ", filename); 660207753Smm 661207753Smm // Percentage is printed only if we didn't finish yet. 662207753Smm if (!finished) { 663207753Smm // Don't print the percentage when it isn't known 664207753Smm // (starts with a dash). 665207753Smm const char *percentage = progress_percentage(in_pos); 666207753Smm if (percentage[0] != '-') 667207753Smm fprintf(stderr, "%s, ", percentage); 668207753Smm } 669207753Smm 670207753Smm // Size information is always printed. 671207753Smm fprintf(stderr, "%s", progress_sizes( 672207753Smm compressed_pos, uncompressed_pos, true)); 673207753Smm 674207753Smm // The speed and elapsed time aren't always shown. 675207753Smm const char *speed = progress_speed(uncompressed_pos, elapsed); 676207753Smm if (speed[0] != '\0') 677207753Smm fprintf(stderr, ", %s", speed); 678207753Smm 679207753Smm const char *elapsed_str = progress_time(elapsed); 680207753Smm if (elapsed_str[0] != '\0') 681207753Smm fprintf(stderr, ", %s", elapsed_str); 682207753Smm 683207753Smm fputc('\n', stderr); 684207753Smm } 685207753Smm 686207753Smm signals_unblock(); 687207753Smm 688207753Smm return; 689207753Smm} 690207753Smm 691207753Smm 692207753Smmextern void 693207753Smmmessage_progress_end(bool success) 694207753Smm{ 695207753Smm assert(progress_started); 696207753Smm progress_flush(success); 697207753Smm progress_started = false; 698207753Smm return; 699207753Smm} 700207753Smm 701207753Smm 702207753Smmstatic void 703207753Smmvmessage(enum message_verbosity v, const char *fmt, va_list ap) 704207753Smm{ 705207753Smm if (v <= verbosity) { 706207753Smm signals_block(); 707207753Smm 708207753Smm progress_flush(false); 709207753Smm 710223935Smm // TRANSLATORS: This is the program name in the beginning 711223935Smm // of the line in messages. Usually it becomes "xz: ". 712223935Smm // This is a translatable string because French needs 713223935Smm // a space before a colon. 714223935Smm fprintf(stderr, _("%s: "), progname); 715207753Smm vfprintf(stderr, fmt, ap); 716207753Smm fputc('\n', stderr); 717207753Smm 718207753Smm signals_unblock(); 719207753Smm } 720207753Smm 721207753Smm return; 722207753Smm} 723207753Smm 724207753Smm 725207753Smmextern void 726207753Smmmessage(enum message_verbosity v, const char *fmt, ...) 727207753Smm{ 728207753Smm va_list ap; 729207753Smm va_start(ap, fmt); 730207753Smm vmessage(v, fmt, ap); 731207753Smm va_end(ap); 732207753Smm return; 733207753Smm} 734207753Smm 735207753Smm 736207753Smmextern void 737207753Smmmessage_warning(const char *fmt, ...) 738207753Smm{ 739207753Smm va_list ap; 740207753Smm va_start(ap, fmt); 741207753Smm vmessage(V_WARNING, fmt, ap); 742207753Smm va_end(ap); 743207753Smm 744207753Smm set_exit_status(E_WARNING); 745207753Smm return; 746207753Smm} 747207753Smm 748207753Smm 749207753Smmextern void 750207753Smmmessage_error(const char *fmt, ...) 751207753Smm{ 752207753Smm va_list ap; 753207753Smm va_start(ap, fmt); 754207753Smm vmessage(V_ERROR, fmt, ap); 755207753Smm va_end(ap); 756207753Smm 757207753Smm set_exit_status(E_ERROR); 758207753Smm return; 759207753Smm} 760207753Smm 761207753Smm 762207753Smmextern void 763207753Smmmessage_fatal(const char *fmt, ...) 764207753Smm{ 765207753Smm va_list ap; 766207753Smm va_start(ap, fmt); 767207753Smm vmessage(V_ERROR, fmt, ap); 768207753Smm va_end(ap); 769207753Smm 770207753Smm tuklib_exit(E_ERROR, E_ERROR, false); 771207753Smm} 772207753Smm 773207753Smm 774207753Smmextern void 775207753Smmmessage_bug(void) 776207753Smm{ 777207753Smm message_fatal(_("Internal error (bug)")); 778207753Smm} 779207753Smm 780207753Smm 781207753Smmextern void 782207753Smmmessage_signal_handler(void) 783207753Smm{ 784207753Smm message_fatal(_("Cannot establish signal handlers")); 785207753Smm} 786207753Smm 787207753Smm 788207753Smmextern const char * 789207753Smmmessage_strm(lzma_ret code) 790207753Smm{ 791207753Smm switch (code) { 792207753Smm case LZMA_NO_CHECK: 793207753Smm return _("No integrity check; not verifying file integrity"); 794207753Smm 795207753Smm case LZMA_UNSUPPORTED_CHECK: 796207753Smm return _("Unsupported type of integrity check; " 797207753Smm "not verifying file integrity"); 798207753Smm 799207753Smm case LZMA_MEM_ERROR: 800207753Smm return strerror(ENOMEM); 801207753Smm 802207753Smm case LZMA_MEMLIMIT_ERROR: 803207753Smm return _("Memory usage limit reached"); 804207753Smm 805207753Smm case LZMA_FORMAT_ERROR: 806207753Smm return _("File format not recognized"); 807207753Smm 808207753Smm case LZMA_OPTIONS_ERROR: 809207753Smm return _("Unsupported options"); 810207753Smm 811207753Smm case LZMA_DATA_ERROR: 812207753Smm return _("Compressed data is corrupt"); 813207753Smm 814207753Smm case LZMA_BUF_ERROR: 815207753Smm return _("Unexpected end of input"); 816207753Smm 817207753Smm case LZMA_OK: 818207753Smm case LZMA_STREAM_END: 819207753Smm case LZMA_GET_CHECK: 820207753Smm case LZMA_PROG_ERROR: 821215187Smm // Without "default", compiler will warn if new constants 822215187Smm // are added to lzma_ret, it is not too easy to forget to 823215187Smm // add the new constants to this function. 824215187Smm break; 825207753Smm } 826207753Smm 827215187Smm return _("Internal error (bug)"); 828207753Smm} 829207753Smm 830207753Smm 831207753Smmextern void 832207753Smmmessage_mem_needed(enum message_verbosity v, uint64_t memusage) 833207753Smm{ 834207753Smm if (v > verbosity) 835207753Smm return; 836207753Smm 837207753Smm // Convert memusage to MiB, rounding up to the next full MiB. 838207753Smm // This way the user can always use the displayed usage as 839207753Smm // the new memory usage limit. (If we rounded to the nearest, 840207753Smm // the user might need to +1 MiB to get high enough limit.) 841207753Smm memusage = round_up_to_mib(memusage); 842207753Smm 843244601Smm uint64_t memlimit = hardware_memlimit_get(opt_mode); 844244601Smm 845244601Smm // Handle the case when there is no memory usage limit. 846244601Smm // This way we don't print a weird message with a huge number. 847244601Smm if (memlimit == UINT64_MAX) { 848244601Smm message(v, _("%s MiB of memory is required. " 849244601Smm "The limiter is disabled."), 850244601Smm uint64_to_str(memusage, 0)); 851244601Smm return; 852244601Smm } 853244601Smm 854213700Smm // With US-ASCII: 855207753Smm // 2^64 with thousand separators + " MiB" suffix + '\0' = 26 + 4 + 1 856213700Smm // But there may be multibyte chars so reserve enough space. 857213700Smm char memlimitstr[128]; 858207753Smm 859207753Smm // Show the memory usage limit as MiB unless it is less than 1 MiB. 860207753Smm // This way it's easy to notice errors where one has typed 861207753Smm // --memory=123 instead of --memory=123MiB. 862207753Smm if (memlimit < (UINT32_C(1) << 20)) { 863207753Smm snprintf(memlimitstr, sizeof(memlimitstr), "%s B", 864207753Smm uint64_to_str(memlimit, 1)); 865207753Smm } else { 866207753Smm // Round up just like with memusage. If this function is 867207753Smm // called for informational purposes (to just show the 868207753Smm // current usage and limit), we should never show that 869207753Smm // the usage is higher than the limit, which would give 870207753Smm // a false impression that the memory usage limit isn't 871207753Smm // properly enforced. 872207753Smm snprintf(memlimitstr, sizeof(memlimitstr), "%s MiB", 873207753Smm uint64_to_str(round_up_to_mib(memlimit), 1)); 874207753Smm } 875207753Smm 876207753Smm message(v, _("%s MiB of memory is required. The limit is %s."), 877207753Smm uint64_to_str(memusage, 0), memlimitstr); 878207753Smm 879207753Smm return; 880207753Smm} 881207753Smm 882207753Smm 883213700Smm/// \brief Convert uint32_t to a nice string for --lzma[12]=dict=SIZE 884213700Smm/// 885213700Smm/// The idea is to use KiB or MiB suffix when possible. 886213700Smmstatic const char * 887213700Smmuint32_to_optstr(uint32_t num) 888213700Smm{ 889213700Smm static char buf[16]; 890213700Smm 891213700Smm if ((num & ((UINT32_C(1) << 20) - 1)) == 0) 892213700Smm snprintf(buf, sizeof(buf), "%" PRIu32 "MiB", num >> 20); 893213700Smm else if ((num & ((UINT32_C(1) << 10) - 1)) == 0) 894213700Smm snprintf(buf, sizeof(buf), "%" PRIu32 "KiB", num >> 10); 895213700Smm else 896213700Smm snprintf(buf, sizeof(buf), "%" PRIu32, num); 897213700Smm 898213700Smm return buf; 899213700Smm} 900213700Smm 901213700Smm 902207753Smmextern void 903213700Smmmessage_filters_to_str(char buf[FILTERS_STR_SIZE], 904213700Smm const lzma_filter *filters, bool all_known) 905207753Smm{ 906213700Smm char *pos = buf; 907213700Smm size_t left = FILTERS_STR_SIZE; 908207753Smm 909207753Smm for (size_t i = 0; filters[i].id != LZMA_VLI_UNKNOWN; ++i) { 910213700Smm // Add the dashes for the filter option. A space is 911213700Smm // needed after the first and later filters. 912213700Smm my_snprintf(&pos, &left, "%s", i == 0 ? "--" : " --"); 913207753Smm 914207753Smm switch (filters[i].id) { 915207753Smm case LZMA_FILTER_LZMA1: 916207753Smm case LZMA_FILTER_LZMA2: { 917207753Smm const lzma_options_lzma *opt = filters[i].options; 918213700Smm const char *mode = NULL; 919213700Smm const char *mf = NULL; 920207753Smm 921213700Smm if (all_known) { 922213700Smm switch (opt->mode) { 923213700Smm case LZMA_MODE_FAST: 924213700Smm mode = "fast"; 925213700Smm break; 926207753Smm 927213700Smm case LZMA_MODE_NORMAL: 928213700Smm mode = "normal"; 929213700Smm break; 930207753Smm 931213700Smm default: 932213700Smm mode = "UNKNOWN"; 933213700Smm break; 934213700Smm } 935207753Smm 936213700Smm switch (opt->mf) { 937213700Smm case LZMA_MF_HC3: 938213700Smm mf = "hc3"; 939213700Smm break; 940207753Smm 941213700Smm case LZMA_MF_HC4: 942213700Smm mf = "hc4"; 943213700Smm break; 944207753Smm 945213700Smm case LZMA_MF_BT2: 946213700Smm mf = "bt2"; 947213700Smm break; 948207753Smm 949213700Smm case LZMA_MF_BT3: 950213700Smm mf = "bt3"; 951213700Smm break; 952207753Smm 953213700Smm case LZMA_MF_BT4: 954213700Smm mf = "bt4"; 955213700Smm break; 956207753Smm 957213700Smm default: 958213700Smm mf = "UNKNOWN"; 959213700Smm break; 960213700Smm } 961207753Smm } 962207753Smm 963213700Smm // Add the filter name and dictionary size, which 964213700Smm // is always known. 965213700Smm my_snprintf(&pos, &left, "lzma%c=dict=%s", 966213700Smm filters[i].id == LZMA_FILTER_LZMA2 967213700Smm ? '2' : '1', 968213700Smm uint32_to_optstr(opt->dict_size)); 969213700Smm 970213700Smm // With LZMA1 also lc/lp/pb are known when 971213700Smm // decompressing, but this function is never 972213700Smm // used to print information about .lzma headers. 973213700Smm assert(filters[i].id == LZMA_FILTER_LZMA2 974213700Smm || all_known); 975213700Smm 976213700Smm // Print the rest of the options, which are known 977213700Smm // only when compressing. 978213700Smm if (all_known) 979213700Smm my_snprintf(&pos, &left, 980207753Smm ",lc=%" PRIu32 ",lp=%" PRIu32 981207753Smm ",pb=%" PRIu32 982207753Smm ",mode=%s,nice=%" PRIu32 ",mf=%s" 983207753Smm ",depth=%" PRIu32, 984207753Smm opt->lc, opt->lp, opt->pb, 985207753Smm mode, opt->nice_len, mf, opt->depth); 986207753Smm break; 987207753Smm } 988207753Smm 989207753Smm case LZMA_FILTER_X86: 990207753Smm case LZMA_FILTER_POWERPC: 991207753Smm case LZMA_FILTER_IA64: 992207753Smm case LZMA_FILTER_ARM: 993207753Smm case LZMA_FILTER_ARMTHUMB: 994213700Smm case LZMA_FILTER_SPARC: { 995213700Smm static const char bcj_names[][9] = { 996213700Smm "x86", 997213700Smm "powerpc", 998213700Smm "ia64", 999213700Smm "arm", 1000213700Smm "armthumb", 1001213700Smm "sparc", 1002213700Smm }; 1003207753Smm 1004213700Smm const lzma_options_bcj *opt = filters[i].options; 1005213700Smm my_snprintf(&pos, &left, "%s", bcj_names[filters[i].id 1006213700Smm - LZMA_FILTER_X86]); 1007213700Smm 1008213700Smm // Show the start offset only when really needed. 1009213700Smm if (opt != NULL && opt->start_offset != 0) 1010213700Smm my_snprintf(&pos, &left, "=start=%" PRIu32, 1011213700Smm opt->start_offset); 1012213700Smm 1013207753Smm break; 1014213700Smm } 1015207753Smm 1016207753Smm case LZMA_FILTER_DELTA: { 1017207753Smm const lzma_options_delta *opt = filters[i].options; 1018213700Smm my_snprintf(&pos, &left, "delta=dist=%" PRIu32, 1019213700Smm opt->dist); 1020207753Smm break; 1021207753Smm } 1022207753Smm 1023207753Smm default: 1024213700Smm // This should be possible only if liblzma is 1025213700Smm // newer than the xz tool. 1026213700Smm my_snprintf(&pos, &left, "UNKNOWN"); 1027207753Smm break; 1028207753Smm } 1029207753Smm } 1030207753Smm 1031207753Smm return; 1032207753Smm} 1033207753Smm 1034207753Smm 1035207753Smmextern void 1036213700Smmmessage_filters_show(enum message_verbosity v, const lzma_filter *filters) 1037213700Smm{ 1038213700Smm if (v > verbosity) 1039213700Smm return; 1040213700Smm 1041213700Smm char buf[FILTERS_STR_SIZE]; 1042213700Smm message_filters_to_str(buf, filters, true); 1043213700Smm fprintf(stderr, _("%s: Filter chain: %s\n"), progname, buf); 1044213700Smm return; 1045213700Smm} 1046213700Smm 1047213700Smm 1048213700Smmextern void 1049207753Smmmessage_try_help(void) 1050207753Smm{ 1051207753Smm // Print this with V_WARNING instead of V_ERROR to prevent it from 1052207753Smm // showing up when --quiet has been specified. 1053207753Smm message(V_WARNING, _("Try `%s --help' for more information."), 1054207753Smm progname); 1055207753Smm return; 1056207753Smm} 1057207753Smm 1058207753Smm 1059207753Smmextern void 1060207753Smmmessage_version(void) 1061207753Smm{ 1062207753Smm // It is possible that liblzma version is different than the command 1063207753Smm // line tool version, so print both. 1064207753Smm if (opt_robot) { 1065213700Smm printf("XZ_VERSION=%" PRIu32 "\nLIBLZMA_VERSION=%" PRIu32 "\n", 1066207753Smm LZMA_VERSION, lzma_version_number()); 1067207753Smm } else { 1068207753Smm printf("xz (" PACKAGE_NAME ") " LZMA_VERSION_STRING "\n"); 1069207753Smm printf("liblzma %s\n", lzma_version_string()); 1070207753Smm } 1071207753Smm 1072207753Smm tuklib_exit(E_SUCCESS, E_ERROR, verbosity != V_SILENT); 1073207753Smm} 1074207753Smm 1075207753Smm 1076207753Smmextern void 1077207753Smmmessage_help(bool long_help) 1078207753Smm{ 1079207753Smm printf(_("Usage: %s [OPTION]... [FILE]...\n" 1080207753Smm "Compress or decompress FILEs in the .xz format.\n\n"), 1081207753Smm progname); 1082207753Smm 1083213700Smm // NOTE: The short help doesn't currently have options that 1084213700Smm // take arguments. 1085213700Smm if (long_help) 1086213700Smm puts(_("Mandatory arguments to long options are mandatory " 1087213700Smm "for short options too.\n")); 1088207753Smm 1089207753Smm if (long_help) 1090207753Smm puts(_(" Operation mode:\n")); 1091207753Smm 1092207753Smm puts(_( 1093207753Smm" -z, --compress force compression\n" 1094207753Smm" -d, --decompress force decompression\n" 1095207753Smm" -t, --test test compressed file integrity\n" 1096213700Smm" -l, --list list information about .xz files")); 1097207753Smm 1098207753Smm if (long_help) 1099207753Smm puts(_("\n Operation modifiers:\n")); 1100207753Smm 1101207753Smm puts(_( 1102207753Smm" -k, --keep keep (don't delete) input files\n" 1103207753Smm" -f, --force force overwrite of output file and (de)compress links\n" 1104207753Smm" -c, --stdout write to standard output and don't delete input files")); 1105207753Smm 1106292588Sdelphij if (long_help) { 1107207753Smm puts(_( 1108292588Sdelphij" --single-stream decompress only the first stream, and silently\n" 1109292588Sdelphij" ignore possible remaining input data")); 1110292588Sdelphij puts(_( 1111207753Smm" --no-sparse do not create sparse files when decompressing\n" 1112207753Smm" -S, --suffix=.SUF use the suffix `.SUF' on compressed files\n" 1113213700Smm" --files[=FILE] read filenames to process from FILE; if FILE is\n" 1114207753Smm" omitted, filenames are read from the standard input;\n" 1115207753Smm" filenames must be terminated with the newline character\n" 1116213700Smm" --files0[=FILE] like --files but use the null character as terminator")); 1117292588Sdelphij } 1118207753Smm 1119207753Smm if (long_help) { 1120207753Smm puts(_("\n Basic file format and compression options:\n")); 1121207753Smm puts(_( 1122207753Smm" -F, --format=FMT file format to encode or decode; possible values are\n" 1123207753Smm" `auto' (default), `xz', `lzma', and `raw'\n" 1124213700Smm" -C, --check=CHECK integrity check type: `none' (use with caution),\n" 1125213700Smm" `crc32', `crc64' (default), or `sha256'")); 1126292588Sdelphij puts(_( 1127292588Sdelphij" --ignore-check don't verify the integrity check when decompressing")); 1128207753Smm } 1129207753Smm 1130207753Smm puts(_( 1131213700Smm" -0 ... -9 compression preset; default is 6; take compressor *and*\n" 1132213700Smm" decompressor memory usage into account before using 7-9!")); 1133207753Smm 1134207753Smm puts(_( 1135213700Smm" -e, --extreme try to improve compression ratio by using more CPU time;\n" 1136213700Smm" does not affect decompressor memory requirements")); 1137207753Smm 1138292588Sdelphij puts(_( 1139292588Sdelphij" -T, --threads=NUM use at most NUM threads; the default is 1; set to 0\n" 1140292588Sdelphij" to use as many threads as there are processor cores")); 1141292588Sdelphij 1142213700Smm if (long_help) { 1143292588Sdelphij puts(_( 1144292588Sdelphij" --block-size=SIZE\n" 1145292588Sdelphij" start a new .xz block after every SIZE bytes of input;\n" 1146292588Sdelphij" use this to set the block size for threaded compression")); 1147292588Sdelphij puts(_( 1148292588Sdelphij" --block-list=SIZES\n" 1149292588Sdelphij" start a new .xz block after the given comma-separated\n" 1150292588Sdelphij" intervals of uncompressed data")); 1151292588Sdelphij puts(_( 1152292588Sdelphij" --flush-timeout=TIMEOUT\n" 1153292588Sdelphij" when compressing, if more than TIMEOUT milliseconds has\n" 1154292588Sdelphij" passed since the previous flush and reading more input\n" 1155292588Sdelphij" would block, all pending data is flushed out" 1156292588Sdelphij )); 1157207753Smm puts(_( // xgettext:no-c-format 1158213700Smm" --memlimit-compress=LIMIT\n" 1159213700Smm" --memlimit-decompress=LIMIT\n" 1160213700Smm" -M, --memlimit=LIMIT\n" 1161213700Smm" set memory usage limit for compression, decompression,\n" 1162213700Smm" or both; LIMIT is in bytes, % of RAM, or 0 for defaults")); 1163207753Smm 1164213700Smm puts(_( 1165213700Smm" --no-adjust if compression settings exceed the memory usage limit,\n" 1166213700Smm" give an error instead of adjusting the settings downwards")); 1167213700Smm } 1168213700Smm 1169207753Smm if (long_help) { 1170207753Smm puts(_( 1171207753Smm"\n Custom filter chain for compression (alternative for using presets):")); 1172207753Smm 1173207753Smm#if defined(HAVE_ENCODER_LZMA1) || defined(HAVE_DECODER_LZMA1) \ 1174207753Smm || defined(HAVE_ENCODER_LZMA2) || defined(HAVE_DECODER_LZMA2) 1175213700Smm // TRANSLATORS: The word "literal" in "literal context bits" 1176213700Smm // means how many "context bits" to use when encoding 1177213700Smm // literals. A literal is a single 8-bit byte. It doesn't 1178213700Smm // mean "literally" here. 1179207753Smm puts(_( 1180207753Smm"\n" 1181207753Smm" --lzma1[=OPTS] LZMA1 or LZMA2; OPTS is a comma-separated list of zero or\n" 1182207753Smm" --lzma2[=OPTS] more of the following options (valid values; default):\n" 1183213700Smm" preset=PRE reset options to a preset (0-9[e])\n" 1184207753Smm" dict=NUM dictionary size (4KiB - 1536MiB; 8MiB)\n" 1185207753Smm" lc=NUM number of literal context bits (0-4; 3)\n" 1186207753Smm" lp=NUM number of literal position bits (0-4; 0)\n" 1187207753Smm" pb=NUM number of position bits (0-4; 2)\n" 1188207753Smm" mode=MODE compression mode (fast, normal; normal)\n" 1189207753Smm" nice=NUM nice length of a match (2-273; 64)\n" 1190207753Smm" mf=NAME match finder (hc3, hc4, bt2, bt3, bt4; bt4)\n" 1191207753Smm" depth=NUM maximum search depth; 0=automatic (default)")); 1192207753Smm#endif 1193207753Smm 1194207753Smm puts(_( 1195207753Smm"\n" 1196213700Smm" --x86[=OPTS] x86 BCJ filter (32-bit and 64-bit)\n" 1197207753Smm" --powerpc[=OPTS] PowerPC BCJ filter (big endian only)\n" 1198213700Smm" --ia64[=OPTS] IA-64 (Itanium) BCJ filter\n" 1199207753Smm" --arm[=OPTS] ARM BCJ filter (little endian only)\n" 1200207753Smm" --armthumb[=OPTS] ARM-Thumb BCJ filter (little endian only)\n" 1201207753Smm" --sparc[=OPTS] SPARC BCJ filter\n" 1202207753Smm" Valid OPTS for all BCJ filters:\n" 1203207753Smm" start=NUM start offset for conversions (default=0)")); 1204207753Smm 1205207753Smm#if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA) 1206207753Smm puts(_( 1207207753Smm"\n" 1208207753Smm" --delta[=OPTS] Delta filter; valid OPTS (valid values; default):\n" 1209207753Smm" dist=NUM distance between bytes being subtracted\n" 1210207753Smm" from each other (1-256; 1)")); 1211207753Smm#endif 1212207753Smm } 1213207753Smm 1214207753Smm if (long_help) 1215207753Smm puts(_("\n Other options:\n")); 1216207753Smm 1217207753Smm puts(_( 1218207753Smm" -q, --quiet suppress warnings; specify twice to suppress errors too\n" 1219207753Smm" -v, --verbose be verbose; specify twice for even more verbose")); 1220207753Smm 1221207753Smm if (long_help) { 1222207753Smm puts(_( 1223207753Smm" -Q, --no-warn make warnings not affect the exit status")); 1224207753Smm puts(_( 1225207753Smm" --robot use machine-parsable messages (useful for scripts)")); 1226207753Smm puts(""); 1227207753Smm puts(_( 1228213700Smm" --info-memory display the total amount of RAM and the currently active\n" 1229213700Smm" memory usage limits, and exit")); 1230207753Smm puts(_( 1231207753Smm" -h, --help display the short help (lists only the basic options)\n" 1232207753Smm" -H, --long-help display this long help and exit")); 1233207753Smm } else { 1234207753Smm puts(_( 1235207753Smm" -h, --help display this short help and exit\n" 1236207753Smm" -H, --long-help display the long help (lists also the advanced options)")); 1237207753Smm } 1238207753Smm 1239207753Smm puts(_( 1240207753Smm" -V, --version display the version number and exit")); 1241207753Smm 1242207753Smm puts(_("\nWith no FILE, or when FILE is -, read standard input.\n")); 1243207753Smm 1244207753Smm // TRANSLATORS: This message indicates the bug reporting address 1245207753Smm // for this package. Please add _another line_ saying 1246207753Smm // "Report translation bugs to <...>\n" with the email or WWW 1247207753Smm // address for translation bugs. Thanks. 1248207753Smm printf(_("Report bugs to <%s> (in English or Finnish).\n"), 1249207753Smm PACKAGE_BUGREPORT); 1250207753Smm printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL); 1251207753Smm 1252292588Sdelphij#if LZMA_VERSION_STABILITY != LZMA_VERSION_STABILITY_STABLE 1253292588Sdelphij puts(_( 1254292588Sdelphij"THIS IS A DEVELOPMENT VERSION NOT INTENDED FOR PRODUCTION USE.")); 1255292588Sdelphij#endif 1256292588Sdelphij 1257207753Smm tuklib_exit(E_SUCCESS, E_ERROR, verbosity != V_SILENT); 1258207753Smm} 1259