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 ///////////////////////////////////////////////////////////////////////////////
49 OPTION_FORMAT = INT_MIN,
54 static uint8_t in_buf[BUFSIZ];
57 static uint8_t out_buf[BUFSIZ];
60 static lzma_stream strm = LZMA_STREAM_INIT;
62 /// Number of bytes to use memory at maximum
63 static uint64_t memlimit;
65 /// Program name to be shown in error messages
66 static const char *argv0;
68 /// File currently being processed
71 /// Name of the file currently being processed
72 static const char *filename;
74 static enum return_code exit_status = SUCCESS;
76 static enum format_type format_type = FORMAT_AUTO;
78 static bool force = false;
81 static void lzma_attribute((noreturn))
85 "Usage: %s [OPTION]... [FILE]...\n"
86 "Uncompress files in the .lzma format to the standard output.\n"
88 " -c, --stdout (ignored)\n"
89 " -d, --decompress (ignored)\n"
90 " -k, --keep (ignored)\n"
91 " -f, --force allow reading compressed data from a terminal\n"
92 " -M, --memory=NUM use NUM bytes of memory at maximum (0 means default);\n"
93 " the suffixes k, M, G, Ki, Mi, and Gi are supported.\n"
94 " --format=FMT accept only files in the given file format;\n"
95 " possible FMTs are `auto', `native', and alone',\n"
96 " -h, --help display this help and exit\n"
97 " -V, --version display version and license information and exit\n"
99 "With no FILE, or when FILE is -, read standard input.\n"
101 "On this configuration, the tool will use about %" PRIu64
102 " MiB of memory at maximum.\n"
104 "Report bugs to <" PACKAGE_BUGREPORT "> (in English or Finnish).\n",
105 argv0, ((uint64_t)(memlimit) + 512 * 1024) / (1024 * 1024));
106 // Using PRIu64 above instead of %zu to support pre-C99 libc.
111 static void lzma_attribute((noreturn))
115 "lzmadec (LZMA Utils) " PACKAGE_VERSION "\n"
117 "Copyright (C) 1999-2006 Igor Pavlov\n"
118 "Copyright (C) 2007 Lasse Collin\n"
120 "This program is free software; you can redistribute it and/or\n"
121 "modify it under the terms of the GNU Lesser General Public\n"
122 "License as published by the Free Software Foundation; either\n"
123 "version 2.1 of the License, or (at your option) any later version.\n"
125 "This program is distributed in the hope that it will be useful,\n"
126 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
127 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
128 "Lesser General Public License for more details.\n"
134 /// Finds out the amount of physical memory in the system, and sets
135 /// a default memory usage limit.
137 set_default_memlimit(void)
139 const uint64_t mem = physmem();
142 // Cannot autodetect, use 10 MiB as the default limit.
143 memlimit = (1U << 23) + (1U << 21);
145 // Limit is 33 % of RAM.
152 /// \brief Converts a string to size_t
154 /// This is rudely copied from src/xz/util.c and modified a little. :-(
157 str_to_uint64(const char *value)
161 if (*value < '0' || *value > '9') {
162 fprintf(stderr, "%s: %s: Not a number", argv0, value);
168 if (result > (SIZE_MAX - 9) / 10)
172 result += *value - '0';
174 } while (*value >= '0' && *value <= '9');
176 if (*value != '\0') {
178 static const struct {
187 { "GB", 1000000000 },
192 { "Gi", 1073741824 },
193 { "GiB", 1073741824 }
196 size_t multiplier = 0;
197 for (size_t i = 0; i < ARRAY_SIZE(suffixes); ++i) {
198 if (strcmp(value, suffixes[i].name) == 0) {
199 multiplier = suffixes[i].multiplier;
204 if (multiplier == 0) {
205 fprintf(stderr, "%s: %s: Invalid suffix",
210 // Don't overflow here either.
211 if (result > SIZE_MAX / multiplier)
214 result *= multiplier;
221 /// Parses command line options.
223 parse_options(int argc, char **argv)
225 static const char short_opts[] = "cdkfM:hV";
226 static const struct option long_opts[] = {
227 { "stdout", no_argument, NULL, 'c' },
228 { "to-stdout", no_argument, NULL, 'c' },
229 { "decompress", no_argument, NULL, 'd' },
230 { "uncompress", no_argument, NULL, 'd' },
231 { "force", no_argument, NULL, 'f' },
232 { "keep", no_argument, NULL, 'k' },
233 { "memory", required_argument, NULL, 'M' },
234 { "format", required_argument, NULL, OPTION_FORMAT },
235 { "help", no_argument, NULL, 'h' },
236 { "version", no_argument, NULL, 'V' },
242 while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL))
255 memlimit = str_to_uint64(optarg);
257 set_default_memlimit();
267 case OPTION_FORMAT: {
268 if (strcmp("auto", optarg) == 0) {
269 format_type = FORMAT_AUTO;
270 } else if (strcmp("native", optarg) == 0) {
271 format_type = FORMAT_NATIVE;
272 } else if (strcmp("alone", optarg) == 0) {
273 format_type = FORMAT_ALONE;
275 fprintf(stderr, "%s: %s: Unknown file format "
276 "name\n", argv0, optarg);
291 /// Initializes lzma_stream structure for decoding of a new Stream.
295 const uint32_t flags = LZMA_TELL_UNSUPPORTED_CHECK | LZMA_CONCATENATED;
298 switch (format_type) {
300 ret = lzma_auto_decoder(&strm, memlimit, flags);
304 ret = lzma_stream_decoder(&strm, memlimit, flags);
308 ret = lzma_alone_decoder(&strm, memlimit);
313 ret = LZMA_PROG_ERROR;
316 if (ret != LZMA_OK) {
317 fprintf(stderr, "%s: ", argv0);
319 if (ret == LZMA_MEM_ERROR)
320 fprintf(stderr, "%s\n", strerror(ENOMEM));
322 fprintf(stderr, "Internal program error (bug)\n");
334 if (file == stdin && !force && isatty(STDIN_FILENO)) {
335 fprintf(stderr, "%s: Compressed data not read from "
336 "a terminal.\n%s: Use `-f' to force reading "
337 "from a terminal, or `-h' for help.\n",
345 strm.next_out = out_buf;
346 strm.avail_out = BUFSIZ;
348 lzma_action action = LZMA_RUN;
351 if (strm.avail_in == 0) {
352 strm.next_in = in_buf;
353 strm.avail_in = fread(in_buf, 1, BUFSIZ, file);
356 // POSIX says that fread() sets errno if
357 // an error occurred. ferror() doesn't
359 fprintf(stderr, "%s: %s: Error reading "
367 action = LZMA_FINISH;
370 const lzma_ret ret = lzma_code(&strm, action);
372 // Write and check write error before checking decoder error.
373 // This way as much data as possible gets written to output
374 // even if decoder detected an error.
375 if (strm.avail_out == 0 || ret != LZMA_OK) {
376 const size_t write_size = BUFSIZ - strm.avail_out;
378 if (fwrite(out_buf, 1, write_size, stdout)
380 // Wouldn't be a surprise if writing to stderr
381 // would fail too but at least try to show an
383 fprintf(stderr, "%s: Cannot write to "
384 "standard output: %s\n", argv0,
389 strm.next_out = out_buf;
390 strm.avail_out = BUFSIZ;
393 if (ret != LZMA_OK) {
394 // FIXME !!! Doesn't work with LZMA_Alone for the
395 // same reason as in process.c.
396 if (ret == LZMA_STREAM_END)
399 fprintf(stderr, "%s: %s: ", argv0, filename);
401 // FIXME Add LZMA_*_CHECK and LZMA_FORMAT_ERROR.
403 case LZMA_DATA_ERROR:
404 fprintf(stderr, "File is corrupt\n");
407 case LZMA_OPTIONS_ERROR:
408 fprintf(stderr, "Unsupported file "
409 "format or filters\n");
413 fprintf(stderr, "%s\n", strerror(ENOMEM));
416 case LZMA_MEMLIMIT_ERROR:
417 fprintf(stderr, "Memory usage limit "
422 fprintf(stderr, "Unexpected end of input\n");
425 case LZMA_UNSUPPORTED_CHECK:
426 fprintf(stderr, "Unsupported type of "
427 "integrity check; not "
428 "verifying file integrity\n");
429 exit_status = WARNING;
432 case LZMA_PROG_ERROR:
434 fprintf(stderr, "Internal program "
444 main(int argc, char **argv)
448 set_default_memlimit();
450 parse_options(argc, argv);
455 setmode(fileno(stdin), O_BINARY);
456 setmode(fileno(stdout), O_BINARY);
459 if (optind == argc) {
461 filename = "(stdin)";
465 if (strcmp(argv[optind], "-") == 0) {
467 filename = "(stdin)";
470 filename = argv[optind];
471 file = fopen(filename, "rb");
473 fprintf(stderr, "%s: %s: %s\n",
482 } while (++optind < argc);
486 // Free the memory only when debugging. Freeing wastes some time,
487 // but allows detecting possible memory leaks with Valgrind.