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 %zu MiB of memory at maximum.\n"
113 "Report bugs to <" PACKAGE_BUGREPORT "> (in English or Finnish).\n",
114 argv0, (mem_limit + 512 * 1024) / (1024 * 1024));
119 static void lzma_attribute((noreturn))
123 "lzmadec (LZMA Utils) " PACKAGE_VERSION "\n"
125 "Copyright (C) 1999-2006 Igor Pavlov\n"
126 "Copyright (C) 2007 Lasse Collin\n"
128 "This program is free software; you can redistribute it and/or\n"
129 "modify it under the terms of the GNU Lesser General Public\n"
130 "License as published by the Free Software Foundation; either\n"
131 "version 2.1 of the License, or (at your option) any later version.\n"
133 "This program is distributed in the hope that it will be useful,\n"
134 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
135 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
136 "Lesser General Public License for more details.\n"
142 /// Finds out the amount of physical memory in the system, and sets
143 /// a default memory usage limit.
145 set_default_mem_limit(void)
147 uint64_t mem = physmem();
151 #if UINT64_MAX > SIZE_MAX
158 // Cannot autodetect, use 10 MiB as the default limit.
159 mem_limit = (1U << 23) + (1U << 21);
166 /// \brief Converts a string to size_t
168 /// This is rudely copied from src/lzma/util.c and modified a little. :-(
171 str_to_size(const char *value)
175 if (*value < '0' || *value > '9') {
176 fprintf(stderr, "%s: %s: Not a number", argv0, value);
182 if (result > (SIZE_MAX - 9) / 10)
186 result += *value - '0';
188 } while (*value >= '0' && *value <= '9');
190 if (*value != '\0') {
192 static const struct {
201 { "Gi", 1073741824 },
205 size_t multiplier = 0;
206 for (size_t i = 0; suffixes[i].name != NULL; ++i) {
207 if (strcmp(value, suffixes[i].name) == 0) {
208 multiplier = suffixes[i].multiplier;
213 if (multiplier == 0) {
214 fprintf(stderr, "%s: %s: Invalid suffix",
219 // Don't overflow here either.
220 if (result > SIZE_MAX / multiplier)
223 result *= multiplier;
230 /// Parses command line options.
232 parse_options(int argc, char **argv)
234 static const char short_opts[] = "cdkfM:hV";
235 static const struct option long_opts[] = {
236 { "stdout", no_argument, NULL, 'c' },
237 { "to-stdout", no_argument, NULL, 'c' },
238 { "decompress", no_argument, NULL, 'd' },
239 { "uncompress", no_argument, NULL, 'd' },
240 { "force", no_argument, NULL, 'f' },
241 { "keep", no_argument, NULL, 'k' },
242 { "memory", required_argument, NULL, 'M' },
243 { "format", required_argument, NULL, OPTION_FORMAT },
244 { "help", no_argument, NULL, 'h' },
245 { "version", no_argument, NULL, 'V' },
251 while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL))
264 mem_limit = str_to_size(optarg);
273 case OPTION_FORMAT: {
274 if (strcmp("auto", optarg) == 0) {
275 format_type = FORMAT_AUTO;
276 } else if (strcmp("native", optarg) == 0
277 || strcmp("single", optarg) == 0
278 || strcmp("multi", optarg) == 0) {
279 format_type = FORMAT_NATIVE;
280 } else if (strcmp("alone", optarg) == 0) {
281 format_type = FORMAT_ALONE;
283 fprintf(stderr, "%s: %s: Unknown file format "
284 "name\n", argv0, optarg);
299 /// Initializes lzma_stream structure for decoding of a new Stream.
305 switch (format_type) {
307 ret = lzma_auto_decoder(&strm, NULL, NULL);
311 ret = lzma_stream_decoder(&strm, NULL, NULL);
315 ret = lzma_alone_decoder(&strm);
320 ret = LZMA_PROG_ERROR;
323 if (ret != LZMA_OK) {
324 fprintf(stderr, "%s: ", argv0);
326 if (ret == LZMA_MEM_ERROR)
327 fprintf(stderr, "%s\n", strerror(ENOMEM));
329 fprintf(stderr, "Internal program error (bug)\n");
341 strm.next_in = in_buf;
342 strm.avail_in = fread(in_buf, 1, BUFSIZ, file);
345 // POSIX says that fread() sets errno if an error occurred.
346 // ferror() doesn't touch errno.
347 fprintf(stderr, "%s: %s: Error reading input file: %s\n",
348 argv0, filename, strerror(errno));
359 // Handle concatenated Streams. There can be arbitrary number of
360 // nul-byte padding between the Streams, which must be ignored.
362 // NOTE: Concatenating LZMA_Alone files works only if at least
363 // one of lc, lp, and pb is non-zero. Using the concatenation
364 // on LZMA_Alone files is strongly discouraged.
366 while (strm.avail_in > 0) {
367 if (*strm.next_in != '\0')
385 if (file == stdin && !force && isatty(STDIN_FILENO)) {
386 fprintf(stderr, "%s: Compressed data not read from "
387 "a terminal.\n%s: Use `-f' to force reading "
388 "from a terminal, or `-h' for help.\n",
397 if (strm.avail_in == 0)
400 strm.next_out = out_buf;
401 strm.avail_out = BUFSIZ;
403 const lzma_ret ret = lzma_code(&strm, LZMA_RUN);
405 // Write and check write error before checking decoder error.
406 // This way as much data as possible gets written to output
407 // even if decoder detected an error. Checking write error
408 // needs to be done before checking decoder error due to
409 // how concatenated Streams are handled a few lines later.
410 const size_t write_size = BUFSIZ - strm.avail_out;
411 if (fwrite(out_buf, 1, write_size, stdout) != write_size) {
412 // Wouldn't be a surprise if writing to stderr would
413 // fail too but at least try to show an error message.
414 fprintf(stderr, "%s: Cannot write to "
415 "standard output: %s\n", argv0,
420 if (ret != LZMA_OK) {
421 if (ret == LZMA_STREAM_END) {
422 if (skip_padding()) {
430 fprintf(stderr, "%s: %s: ", argv0, filename);
433 case LZMA_DATA_ERROR:
434 fprintf(stderr, "File is corrupt\n");
437 case LZMA_HEADER_ERROR:
438 fprintf(stderr, "Unsupported file "
439 "format or filters\n");
443 fprintf(stderr, "%s\n", strerror(ENOMEM));
447 fprintf(stderr, "Unexpected end of input\n");
450 case LZMA_UNSUPPORTED_CHECK:
451 fprintf(stderr, "Unsupported type of "
452 "integrity check; not "
453 "verifying file integrity\n");
454 exit_status = WARNING;
457 case LZMA_PROG_ERROR:
459 fprintf(stderr, "Internal program "
469 main(int argc, char **argv)
473 set_default_mem_limit();
475 parse_options(argc, argv);
479 lzma_memlimit *mem_limitter = lzma_memlimit_create(mem_limit);
480 if (mem_limitter == NULL) {
481 fprintf(stderr, "%s: %s\n", argv0, strerror(ENOMEM));
485 allocator.opaque = mem_limitter;
486 strm.allocator = &allocator;
488 if (optind == argc) {
490 filename = "(stdin)";
494 if (strcmp(argv[optind], "-") == 0) {
496 filename = "(stdin)";
499 filename = argv[optind];
500 file = fopen(filename, "rb");
502 fprintf(stderr, "%s: %s: %s\n",
511 } while (++optind < argc);