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 memlimit;
70 /// Program name to be shown in error messages
71 static const char *argv0;
73 /// File currently being processed
76 /// Name of the file currently being processed
77 static const char *filename;
79 static enum return_code exit_status = SUCCESS;
81 static enum format_type format_type = FORMAT_AUTO;
83 static bool force = false;
86 static void lzma_attribute((noreturn))
90 "Usage: %s [OPTION]... [FILE]...\n"
91 "Uncompress files in the .lzma format to the standard output.\n"
93 " -c, --stdout (ignored)\n"
94 " -d, --decompress (ignored)\n"
95 " -k, --keep (ignored)\n"
96 " -f, --force allow reading compressed data from a terminal\n"
97 " -M, --memory=NUM use NUM bytes of memory at maximum; the suffixes\n"
98 " k, M, G, Ki, Mi, and Gi are supported.\n"
99 " --format=FMT accept only files in the given file format;\n"
100 " possible FMTs are `auto', `native', and alone',\n"
101 " -h, --help display this help and exit\n"
102 " -V, --version display version and license information and exit\n"
104 "With no FILE, or when FILE is -, read standard input.\n"
106 "On this configuration, the tool will use about %" PRIu64
107 " MiB of memory at maximum.\n"
109 "Report bugs to <" PACKAGE_BUGREPORT "> (in English or Finnish).\n",
110 argv0, ((uint64_t)(memlimit) + 512 * 1024) / (1024 * 1024));
111 // Using PRIu64 above instead of %zu to support pre-C99 libc.
116 static void lzma_attribute((noreturn))
120 "lzmadec (LZMA Utils) " PACKAGE_VERSION "\n"
122 "Copyright (C) 1999-2006 Igor Pavlov\n"
123 "Copyright (C) 2007 Lasse Collin\n"
125 "This program is free software; you can redistribute it and/or\n"
126 "modify it under the terms of the GNU Lesser General Public\n"
127 "License as published by the Free Software Foundation; either\n"
128 "version 2.1 of the License, or (at your option) any later version.\n"
130 "This program is distributed in the hope that it will be useful,\n"
131 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
132 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
133 "Lesser General Public License for more details.\n"
139 /// Finds out the amount of physical memory in the system, and sets
140 /// a default memory usage limit.
142 set_default_memlimit(void)
144 uint64_t mem = physmem();
148 #if UINT64_MAX > SIZE_MAX
155 // Cannot autodetect, use 10 MiB as the default limit.
156 memlimit = (1U << 23) + (1U << 21);
163 /// \brief Converts a string to size_t
165 /// This is rudely copied from src/lzma/util.c and modified a little. :-(
168 str_to_size(const char *value)
172 if (*value < '0' || *value > '9') {
173 fprintf(stderr, "%s: %s: Not a number", argv0, value);
179 if (result > (SIZE_MAX - 9) / 10)
183 result += *value - '0';
185 } while (*value >= '0' && *value <= '9');
187 if (*value != '\0') {
189 static const struct {
198 { "GB", 1000000000 },
203 { "Gi", 1073741824 },
204 { "GiB", 1073741824 }
207 size_t multiplier = 0;
208 for (size_t i = 0; i < ARRAY_SIZE(suffixes); ++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 memlimit = 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 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.
303 const uint32_t flags = LZMA_TELL_UNSUPPORTED_CHECK | LZMA_CONCATENATED;
306 switch (format_type) {
308 ret = lzma_auto_decoder(&strm, memlimit, flags);
312 ret = lzma_stream_decoder(&strm, memlimit, flags);
316 ret = lzma_alone_decoder(&strm, memlimit);
321 ret = LZMA_PROG_ERROR;
324 if (ret != LZMA_OK) {
325 fprintf(stderr, "%s: ", argv0);
327 if (ret == LZMA_MEM_ERROR)
328 fprintf(stderr, "%s\n", strerror(ENOMEM));
330 fprintf(stderr, "Internal program error (bug)\n");
342 if (file == stdin && !force && isatty(STDIN_FILENO)) {
343 fprintf(stderr, "%s: Compressed data not read from "
344 "a terminal.\n%s: Use `-f' to force reading "
345 "from a terminal, or `-h' for help.\n",
353 strm.next_out = out_buf;
354 strm.avail_out = BUFSIZ;
356 lzma_action action = LZMA_RUN;
359 if (strm.avail_in == 0) {
360 strm.next_in = in_buf;
361 strm.avail_in = fread(in_buf, 1, BUFSIZ, file);
364 // POSIX says that fread() sets errno if
365 // an error occurred. ferror() doesn't
367 fprintf(stderr, "%s: %s: Error reading "
375 action = LZMA_FINISH;
378 const lzma_ret ret = lzma_code(&strm, action);
380 // Write and check write error before checking decoder error.
381 // This way as much data as possible gets written to output
382 // even if decoder detected an error.
383 if (strm.avail_out == 0 || ret != LZMA_OK) {
384 const size_t write_size = BUFSIZ - strm.avail_out;
386 if (fwrite(out_buf, 1, write_size, stdout)
388 // Wouldn't be a surprise if writing to stderr
389 // would fail too but at least try to show an
391 fprintf(stderr, "%s: Cannot write to "
392 "standard output: %s\n", argv0,
397 strm.next_out = out_buf;
398 strm.avail_out = BUFSIZ;
401 if (ret != LZMA_OK) {
402 // FIXME !!! Doesn't work with LZMA_Alone for the
403 // same reason as in process.c.
404 if (ret == LZMA_STREAM_END)
407 fprintf(stderr, "%s: %s: ", argv0, filename);
409 // FIXME Add LZMA_*_CHECK and LZMA_FORMAT_ERROR.
411 case LZMA_DATA_ERROR:
412 fprintf(stderr, "File is corrupt\n");
415 case LZMA_OPTIONS_ERROR:
416 fprintf(stderr, "Unsupported file "
417 "format or filters\n");
421 fprintf(stderr, "%s\n", strerror(ENOMEM));
424 case LZMA_MEMLIMIT_ERROR:
425 fprintf(stderr, "Memory usage limit "
430 fprintf(stderr, "Unexpected end of input\n");
433 case LZMA_UNSUPPORTED_CHECK:
434 fprintf(stderr, "Unsupported type of "
435 "integrity check; not "
436 "verifying file integrity\n");
437 exit_status = WARNING;
440 case LZMA_PROG_ERROR:
442 fprintf(stderr, "Internal program "
452 main(int argc, char **argv)
456 set_default_memlimit();
458 parse_options(argc, argv);
463 setmode(fileno(stdin), O_BINARY);
464 setmode(fileno(stdout), O_BINARY);
467 if (optind == argc) {
469 filename = "(stdin)";
473 if (strcmp(argv[optind], "-") == 0) {
475 filename = "(stdin)";
478 filename = argv[optind];
479 file = fopen(filename, "rb");
481 fprintf(stderr, "%s: %s: %s\n",
490 } while (++optind < argc);
494 // Free the memory only when debugging. Freeing wastes some time,
495 // but allows detecting possible memory leaks with Valgrind.