aboutsummaryrefslogtreecommitdiff
path: root/src/lzma/args.c
diff options
context:
space:
mode:
authorLasse Collin <lasse.collin@tukaani.org>2007-12-09 00:42:33 +0200
committerLasse Collin <lasse.collin@tukaani.org>2007-12-09 00:42:33 +0200
commit5d018dc03549c1ee4958364712fb0c94e1bf2741 (patch)
tree1b211911fb33fddb3f04b77f99e81df23623ffc4 /src/lzma/args.c
downloadxz-5d018dc03549c1ee4958364712fb0c94e1bf2741.tar.xz
Imported to git.
Diffstat (limited to 'src/lzma/args.c')
-rw-r--r--src/lzma/args.c566
1 files changed, 566 insertions, 0 deletions
diff --git a/src/lzma/args.c b/src/lzma/args.c
new file mode 100644
index 00000000..d6163ae7
--- /dev/null
+++ b/src/lzma/args.c
@@ -0,0 +1,566 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file args.c
+/// \brief Argument parsing
+///
+/// \note Filter-specific options parsing is in options.c.
+//
+// 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 "private.h"
+
+#include "getopt.h"
+#include <ctype.h>
+
+
+enum tool_mode opt_mode = MODE_COMPRESS;
+enum header_type opt_header = HEADER_AUTO;
+
+char *opt_suffix = NULL;
+
+char *opt_files_name = NULL;
+char opt_files_split = '\0';
+FILE *opt_files_file = NULL;
+
+bool opt_stdout = false;
+bool opt_force = false;
+bool opt_keep_original = false;
+bool opt_preserve_name = false;
+
+lzma_check_type opt_check = LZMA_CHECK_CRC64;
+lzma_options_filter opt_filters[8];
+
+// We don't modify or free() this, but we need to assign it in some
+// non-const pointers.
+const char *stdin_filename = "(stdin)";
+
+static size_t preset_number = 7 - 1;
+static bool preset_default = true;
+static size_t filter_count = 0;
+
+
+enum {
+ OPT_COPY = INT_MIN,
+ OPT_SUBBLOCK,
+ OPT_X86,
+ OPT_POWERPC,
+ OPT_IA64,
+ OPT_ARM,
+ OPT_ARMTHUMB,
+ OPT_SPARC,
+ OPT_DELTA,
+ OPT_LZMA,
+
+ OPT_FILES,
+ OPT_FILES0,
+};
+
+
+static const char short_opts[] = "cC:dfFhlLkqrStT:vVz123456789";
+
+
+static const struct option long_opts[] = {
+ // gzip-like options
+ { "fast", no_argument, NULL, '1' },
+ { "best", no_argument, NULL, '9' },
+ { "memory", required_argument, NULL, 'M' },
+ { "name", no_argument, NULL, 'N' },
+ { "suffix", required_argument, NULL, 'S' },
+ { "threads", required_argument, NULL, 'T' },
+ { "version", no_argument, NULL, 'V' },
+ { "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' },
+ { "help", no_argument, NULL, 'h' },
+ { "list", no_argument, NULL, 'l' },
+ { "info", no_argument, NULL, 'l' },
+ { "keep", no_argument, NULL, 'k' },
+ { "no-name", no_argument, NULL, 'n' },
+ { "quiet", no_argument, NULL, 'q' },
+// { "recursive", no_argument, NULL, 'r' }, // TODO
+ { "test", no_argument, NULL, 't' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "compress", no_argument, NULL, 'z' },
+
+ // Filters
+ { "copy", no_argument, NULL, OPT_COPY },
+ { "subblock", optional_argument, NULL, OPT_SUBBLOCK },
+ { "x86", no_argument, NULL, OPT_X86 },
+ { "bcj", no_argument, NULL, OPT_X86 },
+ { "powerpc", no_argument, NULL, OPT_POWERPC },
+ { "ppc", no_argument, NULL, OPT_POWERPC },
+ { "ia64", no_argument, NULL, OPT_IA64 },
+ { "itanium", no_argument, NULL, OPT_IA64 },
+ { "arm", no_argument, NULL, OPT_ARM },
+ { "armthumb", no_argument, NULL, OPT_ARMTHUMB },
+ { "sparc", no_argument, NULL, OPT_SPARC },
+ { "delta", optional_argument, NULL, OPT_DELTA },
+ { "lzma", optional_argument, NULL, OPT_LZMA },
+
+ // Other
+ { "format", required_argument, NULL, 'F' },
+ { "check", required_argument, NULL, 'C' },
+ { "files", optional_argument, NULL, OPT_FILES },
+ { "files0", optional_argument, NULL, OPT_FILES0 },
+
+ { NULL, 0, NULL, 0 }
+};
+
+
+static void
+add_filter(lzma_vli id, const char *opt_str)
+{
+ if (filter_count == 7) {
+ errmsg(V_ERROR, _("Maximum number of filters is seven"));
+ my_exit(ERROR);
+ }
+
+ opt_filters[filter_count].id = id;
+
+ switch (id) {
+ case LZMA_FILTER_SUBBLOCK:
+ opt_filters[filter_count].options
+ = parse_options_subblock(opt_str);
+ break;
+
+ case LZMA_FILTER_DELTA:
+ opt_filters[filter_count].options
+ = parse_options_delta(opt_str);
+ break;
+
+ case LZMA_FILTER_LZMA:
+ opt_filters[filter_count].options
+ = parse_options_lzma(opt_str);
+ break;
+
+ default:
+ assert(opt_str == NULL);
+ opt_filters[filter_count].options = NULL;
+ break;
+ }
+
+ ++filter_count;
+ preset_default = false;
+ return;
+}
+
+
+static void
+parse_real(int argc, char **argv)
+{
+ int c;
+
+ while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL))
+ != -1) {
+ switch (c) {
+ // gzip-like options
+
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ preset_number = c - '1';
+ preset_default = false;
+ break;
+
+ // --memory
+ case 'M':
+ opt_memory = str_to_uint64("memory", optarg,
+ 1, SIZE_MAX);
+ break;
+
+ case 'N':
+ opt_preserve_name = true;
+ break;
+
+ // --suffix
+ case 'S':
+ // Empty suffix and suffixes having a slash are
+ // rejected. Such suffixes would break things later.
+ if (optarg[0] == '\0' || strchr(optarg, '/') != NULL) {
+ errmsg(V_ERROR, _("%s: Invalid filename "
+ "suffix"), optarg);
+ my_exit(ERROR);
+ }
+
+ free(opt_suffix);
+ opt_suffix = xstrdup(optarg);
+ break;
+
+ case 'T':
+ opt_threads = str_to_uint64("threads", optarg,
+ 1, SIZE_MAX);
+ break;
+
+ // --version
+ case 'V':
+ // This doesn't return.
+ show_version();
+
+ // --stdout
+ case 'c':
+ opt_stdout = true;
+ break;
+
+ // --decompress
+ case 'd':
+ opt_mode = MODE_DECOMPRESS;
+ break;
+
+ // --force
+ case 'f':
+ opt_force = true;
+ break;
+
+ // --help
+ case 'h':
+ // This doesn't return.
+ show_help();
+
+ // --list
+ case 'l':
+ opt_mode = MODE_LIST;
+ break;
+
+ // --keep
+ case 'k':
+ opt_keep_original = true;
+ break;
+
+ case 'n':
+ opt_preserve_name = false;
+ break;
+
+ // --quiet
+ case 'q':
+ if (verbosity > V_SILENT)
+ --verbosity;
+
+ break;
+
+ case 't':
+ opt_mode = MODE_TEST;
+ break;
+
+ // --verbose
+ case 'v':
+ if (verbosity < V_DEBUG)
+ ++verbosity;
+
+ break;
+
+ case 'z':
+ opt_mode = MODE_COMPRESS;
+ break;
+
+ // Filter setup
+
+ case OPT_COPY:
+ add_filter(LZMA_FILTER_COPY, NULL);
+ break;
+
+ case OPT_SUBBLOCK:
+ add_filter(LZMA_FILTER_SUBBLOCK, optarg);
+ break;
+
+ case OPT_X86:
+ add_filter(LZMA_FILTER_X86, NULL);
+ break;
+
+ case OPT_POWERPC:
+ add_filter(LZMA_FILTER_POWERPC, NULL);
+ break;
+
+ case OPT_IA64:
+ add_filter(LZMA_FILTER_IA64, NULL);
+ break;
+
+ case OPT_ARM:
+ add_filter(LZMA_FILTER_ARM, NULL);
+ break;
+
+ case OPT_ARMTHUMB:
+ add_filter(LZMA_FILTER_ARMTHUMB, NULL);
+ break;
+
+ case OPT_SPARC:
+ add_filter(LZMA_FILTER_SPARC, NULL);
+ break;
+
+ case OPT_DELTA:
+ add_filter(LZMA_FILTER_DELTA, optarg);
+ break;
+
+ case OPT_LZMA:
+ add_filter(LZMA_FILTER_LZMA, optarg);
+ break;
+
+ // Other
+
+ // --format
+ case 'F': {
+ static const char *types[] = {
+ "auto",
+ "native",
+ "single",
+ "multi",
+ "alone",
+// "gzip",
+ NULL
+ };
+
+ opt_header = 0;
+ while (strcmp(types[opt_header], optarg) != 0) {
+ if (types[++opt_header] == NULL) {
+ errmsg(V_ERROR, _("%s: Unknown file "
+ "format type"),
+ optarg);
+ my_exit(ERROR);
+ }
+ }
+
+ break;
+ }
+
+ // --check
+ case 'C': {
+ static const struct {
+ const char *str;
+ unsigned int value;
+ } types[] = {
+ { "none", LZMA_CHECK_NONE },
+ { "crc32", LZMA_CHECK_CRC32 },
+ { "crc64", LZMA_CHECK_CRC64 },
+ { "sha256", LZMA_CHECK_SHA256 },
+ { NULL, 0 }
+ };
+
+ size_t i = 0;
+ while (strcmp(types[i].str, optarg) != 0) {
+ if (types[++i].str == NULL) {
+ errmsg(V_ERROR, _("%s: Unknown "
+ "integrity check "
+ "type"), optarg);
+ my_exit(ERROR);
+ }
+ }
+
+ opt_check = types[i].value;
+ break;
+ }
+
+ case OPT_FILES:
+ opt_files_split = '\n';
+
+ // Fall through
+
+ case OPT_FILES0:
+ if (opt_files_name != NULL) {
+ errmsg(V_ERROR, _("Only one file can be "
+ "specified with `--files'"
+ "or `--files0'."));
+ my_exit(ERROR);
+ }
+
+ if (optarg == NULL) {
+ opt_files_name = (char *)stdin_filename;
+ opt_files_file = stdin;
+ } else {
+ opt_files_name = optarg;
+ opt_files_file = fopen(optarg,
+ c == OPT_FILES ? "r" : "rb");
+ if (opt_files_file == NULL) {
+ errmsg(V_ERROR, "%s: %s", optarg,
+ strerror(errno));
+ my_exit(ERROR);
+ }
+ }
+
+ break;
+
+ default:
+ show_try_help();
+ my_exit(ERROR);
+ }
+ }
+
+ return;
+}
+
+
+static void
+parse_environment(void)
+{
+ char *env = getenv("LZMA_OPT");
+ if (env == NULL)
+ return;
+
+ env = xstrdup(env);
+
+ // Calculate the number of arguments in env.
+ unsigned int argc = 1;
+ bool prev_was_space = true;
+ for (size_t i = 0; env[i] != '\0'; ++i) {
+ if (isspace(env[i])) {
+ prev_was_space = true;
+ } else if (prev_was_space) {
+ prev_was_space = false;
+ if (++argc > (unsigned int)(INT_MAX)) {
+ errmsg(V_ERROR, _("The environment variable "
+ "LZMA_OPT contains too many "
+ "arguments"));
+ my_exit(ERROR);
+ }
+ }
+ }
+
+ char **argv = xmalloc((argc + 1) * sizeof(char*));
+ argv[0] = argv0;
+ argv[argc] = NULL;
+
+ argc = 1;
+ prev_was_space = true;
+ for (size_t i = 0; env[i] != '\0'; ++i) {
+ if (isspace(env[i])) {
+ prev_was_space = true;
+ } else if (prev_was_space) {
+ prev_was_space = false;
+ argv[argc++] = env + i;
+ }
+ }
+
+ parse_real((int)(argc), argv);
+
+ free(env);
+
+ return;
+}
+
+
+static void
+set_compression_settings(void)
+{
+ if (filter_count == 0) {
+ opt_filters[0].id = LZMA_FILTER_LZMA;
+ opt_filters[0].options = (lzma_options_lzma *)(
+ lzma_preset_lzma + preset_number);
+ filter_count = 1;
+ }
+
+ // Terminate the filter options array.
+ opt_filters[filter_count].id = LZMA_VLI_VALUE_UNKNOWN;
+
+ // Optimize the filter chain a little by removing all
+ // Copy filters.
+ for (size_t i = 0; opt_filters[i].id != LZMA_VLI_VALUE_UNKNOWN; ++i) {
+ while (opt_filters[i].id == LZMA_FILTER_COPY) {
+ size_t j = i;
+ do {
+ opt_filters[j] = opt_filters[j + 1];
+ } while (opt_filters[++j].id
+ != LZMA_VLI_VALUE_UNKNOWN);
+ }
+ }
+
+ const uint32_t memory_limit = opt_memory / (1024 * 1024) + 1;
+ uint32_t memory_usage = lzma_memory_usage(opt_filters, true);
+
+ // Don't go over the memory limits when the default
+ // setting is used.
+ if (preset_default) {
+ while (memory_usage > memory_limit) {
+ if (preset_number == 0) {
+ errmsg(V_ERROR, _("Memory usage limit is too "
+ "small for any internal "
+ "filter preset"));
+ my_exit(ERROR);
+ }
+
+ --preset_number;
+ opt_filters[0].options = (lzma_options_lzma *)(
+ lzma_preset_lzma
+ + preset_number);
+ memory_usage = lzma_memory_usage(opt_filters,
+ true);
+ }
+ } else {
+ if (memory_usage > memory_limit) {
+ errmsg(V_ERROR, _("Memory usage limit is too small "
+ "for the given filter setup"));
+ my_exit(ERROR);
+ }
+ }
+
+ // Limit the number of worked threads so that memory usage
+ // limit isn't exceeded.
+ // FIXME: Probably should use bytes instead of mebibytes for
+ // memory_usage and memory_limit.
+ if (memory_usage == 0)
+ memory_usage = 1;
+
+ size_t thread_limit = memory_limit / memory_usage;
+ if (thread_limit == 0)
+ thread_limit = 1;
+
+ if (opt_threads > thread_limit)
+ opt_threads = thread_limit;
+
+ return;
+}
+
+
+extern char **
+parse_args(int argc, char **argv)
+{
+ // Check how we were called.
+ {
+ const char *name = str_filename(argv[0]);
+ if (name != NULL) {
+ if (strstr(name, "cat") != NULL) {
+ opt_mode = MODE_DECOMPRESS;
+ opt_stdout = true;
+ } else if (strstr(name, "un") != NULL) {
+ opt_mode = MODE_DECOMPRESS;
+ }
+ }
+ }
+
+ // First the flags from environment
+ parse_environment();
+
+ // Then from the command line
+ optind = 1;
+ parse_real(argc, argv);
+
+ // Never remove the source file when the destination is not on disk.
+ // In test mode the data is written nowhere, but setting opt_stdout
+ // will make the rest of the code behave well.
+ if (opt_stdout || opt_mode == MODE_TEST) {
+ opt_keep_original = true;
+ opt_stdout = true;
+ }
+
+ if (opt_mode == MODE_COMPRESS)
+ set_compression_settings();
+
+ // If no filenames are given, use stdin.
+ if (argv[optind] == NULL && opt_files_name == NULL) {
+ // We don't modify or free() the "-" constant.
+ static char *argv_stdin[2] = { (char *)"-", NULL };
+ return argv_stdin;
+ }
+
+ return argv + optind;
+}