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 ///////////////////////////////////////////////////////////////////////////////
50 OPTION_FORMAT = INT_MIN,
55 static uint8_t in_buf[BUFSIZ];
58 static uint8_t out_buf[BUFSIZ];
61 static lzma_stream strm = LZMA_STREAM_INIT;
63 /// Number of bytes to use memory at maximum
64 static size_t mem_limit;
66 /// Memory allocation hooks
67 static lzma_allocator allocator = {
68 .alloc = (void *(*)(void *, size_t, size_t))(&lzma_memlimit_alloc),
69 .free = (void (*)(void *, void *))(&lzma_memlimit_free),
73 /// Program name to be shown in error messages
74 static const char *argv0;
76 /// File currently being processed
79 /// Name of the file currently being processed
80 static const char *filename;
82 static enum return_code exit_status = SUCCESS;
84 static enum format_type format_type = FORMAT_AUTO;
86 static bool force = false;
89 static void lzma_attribute((noreturn))
93 "Usage: %s [OPTION]... [FILE]...\n"
94 "Uncompress files in the .lzma format to the standard output.\n"
96 " -c, --stdout (ignored)\n"
97 " -d, --decompress (ignored)\n"
98 " -k, --keep (ignored)\n"
99 " -f, --force allow reading compressed data from a terminal\n"
100 " -M, --memory=NUM use NUM bytes of memory at maximum; the suffixes\n"
101 " k, M, G, Ki, Mi, and Gi are supported.\n"
102 " --format=FMT accept only files in the given file format;\n"
103 " possible FMTs are `auto', `native', `single',\n"
104 " `multi', and `alone', of which `single' and `multi'\n"
105 " are aliases for `native'\n"
106 " -h, --help display this help and exit\n"
107 " -V, --version display version and license information and exit\n"
109 "With no FILE, or when FILE is -, read standard input.\n"
111 "On this configuration, the tool will use about %" PRIu64
112 " MiB of memory at maximum.\n"
114 "Report bugs to <" PACKAGE_BUGREPORT "> (in English or Finnish).\n",
115 argv0, (uint64_t)((mem_limit + 512 * 1024) / (1024 * 1024)));
116 // Using PRIu64 above instead of %zu to support pre-C99 libc.
121 static void lzma_attribute((noreturn))
125 "lzmadec (LZMA Utils) " PACKAGE_VERSION "\n"
127 "Copyright (C) 1999-2006 Igor Pavlov\n"
128 "Copyright (C) 2007 Lasse Collin\n"
130 "This program is free software; you can redistribute it and/or\n"
131 "modify it under the terms of the GNU Lesser General Public\n"
132 "License as published by the Free Software Foundation; either\n"
133 "version 2.1 of the License, or (at your option) any later version.\n"
135 "This program is distributed in the hope that it will be useful,\n"
136 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
137 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
138 "Lesser General Public License for more details.\n"
144 /// Finds out the amount of physical memory in the system, and sets
145 /// a default memory usage limit.
147 set_default_mem_limit(void)
149 uint64_t mem = physmem();
153 #if UINT64_MAX > SIZE_MAX
160 // Cannot autodetect, use 10 MiB as the default limit.
161 mem_limit = (1U << 23) + (1U << 21);
168 /// \brief Converts a string to size_t
170 /// This is rudely copied from src/lzma/util.c and modified a little. :-(
173 str_to_size(const char *value)
177 if (*value < '0' || *value > '9') {
178 fprintf(stderr, "%s: %s: Not a number", argv0, value);
184 if (result > (SIZE_MAX - 9) / 10)
188 result += *value - '0';
190 } while (*value >= '0' && *value <= '9');
192 if (*value != '\0') {
194 static const struct {
203 { "Gi", 1073741824 },
207 size_t multiplier = 0;
208 for (size_t i = 0; suffixes[i].name != NULL; ++i) {
209 if (strcmp(value, suffixes[i].name) == 0) {
210 multiplier = suffixes[i].multiplier;
215 if (multiplier == 0) {
216 fprintf(stderr, "%s: %s: Invalid suffix",
221 // Don't overflow here either.
222 if (result > SIZE_MAX / multiplier)
225 result *= multiplier;
232 /// Parses command line options.
234 parse_options(int argc, char **argv)
236 static const char short_opts[] = "cdkfM:hV";
237 static const struct option long_opts[] = {
238 { "stdout", no_argument, NULL, 'c' },
239 { "to-stdout", no_argument, NULL, 'c' },
240 { "decompress", no_argument, NULL, 'd' },
241 { "uncompress", no_argument, NULL, 'd' },
242 { "force", no_argument, NULL, 'f' },
243 { "keep", no_argument, NULL, 'k' },
244 { "memory", required_argument, NULL, 'M' },
245 { "format", required_argument, NULL, OPTION_FORMAT },
246 { "help", no_argument, NULL, 'h' },
247 { "version", no_argument, NULL, 'V' },
253 while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL))
266 mem_limit = str_to_size(optarg);
275 case OPTION_FORMAT: {
276 if (strcmp("auto", optarg) == 0) {
277 format_type = FORMAT_AUTO;
278 } else if (strcmp("native", optarg) == 0
279 || strcmp("single", optarg) == 0
280 || strcmp("multi", optarg) == 0) {
281 format_type = FORMAT_NATIVE;
282 } else if (strcmp("alone", optarg) == 0) {
283 format_type = FORMAT_ALONE;
285 fprintf(stderr, "%s: %s: Unknown file format "
286 "name\n", argv0, optarg);
301 /// Initializes lzma_stream structure for decoding of a new Stream.
307 switch (format_type) {
309 ret = lzma_auto_decoder(&strm, NULL, NULL);
313 ret = lzma_stream_decoder(&strm, NULL, NULL);
317 ret = lzma_alone_decoder(&strm);
322 ret = LZMA_PROG_ERROR;
325 if (ret != LZMA_OK) {
326 fprintf(stderr, "%s: ", argv0);
328 if (ret == LZMA_MEM_ERROR)
329 fprintf(stderr, "%s\n", strerror(ENOMEM));
331 fprintf(stderr, "Internal program error (bug)\n");
343 strm.next_in = in_buf;
344 strm.avail_in = fread(in_buf, 1, BUFSIZ, file);
347 // POSIX says that fread() sets errno if an error occurred.
348 // ferror() doesn't touch errno.
349 fprintf(stderr, "%s: %s: Error reading input file: %s\n",
350 argv0, filename, strerror(errno));
361 // Handle concatenated Streams. There can be arbitrary number of
362 // nul-byte padding between the Streams, which must be ignored.
364 // NOTE: Concatenating LZMA_Alone files works only if at least
365 // one of lc, lp, and pb is non-zero. Using the concatenation
366 // on LZMA_Alone files is strongly discouraged.
368 while (strm.avail_in > 0) {
369 if (*strm.next_in != '\0')
387 if (file == stdin && !force && isatty(STDIN_FILENO)) {
388 fprintf(stderr, "%s: Compressed data not read from "
389 "a terminal.\n%s: Use `-f' to force reading "
390 "from a terminal, or `-h' for help.\n",
399 if (strm.avail_in == 0)
402 strm.next_out = out_buf;
403 strm.avail_out = BUFSIZ;
405 const lzma_ret ret = lzma_code(&strm, LZMA_RUN);
407 // Write and check write error before checking decoder error.
408 // This way as much data as possible gets written to output
409 // even if decoder detected an error. Checking write error
410 // needs to be done before checking decoder error due to
411 // how concatenated Streams are handled a few lines later.
412 const size_t write_size = BUFSIZ - strm.avail_out;
413 if (fwrite(out_buf, 1, write_size, stdout) != write_size) {
414 // Wouldn't be a surprise if writing to stderr would
415 // fail too but at least try to show an error message.
416 fprintf(stderr, "%s: Cannot write to "
417 "standard output: %s\n", argv0,
422 if (ret != LZMA_OK) {
423 if (ret == LZMA_STREAM_END) {
424 if (skip_padding()) {
432 fprintf(stderr, "%s: %s: ", argv0, filename);
435 case LZMA_DATA_ERROR:
436 fprintf(stderr, "File is corrupt\n");
439 case LZMA_HEADER_ERROR:
440 fprintf(stderr, "Unsupported file "
441 "format or filters\n");
445 fprintf(stderr, "%s\n", strerror(ENOMEM));
449 fprintf(stderr, "Unexpected end of input\n");
452 case LZMA_UNSUPPORTED_CHECK:
453 fprintf(stderr, "Unsupported type of "
454 "integrity check; not "
455 "verifying file integrity\n");
456 exit_status = WARNING;
459 case LZMA_PROG_ERROR:
461 fprintf(stderr, "Internal program "
471 main(int argc, char **argv)
475 set_default_mem_limit();
477 parse_options(argc, argv);
481 lzma_memlimit *mem_limitter = lzma_memlimit_create(mem_limit);
482 if (mem_limitter == NULL) {
483 fprintf(stderr, "%s: %s\n", argv0, strerror(ENOMEM));
487 allocator.opaque = mem_limitter;
488 strm.allocator = &allocator;
490 if (optind == argc) {
492 filename = "(stdin)";
496 if (strcmp(argv[optind], "-") == 0) {
498 filename = "(stdin)";
501 filename = argv[optind];
502 file = fopen(filename, "rb");
504 fprintf(stderr, "%s: %s: %s\n",
513 } while (++optind < argc);
517 // Free the memory only when debugging. Freeing wastes some time,
518 // but allows detecting possible memory leaks with Valgrind.
520 lzma_memlimit_end(mem_limitter, false);