1 ///////////////////////////////////////////////////////////////////////////////
4 /// \brief Simple single-threaded tool to uncompress .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 ///////////////////////////////////////////////////////////////////////////////
54 OPTION_FORMAT = INT_MIN,
59 static uint8_t in_buf[BUFSIZ];
62 static uint8_t out_buf[BUFSIZ];
65 static lzma_stream strm = LZMA_STREAM_INIT;
67 /// Number of bytes to use memory at maximum
68 static size_t mem_limit;
70 /// Memory allocation hooks
71 static lzma_allocator allocator = {
72 .alloc = (void *(*)(void *, size_t, size_t))(&lzma_memlimit_alloc),
73 .free = (void (*)(void *, void *))(&lzma_memlimit_free),
77 /// Program name to be shown in error messages
78 static const char *argv0;
80 /// File currently being processed
83 /// Name of the file currently being processed
84 static const char *filename;
86 static enum return_code exit_status = SUCCESS;
88 static enum format_type format_type = FORMAT_AUTO;
90 static bool force = false;
93 static void lzma_attribute((noreturn))
97 "Usage: %s [OPTION]... [FILE]...\n"
98 "Uncompress files in the .lzma format to the standard output.\n"
100 " -c, --stdout (ignored)\n"
101 " -d, --decompress (ignored)\n"
102 " -k, --keep (ignored)\n"
103 " -f, --force allow reading compressed data from a terminal\n"
104 " -M, --memory=NUM use NUM bytes of memory at maximum; the suffixes\n"
105 " k, M, G, Ki, Mi, and Gi are supported.\n"
106 " --format=FMT accept only files in the given file format;\n"
107 " possible FMTs are `auto', `native', `single',\n"
108 " `multi', and `alone', of which `single' and `multi'\n"
109 " are aliases for `native'\n"
110 " -h, --help display this help and exit\n"
111 " -V, --version display version and license information and exit\n"
113 "With no FILE, or when FILE is -, read standard input.\n"
115 "On this configuration, the tool will use about %" PRIu64
116 " MiB of memory at maximum.\n"
118 "Report bugs to <" PACKAGE_BUGREPORT "> (in English or Finnish).\n",
119 argv0, (uint64_t)((mem_limit + 512 * 1024) / (1024 * 1024)));
120 // Using PRIu64 above instead of %zu to support pre-C99 libc.
125 static void lzma_attribute((noreturn))
129 "lzmadec (LZMA Utils) " PACKAGE_VERSION "\n"
131 "Copyright (C) 1999-2006 Igor Pavlov\n"
132 "Copyright (C) 2007 Lasse Collin\n"
134 "This program is free software; you can redistribute it and/or\n"
135 "modify it under the terms of the GNU Lesser General Public\n"
136 "License as published by the Free Software Foundation; either\n"
137 "version 2.1 of the License, or (at your option) any later version.\n"
139 "This program is distributed in the hope that it will be useful,\n"
140 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
141 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
142 "Lesser General Public License for more details.\n"
148 /// Finds out the amount of physical memory in the system, and sets
149 /// a default memory usage limit.
151 set_default_mem_limit(void)
153 uint64_t mem = physmem();
157 #if UINT64_MAX > SIZE_MAX
164 // Cannot autodetect, use 10 MiB as the default limit.
165 mem_limit = (1U << 23) + (1U << 21);
172 /// \brief Converts a string to size_t
174 /// This is rudely copied from src/lzma/util.c and modified a little. :-(
177 str_to_size(const char *value)
181 if (*value < '0' || *value > '9') {
182 fprintf(stderr, "%s: %s: Not a number", argv0, value);
188 if (result > (SIZE_MAX - 9) / 10)
192 result += *value - '0';
194 } while (*value >= '0' && *value <= '9');
196 if (*value != '\0') {
198 static const struct {
207 { "Gi", 1073741824 },
211 size_t multiplier = 0;
212 for (size_t i = 0; suffixes[i].name != NULL; ++i) {
213 if (strcmp(value, suffixes[i].name) == 0) {
214 multiplier = suffixes[i].multiplier;
219 if (multiplier == 0) {
220 fprintf(stderr, "%s: %s: Invalid suffix",
225 // Don't overflow here either.
226 if (result > SIZE_MAX / multiplier)
229 result *= multiplier;
236 /// Parses command line options.
238 parse_options(int argc, char **argv)
240 static const char short_opts[] = "cdkfM:hV";
241 static const struct option long_opts[] = {
242 { "stdout", no_argument, NULL, 'c' },
243 { "to-stdout", no_argument, NULL, 'c' },
244 { "decompress", no_argument, NULL, 'd' },
245 { "uncompress", no_argument, NULL, 'd' },
246 { "force", no_argument, NULL, 'f' },
247 { "keep", no_argument, NULL, 'k' },
248 { "memory", required_argument, NULL, 'M' },
249 { "format", required_argument, NULL, OPTION_FORMAT },
250 { "help", no_argument, NULL, 'h' },
251 { "version", no_argument, NULL, 'V' },
257 while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL))
270 mem_limit = str_to_size(optarg);
279 case OPTION_FORMAT: {
280 if (strcmp("auto", optarg) == 0) {
281 format_type = FORMAT_AUTO;
282 } else if (strcmp("native", optarg) == 0
283 || strcmp("single", optarg) == 0
284 || strcmp("multi", optarg) == 0) {
285 format_type = FORMAT_NATIVE;
286 } else if (strcmp("alone", optarg) == 0) {
287 format_type = FORMAT_ALONE;
289 fprintf(stderr, "%s: %s: Unknown file format "
290 "name\n", argv0, optarg);
305 /// Initializes lzma_stream structure for decoding of a new Stream.
311 switch (format_type) {
313 ret = lzma_auto_decoder(&strm, NULL, NULL);
317 ret = lzma_stream_decoder(&strm, NULL, NULL);
321 ret = lzma_alone_decoder(&strm);
326 ret = LZMA_PROG_ERROR;
329 if (ret != LZMA_OK) {
330 fprintf(stderr, "%s: ", argv0);
332 if (ret == LZMA_MEM_ERROR)
333 fprintf(stderr, "%s\n", strerror(ENOMEM));
335 fprintf(stderr, "Internal program error (bug)\n");
347 strm.next_in = in_buf;
348 strm.avail_in = fread(in_buf, 1, BUFSIZ, file);
351 // POSIX says that fread() sets errno if an error occurred.
352 // ferror() doesn't touch errno.
353 fprintf(stderr, "%s: %s: Error reading input file: %s\n",
354 argv0, filename, strerror(errno));
365 // Handle concatenated Streams. There can be arbitrary number of
366 // nul-byte padding between the Streams, which must be ignored.
368 // NOTE: Concatenating LZMA_Alone files works only if at least
369 // one of lc, lp, and pb is non-zero. Using the concatenation
370 // on LZMA_Alone files is strongly discouraged.
372 while (strm.avail_in > 0) {
373 if (*strm.next_in != '\0')
391 if (file == stdin && !force && isatty(STDIN_FILENO)) {
392 fprintf(stderr, "%s: Compressed data not read from "
393 "a terminal.\n%s: Use `-f' to force reading "
394 "from a terminal, or `-h' for help.\n",
403 if (strm.avail_in == 0)
406 strm.next_out = out_buf;
407 strm.avail_out = BUFSIZ;
409 const lzma_ret ret = lzma_code(&strm, LZMA_RUN);
411 // Write and check write error before checking decoder error.
412 // This way as much data as possible gets written to output
413 // even if decoder detected an error. Checking write error
414 // needs to be done before checking decoder error due to
415 // how concatenated Streams are handled a few lines later.
416 const size_t write_size = BUFSIZ - strm.avail_out;
417 if (fwrite(out_buf, 1, write_size, stdout) != write_size) {
418 // Wouldn't be a surprise if writing to stderr would
419 // fail too but at least try to show an error message.
420 fprintf(stderr, "%s: Cannot write to "
421 "standard output: %s\n", argv0,
426 if (ret != LZMA_OK) {
427 if (ret == LZMA_STREAM_END) {
428 if (skip_padding()) {
436 fprintf(stderr, "%s: %s: ", argv0, filename);
439 case LZMA_DATA_ERROR:
440 fprintf(stderr, "File is corrupt\n");
443 case LZMA_HEADER_ERROR:
444 fprintf(stderr, "Unsupported file "
445 "format or filters\n");
449 fprintf(stderr, "%s\n", strerror(ENOMEM));
453 fprintf(stderr, "Unexpected end of input\n");
456 case LZMA_UNSUPPORTED_CHECK:
457 fprintf(stderr, "Unsupported type of "
458 "integrity check; not "
459 "verifying file integrity\n");
460 exit_status = WARNING;
463 case LZMA_PROG_ERROR:
465 fprintf(stderr, "Internal program "
475 main(int argc, char **argv)
479 set_default_mem_limit();
481 parse_options(argc, argv);
485 lzma_memlimit *mem_limiter = lzma_memlimit_create(mem_limit);
486 if (mem_limiter == NULL) {
487 fprintf(stderr, "%s: %s\n", argv0, strerror(ENOMEM));
491 assert(lzma_memlimit_count(mem_limiter) == 0);
493 allocator.opaque = mem_limiter;
494 strm.allocator = &allocator;
497 setmode(fileno(stdin), O_BINARY);
498 setmode(fileno(stdout), O_BINARY);
501 if (optind == argc) {
503 filename = "(stdin)";
507 if (strcmp(argv[optind], "-") == 0) {
509 filename = "(stdin)";
512 filename = argv[optind];
513 file = fopen(filename, "rb");
515 fprintf(stderr, "%s: %s: %s\n",
524 } while (++optind < argc);
528 // Free the memory only when debugging. Freeing wastes some time,
529 // but allows detecting possible memory leaks with Valgrind.
531 assert(lzma_memlimit_count(mem_limiter) == 0);
532 lzma_memlimit_end(mem_limiter, false);