summaryrefslogtreecommitdiff
path: root/Build/source/utils/xz-4.999.8beta/src/xzdec/xzdec.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/utils/xz-4.999.8beta/src/xzdec/xzdec.c')
-rw-r--r--Build/source/utils/xz-4.999.8beta/src/xzdec/xzdec.c447
1 files changed, 447 insertions, 0 deletions
diff --git a/Build/source/utils/xz-4.999.8beta/src/xzdec/xzdec.c b/Build/source/utils/xz-4.999.8beta/src/xzdec/xzdec.c
new file mode 100644
index 00000000000..c17b5790948
--- /dev/null
+++ b/Build/source/utils/xz-4.999.8beta/src/xzdec/xzdec.c
@@ -0,0 +1,447 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file xzdec.c
+/// \brief Simple single-threaded tool to uncompress .xz or .lzma files
+//
+// Copyright (C) 2007 Lasse Collin
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "sysdefs.h"
+#include "lzma.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#ifdef _WIN32
+# include <fcntl.h>
+#endif
+
+#include "getopt.h"
+#include "physmem.h"
+
+
+#ifdef LZMADEC
+# define TOOL_FORMAT "lzma"
+#else
+# define TOOL_FORMAT "xz"
+#endif
+
+
+/// Number of bytes to use memory at maximum
+static uint64_t memlimit;
+
+/// Program name to be shown in error messages
+static const char *argv0;
+
+
+static void lzma_attribute((noreturn))
+my_exit(void)
+{
+ int status = EXIT_SUCCESS;
+
+ // Close stdout. We don't care about stderr, because we write to it
+ // only when an error has already occurred.
+ const int ferror_err = ferror(stdout);
+ const int fclose_err = fclose(stdout);
+
+ if (ferror_err || fclose_err) {
+ // If it was fclose() that failed, we have the reason
+ // in errno. If only ferror() indicated an error,
+ // we have no idea what the reason was.
+ fprintf(stderr, "%s: Cannot write to standard output: %s\n",
+ argv0, fclose_err
+ ? strerror(errno) : "Unknown error");
+ status = EXIT_FAILURE;
+ }
+
+ exit(status);
+}
+
+
+static void lzma_attribute((noreturn))
+help(void)
+{
+ printf(
+"Usage: %s [OPTION]... [FILE]...\n"
+"Uncompress files in the ." TOOL_FORMAT " format to the standard output.\n"
+"\n"
+" -c, --stdout (ignored)\n"
+" -d, --decompress (ignored)\n"
+" -k, --keep (ignored)\n"
+" -f, --force (ignored)\n"
+" -M, --memory=NUM use NUM bytes of memory at maximum (0 means default);\n"
+" the suffixes k, M, G, Ki, Mi, and Gi are supported.\n"
+" -h, --help display this help and exit\n"
+" -V, --version display version and license information and exit\n"
+"\n"
+"With no FILE, or when FILE is -, read standard input.\n"
+"\n"
+"On this configuration, the tool will use about %" PRIu64
+ " MiB of memory at maximum.\n"
+"\n"
+"Report bugs to <" PACKAGE_BUGREPORT "> (in English or Finnish).\n",
+ argv0, (memlimit + 512 * 1024) / (1024 * 1024));
+ my_exit();
+}
+
+
+static void lzma_attribute((noreturn))
+version(void)
+{
+ printf(TOOL_FORMAT "dec " PACKAGE_VERSION "\n"
+ "liblzma %s\n", lzma_version_string());
+
+ my_exit();
+}
+
+
+/// Finds out the amount of physical memory in the system, and sets
+/// a default memory usage limit.
+static void
+set_default_memlimit(void)
+{
+ const uint64_t mem = physmem();
+
+ if (mem == 0)
+ // Cannot autodetect, use 10 MiB as the default limit.
+ memlimit = (1U << 23) + (1U << 21);
+ else
+ // Limit is 33 % of RAM.
+ memlimit = mem / 3;
+
+ return;
+}
+
+
+/// \brief Converts a string to uint64_t
+///
+/// This is rudely copied from src/xz/util.c and modified a little. :-(
+///
+static uint64_t
+str_to_uint64(const char *value)
+{
+ uint64_t result = 0;
+
+ if (*value < '0' || *value > '9') {
+ fprintf(stderr, "%s: %s: Not a number\n", argv0, value);
+ exit(EXIT_FAILURE);
+ }
+
+ do {
+ // Don't overflow.
+ if (result > (UINT64_MAX - 9) / 10)
+ return UINT64_MAX;
+
+ result *= 10;
+ result += *value - '0';
+ ++value;
+ } while (*value >= '0' && *value <= '9');
+
+ if (*value != '\0') {
+ // Look for suffix.
+ static const struct {
+ const char name[4];
+ uint32_t multiplier;
+ } suffixes[] = {
+ { "k", 1000 },
+ { "kB", 1000 },
+ { "M", 1000000 },
+ { "MB", 1000000 },
+ { "G", 1000000000 },
+ { "GB", 1000000000 },
+ { "Ki", 1024 },
+ { "KiB", 1024 },
+ { "Mi", 1048576 },
+ { "MiB", 1048576 },
+ { "Gi", 1073741824 },
+ { "GiB", 1073741824 }
+ };
+
+ uint32_t multiplier = 0;
+ for (size_t i = 0; i < ARRAY_SIZE(suffixes); ++i) {
+ if (strcmp(value, suffixes[i].name) == 0) {
+ multiplier = suffixes[i].multiplier;
+ break;
+ }
+ }
+
+ if (multiplier == 0) {
+ fprintf(stderr, "%s: %s: Invalid suffix\n",
+ argv0, value);
+ exit(EXIT_FAILURE);
+ }
+
+ // Don't overflow here either.
+ if (result > UINT64_MAX / multiplier)
+ result = UINT64_MAX;
+ else
+ result *= multiplier;
+ }
+
+ return result;
+}
+
+
+/// Parses command line options.
+static void
+parse_options(int argc, char **argv)
+{
+ static const char short_opts[] = "cdkfM:hV";
+ static const struct option long_opts[] = {
+ { "stdout", no_argument, NULL, 'c' },
+ { "to-stdout", no_argument, NULL, 'c' },
+ { "decompress", no_argument, NULL, 'd' },
+ { "uncompress", no_argument, NULL, 'd' },
+ { "force", no_argument, NULL, 'f' },
+ { "keep", no_argument, NULL, 'k' },
+ { "memory", required_argument, NULL, 'M' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+
+ while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL))
+ != -1) {
+ switch (c) {
+ case 'c':
+ case 'd':
+ case 'f':
+ case 'k':
+ break;
+
+ case 'M':
+ memlimit = str_to_uint64(optarg);
+ if (memlimit == 0)
+ set_default_memlimit();
+
+ break;
+
+ case 'h':
+ help();
+
+ case 'V':
+ version();
+
+ default:
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ return;
+}
+
+
+static void
+uncompress(lzma_stream *strm, FILE *file, const char *filename)
+{
+ lzma_ret ret;
+
+ // Initialize the decoder
+#ifdef LZMADEC
+ ret = lzma_alone_decoder(strm, memlimit);
+#else
+ ret = lzma_stream_decoder(strm, memlimit, LZMA_CONCATENATED);
+#endif
+
+ // The only reasonable error here is LZMA_MEM_ERROR.
+ // FIXME: Maybe also LZMA_MEMLIMIT_ERROR in future?
+ if (ret != LZMA_OK) {
+ fprintf(stderr, "%s: ", argv0);
+
+ if (ret == LZMA_MEM_ERROR)
+ fprintf(stderr, "%s\n", strerror(ENOMEM));
+ else
+ fprintf(stderr, "Internal program error (bug)\n");
+
+ exit(EXIT_FAILURE);
+ }
+
+ // Input and output buffers
+ uint8_t in_buf[BUFSIZ];
+ uint8_t out_buf[BUFSIZ];
+
+ strm->avail_in = 0;
+ strm->next_out = out_buf;
+ strm->avail_out = BUFSIZ;
+
+ lzma_action action = LZMA_RUN;
+
+ while (true) {
+ if (strm->avail_in == 0) {
+ strm->next_in = in_buf;
+ strm->avail_in = fread(in_buf, 1, BUFSIZ, file);
+
+ if (ferror(file)) {
+ // POSIX says that fread() sets errno if
+ // an error occurred. ferror() doesn't
+ // touch errno.
+ fprintf(stderr, "%s: %s: Error reading "
+ "input file: %s\n",
+ argv0, filename,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+#ifndef LZMADEC
+ // When using LZMA_CONCATENATED, we need to tell
+ // liblzma when it has got all the input.
+ if (feof(file))
+ action = LZMA_FINISH;
+#endif
+ }
+
+ ret = lzma_code(strm, action);
+
+ // Write and check write error before checking decoder error.
+ // This way as much data as possible gets written to output
+ // even if decoder detected an error.
+ if (strm->avail_out == 0 || ret != LZMA_OK) {
+ const size_t write_size = BUFSIZ - strm->avail_out;
+
+ if (fwrite(out_buf, 1, write_size, stdout)
+ != write_size) {
+ // Wouldn't be a surprise if writing to stderr
+ // would fail too but at least try to show an
+ // error message.
+ fprintf(stderr, "%s: Cannot write to "
+ "standard output: %s\n", argv0,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ strm->next_out = out_buf;
+ strm->avail_out = BUFSIZ;
+ }
+
+ if (ret != LZMA_OK) {
+ if (ret == LZMA_STREAM_END) {
+#ifdef LZMADEC
+ // Check that there's no trailing garbage.
+ if (strm->avail_in != 0
+ || fread(in_buf, 1, 1, file)
+ != 0
+ || !feof(file))
+ ret = LZMA_DATA_ERROR;
+ else
+ return;
+#else
+ // lzma_stream_decoder() already guarantees
+ // that there's no trailing garbage.
+ assert(strm->avail_in == 0);
+ assert(action == LZMA_FINISH);
+ assert(feof(file));
+ return;
+#endif
+ }
+
+ const char *msg;
+ switch (ret) {
+ case LZMA_MEM_ERROR:
+ msg = strerror(ENOMEM);
+ break;
+
+ case LZMA_MEMLIMIT_ERROR:
+ msg = "Memory usage limit reached";
+ break;
+
+ case LZMA_FORMAT_ERROR:
+ msg = "File format not recognized";
+ break;
+
+ case LZMA_OPTIONS_ERROR:
+ // FIXME: Better message?
+ msg = "Unsupported compression options";
+ break;
+
+ case LZMA_DATA_ERROR:
+ msg = "File is corrupt";
+ break;
+
+ case LZMA_BUF_ERROR:
+ msg = "Unexpected end of input";
+ break;
+
+ default:
+ msg = "Internal program error (bug)";
+ break;
+ }
+
+ fprintf(stderr, "%s: %s: %s\n", argv0, filename, msg);
+
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+
+int
+main(int argc, char **argv)
+{
+ // Set the argv0 global so that we can print the command name in
+ // error and help messages.
+ argv0 = argv[0];
+
+ // Detect amount of installed RAM and set the memory usage limit.
+ // This is needed before parsing the command line arguments.
+ set_default_memlimit();
+
+ // Parse the command line options.
+ parse_options(argc, argv);
+
+ // The same lzma_stream is used for all files that we decode. This way
+ // we don't need to reallocate memory for every file if they use same
+ // compression settings.
+ lzma_stream strm = LZMA_STREAM_INIT;
+
+ // Some systems require setting stdin and stdout to binary mode.
+#ifdef _WIN32
+ setmode(fileno(stdin), O_BINARY);
+ setmode(fileno(stdout), O_BINARY);
+#endif
+
+ if (optind == argc) {
+ // No filenames given, decode from stdin.
+ uncompress(&strm, stdin, "(stdin)");
+ } else {
+ // Loop through the filenames given on the command line.
+ do {
+ // "-" indicates stdin.
+ if (strcmp(argv[optind], "-") == 0) {
+ uncompress(&strm, stdin, "(stdin)");
+ } else {
+ FILE *file = fopen(argv[optind], "rb");
+ if (file == NULL) {
+ fprintf(stderr, "%s: %s: %s\n",
+ argv0, argv[optind],
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ uncompress(&strm, file, argv[optind]);
+ fclose(file);
+ }
+ } while (++optind < argc);
+ }
+
+#ifndef NDEBUG
+ // Free the memory only when debugging. Freeing wastes some time,
+ // but allows detecting possible memory leaks with Valgrind.
+ lzma_end(&strm);
+#endif
+
+ my_exit();
+}