1 ///////////////////////////////////////////////////////////////////////////////
4 /// \brief Simple single-threaded tool to uncompress .xz or .lzma files
6 // Copyright (C) 2007 Lasse Collin
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Lesser General Public
10 // License as published by the Free Software Foundation; either
11 // version 2.1 of the License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Lesser General Public License for more details.
18 ///////////////////////////////////////////////////////////////////////////////
35 # define TOOL_FORMAT "lzma"
37 # define TOOL_FORMAT "xz"
41 /// Number of bytes to use memory at maximum
42 static uint64_t memlimit;
44 /// Program name to be shown in error messages
45 static const char *argv0;
48 static void lzma_attribute((noreturn))
51 int status = EXIT_SUCCESS;
53 // Close stdout. We don't care about stderr, because we write to it
54 // only when an error has already occurred.
55 const int ferror_err = ferror(stdout);
56 const int fclose_err = fclose(stdout);
58 if (ferror_err || fclose_err) {
59 // If it was fclose() that failed, we have the reason
60 // in errno. If only ferror() indicated an error,
61 // we have no idea what the reason was.
62 fprintf(stderr, "%s: Cannot write to standard output: %s\n",
64 ? strerror(errno) : "Unknown error");
65 status = EXIT_FAILURE;
72 static void lzma_attribute((noreturn))
76 "Usage: %s [OPTION]... [FILE]...\n"
77 "Uncompress files in the ." TOOL_FORMAT " format to the standard output.\n"
79 " -c, --stdout (ignored)\n"
80 " -d, --decompress (ignored)\n"
81 " -k, --keep (ignored)\n"
82 " -f, --force (ignored)\n"
83 " -M, --memory=NUM use NUM bytes of memory at maximum (0 means default);\n"
84 " the suffixes k, M, G, Ki, Mi, and Gi are supported.\n"
85 " -h, --help display this help and exit\n"
86 " -V, --version display version and license information and exit\n"
88 "With no FILE, or when FILE is -, read standard input.\n"
90 "On this configuration, the tool will use about %" PRIu64
91 " MiB of memory at maximum.\n"
93 "Report bugs to <" PACKAGE_BUGREPORT "> (in English or Finnish).\n",
94 argv0, (memlimit + 512 * 1024) / (1024 * 1024));
99 static void lzma_attribute((noreturn))
102 printf(TOOL_FORMAT "dec " PACKAGE_VERSION "\n"
103 "liblzma %s\n", lzma_version_string());
109 /// Finds out the amount of physical memory in the system, and sets
110 /// a default memory usage limit.
112 set_default_memlimit(void)
114 const uint64_t mem = physmem();
117 // Cannot autodetect, use 10 MiB as the default limit.
118 memlimit = (1U << 23) + (1U << 21);
120 // Limit is 33 % of RAM.
127 /// \brief Converts a string to uint64_t
129 /// This is rudely copied from src/xz/util.c and modified a little. :-(
132 str_to_uint64(const char *value)
136 if (*value < '0' || *value > '9') {
137 fprintf(stderr, "%s: %s: Not a number", argv0, value);
143 if (result > (UINT64_MAX - 9) / 10)
147 result += *value - '0';
149 } while (*value >= '0' && *value <= '9');
151 if (*value != '\0') {
153 static const struct {
162 { "GB", 1000000000 },
167 { "Gi", 1073741824 },
168 { "GiB", 1073741824 }
171 uint32_t multiplier = 0;
172 for (size_t i = 0; i < ARRAY_SIZE(suffixes); ++i) {
173 if (strcmp(value, suffixes[i].name) == 0) {
174 multiplier = suffixes[i].multiplier;
179 if (multiplier == 0) {
180 fprintf(stderr, "%s: %s: Invalid suffix",
185 // Don't overflow here either.
186 if (result > UINT64_MAX / multiplier)
189 result *= multiplier;
196 /// Parses command line options.
198 parse_options(int argc, char **argv)
200 static const char short_opts[] = "cdkfM:hV";
201 static const struct option long_opts[] = {
202 { "stdout", no_argument, NULL, 'c' },
203 { "to-stdout", no_argument, NULL, 'c' },
204 { "decompress", no_argument, NULL, 'd' },
205 { "uncompress", no_argument, NULL, 'd' },
206 { "force", no_argument, NULL, 'f' },
207 { "keep", no_argument, NULL, 'k' },
208 { "memory", required_argument, NULL, 'M' },
209 { "help", no_argument, NULL, 'h' },
210 { "version", no_argument, NULL, 'V' },
216 while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL))
226 memlimit = str_to_uint64(optarg);
228 set_default_memlimit();
248 uncompress(lzma_stream *strm, FILE *file, const char *filename)
252 // Initialize the decoder
254 ret = lzma_alone_decoder(strm, memlimit);
256 ret = lzma_stream_decoder(strm, memlimit, LZMA_CONCATENATED);
259 // The only reasonable error here is LZMA_MEM_ERROR.
260 // FIXME: Maybe also LZMA_MEMLIMIT_ERROR in future?
261 if (ret != LZMA_OK) {
262 fprintf(stderr, "%s: ", argv0);
264 if (ret == LZMA_MEM_ERROR)
265 fprintf(stderr, "%s\n", strerror(ENOMEM));
267 fprintf(stderr, "Internal program error (bug)\n");
272 // Input and output buffers
273 uint8_t in_buf[BUFSIZ];
274 uint8_t out_buf[BUFSIZ];
277 strm->next_out = out_buf;
278 strm->avail_out = BUFSIZ;
280 lzma_action action = LZMA_RUN;
283 if (strm->avail_in == 0) {
284 strm->next_in = in_buf;
285 strm->avail_in = fread(in_buf, 1, BUFSIZ, file);
288 // POSIX says that fread() sets errno if
289 // an error occurred. ferror() doesn't
291 fprintf(stderr, "%s: %s: Error reading "
299 // When using LZMA_CONCATENATED, we need to tell
300 // liblzma when it has got all the input.
302 action = LZMA_FINISH;
306 ret = lzma_code(strm, action);
308 // Write and check write error before checking decoder error.
309 // This way as much data as possible gets written to output
310 // even if decoder detected an error.
311 if (strm->avail_out == 0 || ret != LZMA_OK) {
312 const size_t write_size = BUFSIZ - strm->avail_out;
314 if (fwrite(out_buf, 1, write_size, stdout)
316 // Wouldn't be a surprise if writing to stderr
317 // would fail too but at least try to show an
319 fprintf(stderr, "%s: Cannot write to "
320 "standard output: %s\n", argv0,
325 strm->next_out = out_buf;
326 strm->avail_out = BUFSIZ;
329 if (ret != LZMA_OK) {
330 if (ret == LZMA_STREAM_END) {
332 // Check that there's no trailing garbage.
333 if (strm->avail_in != 0
334 || fread(in_buf, 1, 1, file)
337 ret = LZMA_DATA_ERROR;
341 // lzma_stream_decoder() already guarantees
342 // that there's no trailing garbage.
343 assert(strm->avail_in == 0);
344 assert(action == LZMA_FINISH);
353 msg = strerror(ENOMEM);
356 case LZMA_MEMLIMIT_ERROR:
357 msg = "Memory usage limit reached";
360 case LZMA_FORMAT_ERROR:
361 msg = "File format not recognized";
364 case LZMA_OPTIONS_ERROR:
365 // FIXME: Better message?
366 msg = "Unsupported compression options";
369 case LZMA_DATA_ERROR:
370 msg = "File is corrupt";
374 msg = "Unexpected end of input";
378 msg = "Internal program error (bug)";
382 fprintf(stderr, "%s: %s: %s", argv0, filename, msg);
391 main(int argc, char **argv)
393 // Set the argv0 global so that we can print the command name in
394 // error and help messages.
397 // Detect amount of installed RAM and set the memory usage limit.
398 // This is needed before parsing the command line arguments.
399 set_default_memlimit();
401 // Parse the command line options.
402 parse_options(argc, argv);
404 // Initialize liblzma internals.
407 // The same lzma_stream is used for all files that we decode. This way
408 // we don't need to reallocate memory for every file if they use same
409 // compression settings.
410 lzma_stream strm = LZMA_STREAM_INIT;
412 // Some systems require setting stdin and stdout to binary mode.
414 setmode(fileno(stdin), O_BINARY);
415 setmode(fileno(stdout), O_BINARY);
418 if (optind == argc) {
419 // No filenames given, decode from stdin.
420 uncompress(&strm, stdin, "(stdin)");
422 // Loop through the filenames given on the command line.
424 // "-" indicates stdin.
425 if (strcmp(argv[optind], "-") == 0) {
426 uncompress(&strm, stdin, "(stdin)");
428 FILE *file = fopen(argv[optind], "rb");
430 fprintf(stderr, "%s: %s: %s\n",
436 uncompress(&strm, file, argv[optind]);
439 } while (++optind < argc);
443 // Free the memory only when debugging. Freeing wastes some time,
444 // but allows detecting possible memory leaks with Valgrind.