| Index: xz/src/xz/args.c
|
| ===================================================================
|
| --- xz/src/xz/args.c (revision 0)
|
| +++ xz/src/xz/args.c (revision 0)
|
| @@ -0,0 +1,549 @@
|
| +///////////////////////////////////////////////////////////////////////////////
|
| +//
|
| +/// \file args.c
|
| +/// \brief Argument parsing
|
| +///
|
| +/// \note Filter-specific options parsing is in options.c.
|
| +//
|
| +// Author: Lasse Collin
|
| +//
|
| +// This file has been put into the public domain.
|
| +// You can do whatever you want with this file.
|
| +//
|
| +///////////////////////////////////////////////////////////////////////////////
|
| +
|
| +#include "private.h"
|
| +
|
| +#include "getopt.h"
|
| +#include <ctype.h>
|
| +
|
| +
|
| +bool opt_stdout = false;
|
| +bool opt_force = false;
|
| +bool opt_keep_original = false;
|
| +bool opt_robot = false;
|
| +
|
| +// We don't modify or free() this, but we need to assign it in some
|
| +// non-const pointers.
|
| +const char *stdin_filename = "(stdin)";
|
| +
|
| +
|
| +static void
|
| +parse_real(args_info *args, int argc, char **argv)
|
| +{
|
| + enum {
|
| + OPT_X86 = INT_MIN,
|
| + OPT_POWERPC,
|
| + OPT_IA64,
|
| + OPT_ARM,
|
| + OPT_ARMTHUMB,
|
| + OPT_SPARC,
|
| + OPT_DELTA,
|
| + OPT_LZMA1,
|
| + OPT_LZMA2,
|
| +
|
| + OPT_NO_SPARSE,
|
| + OPT_FILES,
|
| + OPT_FILES0,
|
| + OPT_NO_ADJUST,
|
| + OPT_INFO_MEMORY,
|
| + OPT_ROBOT,
|
| + };
|
| +
|
| + static const char short_opts[]
|
| + = "cC:defF:hHlkM:qQrS:tT:vVz0123456789";
|
| +
|
| + static const struct option long_opts[] = {
|
| + // Operation mode
|
| + { "compress", no_argument, NULL, 'z' },
|
| + { "decompress", no_argument, NULL, 'd' },
|
| + { "uncompress", no_argument, NULL, 'd' },
|
| + { "test", no_argument, NULL, 't' },
|
| + { "list", no_argument, NULL, 'l' },
|
| +
|
| + // Operation modifiers
|
| + { "keep", no_argument, NULL, 'k' },
|
| + { "force", no_argument, NULL, 'f' },
|
| + { "stdout", no_argument, NULL, 'c' },
|
| + { "to-stdout", no_argument, NULL, 'c' },
|
| + { "no-sparse", no_argument, NULL, OPT_NO_SPARSE },
|
| + { "suffix", required_argument, NULL, 'S' },
|
| + // { "recursive", no_argument, NULL, 'r' }, // TODO
|
| + { "files", optional_argument, NULL, OPT_FILES },
|
| + { "files0", optional_argument, NULL, OPT_FILES0 },
|
| +
|
| + // Basic compression settings
|
| + { "format", required_argument, NULL, 'F' },
|
| + { "check", required_argument, NULL, 'C' },
|
| + { "no-adjust", no_argument, NULL, OPT_NO_ADJUST },
|
| + { "memory", required_argument, NULL, 'M' },
|
| + { "threads", required_argument, NULL, 'T' },
|
| +
|
| + { "extreme", no_argument, NULL, 'e' },
|
| + { "fast", no_argument, NULL, '0' },
|
| + { "best", no_argument, NULL, '9' },
|
| +
|
| + // Filters
|
| + { "lzma1", optional_argument, NULL, OPT_LZMA1 },
|
| + { "lzma2", optional_argument, NULL, OPT_LZMA2 },
|
| + { "x86", optional_argument, NULL, OPT_X86 },
|
| + { "powerpc", optional_argument, NULL, OPT_POWERPC },
|
| + { "ia64", optional_argument, NULL, OPT_IA64 },
|
| + { "arm", optional_argument, NULL, OPT_ARM },
|
| + { "armthumb", optional_argument, NULL, OPT_ARMTHUMB },
|
| + { "sparc", optional_argument, NULL, OPT_SPARC },
|
| + { "delta", optional_argument, NULL, OPT_DELTA },
|
| +
|
| + // Other options
|
| + { "quiet", no_argument, NULL, 'q' },
|
| + { "verbose", no_argument, NULL, 'v' },
|
| + { "no-warn", no_argument, NULL, 'Q' },
|
| + { "robot", no_argument, NULL, OPT_ROBOT },
|
| + { "info-memory", no_argument, NULL, OPT_INFO_MEMORY },
|
| + { "help", no_argument, NULL, 'h' },
|
| + { "long-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) {
|
| + // Compression preset (also for decompression if --format=raw)
|
| + case '0': case '1': case '2': case '3': case '4':
|
| + case '5': case '6': case '7': case '8': case '9':
|
| + coder_set_preset(c - '0');
|
| + break;
|
| +
|
| + // --memory
|
| + case 'M': {
|
| + // Support specifying the limit as a percentage of
|
| + // installed physical RAM.
|
| + size_t len = strlen(optarg);
|
| + if (len > 0 && optarg[len - 1] == '%') {
|
| + optarg[len - 1] = '\0';
|
| + hardware_memlimit_set_percentage(
|
| + str_to_uint64(
|
| + "memory%", optarg, 1, 100));
|
| + } else {
|
| + // On 32-bit systems, SIZE_MAX would make more
|
| + // sense than UINT64_MAX. But use UINT64_MAX
|
| + // still so that scripts that assume > 4 GiB
|
| + // values don't break.
|
| + hardware_memlimit_set(str_to_uint64(
|
| + "memory", optarg,
|
| + 0, UINT64_MAX));
|
| + }
|
| +
|
| + break;
|
| + }
|
| +
|
| + // --suffix
|
| + case 'S':
|
| + suffix_set(optarg);
|
| + break;
|
| +
|
| + case 'T':
|
| + hardware_threadlimit_set(str_to_uint64(
|
| + "threads", optarg, 0, UINT32_MAX));
|
| + break;
|
| +
|
| + // --version
|
| + case 'V':
|
| + // This doesn't return.
|
| + message_version();
|
| +
|
| + // --stdout
|
| + case 'c':
|
| + opt_stdout = true;
|
| + break;
|
| +
|
| + // --decompress
|
| + case 'd':
|
| + opt_mode = MODE_DECOMPRESS;
|
| + break;
|
| +
|
| + // --extreme
|
| + case 'e':
|
| + coder_set_extreme();
|
| + break;
|
| +
|
| + // --force
|
| + case 'f':
|
| + opt_force = true;
|
| + break;
|
| +
|
| + // --info-memory
|
| + case OPT_INFO_MEMORY:
|
| + // This doesn't return.
|
| + message_memlimit();
|
| +
|
| + // --help
|
| + case 'h':
|
| + // This doesn't return.
|
| + message_help(false);
|
| +
|
| + // --long-help
|
| + case 'H':
|
| + // This doesn't return.
|
| + message_help(true);
|
| +
|
| + // --list
|
| + case 'l':
|
| + opt_mode = MODE_LIST;
|
| + break;
|
| +
|
| + // --keep
|
| + case 'k':
|
| + opt_keep_original = true;
|
| + break;
|
| +
|
| + // --quiet
|
| + case 'q':
|
| + message_verbosity_decrease();
|
| + break;
|
| +
|
| + case 'Q':
|
| + set_exit_no_warn();
|
| + break;
|
| +
|
| + case 't':
|
| + opt_mode = MODE_TEST;
|
| + break;
|
| +
|
| + // --verbose
|
| + case 'v':
|
| + message_verbosity_increase();
|
| + break;
|
| +
|
| + // --robot
|
| + case OPT_ROBOT:
|
| + opt_robot = true;
|
| +
|
| + // This is to make sure that floating point numbers
|
| + // always have a dot as decimal separator.
|
| + setlocale(LC_NUMERIC, "C");
|
| + break;
|
| +
|
| + case 'z':
|
| + opt_mode = MODE_COMPRESS;
|
| + break;
|
| +
|
| + // Filter setup
|
| +
|
| + case OPT_X86:
|
| + coder_add_filter(LZMA_FILTER_X86,
|
| + options_bcj(optarg));
|
| + break;
|
| +
|
| + case OPT_POWERPC:
|
| + coder_add_filter(LZMA_FILTER_POWERPC,
|
| + options_bcj(optarg));
|
| + break;
|
| +
|
| + case OPT_IA64:
|
| + coder_add_filter(LZMA_FILTER_IA64,
|
| + options_bcj(optarg));
|
| + break;
|
| +
|
| + case OPT_ARM:
|
| + coder_add_filter(LZMA_FILTER_ARM,
|
| + options_bcj(optarg));
|
| + break;
|
| +
|
| + case OPT_ARMTHUMB:
|
| + coder_add_filter(LZMA_FILTER_ARMTHUMB,
|
| + options_bcj(optarg));
|
| + break;
|
| +
|
| + case OPT_SPARC:
|
| + coder_add_filter(LZMA_FILTER_SPARC,
|
| + options_bcj(optarg));
|
| + break;
|
| +
|
| + case OPT_DELTA:
|
| + coder_add_filter(LZMA_FILTER_DELTA,
|
| + options_delta(optarg));
|
| + break;
|
| +
|
| + case OPT_LZMA1:
|
| + coder_add_filter(LZMA_FILTER_LZMA1,
|
| + options_lzma(optarg));
|
| + break;
|
| +
|
| + case OPT_LZMA2:
|
| + coder_add_filter(LZMA_FILTER_LZMA2,
|
| + options_lzma(optarg));
|
| + break;
|
| +
|
| + // Other
|
| +
|
| + // --format
|
| + case 'F': {
|
| + // Just in case, support both "lzma" and "alone" since
|
| + // the latter was used for forward compatibility in
|
| + // LZMA Utils 4.32.x.
|
| + static const struct {
|
| + char str[8];
|
| + enum format_type format;
|
| + } types[] = {
|
| + { "auto", FORMAT_AUTO },
|
| + { "xz", FORMAT_XZ },
|
| + { "lzma", FORMAT_LZMA },
|
| + { "alone", FORMAT_LZMA },
|
| + // { "gzip", FORMAT_GZIP },
|
| + // { "gz", FORMAT_GZIP },
|
| + { "raw", FORMAT_RAW },
|
| + };
|
| +
|
| + size_t i = 0;
|
| + while (strcmp(types[i].str, optarg) != 0)
|
| + if (++i == ARRAY_SIZE(types))
|
| + message_fatal(_("%s: Unknown file "
|
| + "format type"),
|
| + optarg);
|
| +
|
| + opt_format = types[i].format;
|
| + break;
|
| + }
|
| +
|
| + // --check
|
| + case 'C': {
|
| + static const struct {
|
| + char str[8];
|
| + lzma_check check;
|
| + } types[] = {
|
| + { "none", LZMA_CHECK_NONE },
|
| + { "crc32", LZMA_CHECK_CRC32 },
|
| + { "crc64", LZMA_CHECK_CRC64 },
|
| + { "sha256", LZMA_CHECK_SHA256 },
|
| + };
|
| +
|
| + size_t i = 0;
|
| + while (strcmp(types[i].str, optarg) != 0) {
|
| + if (++i == ARRAY_SIZE(types))
|
| + message_fatal(_("%s: Unsupported "
|
| + "integrity "
|
| + "check type"), optarg);
|
| + }
|
| +
|
| + // Use a separate check in case we are using different
|
| + // liblzma than what was used to compile us.
|
| + if (!lzma_check_is_supported(types[i].check))
|
| + message_fatal(_("%s: Unsupported integrity "
|
| + "check type"), optarg);
|
| +
|
| + coder_set_check(types[i].check);
|
| + break;
|
| + }
|
| +
|
| + case OPT_NO_SPARSE:
|
| + io_no_sparse();
|
| + break;
|
| +
|
| + case OPT_FILES:
|
| + args->files_delim = '\n';
|
| +
|
| + // Fall through
|
| +
|
| + case OPT_FILES0:
|
| + if (args->files_name != NULL)
|
| + message_fatal(_("Only one file can be "
|
| + "specified with `--files' "
|
| + "or `--files0'."));
|
| +
|
| + if (optarg == NULL) {
|
| + args->files_name = (char *)stdin_filename;
|
| + args->files_file = stdin;
|
| + } else {
|
| + args->files_name = optarg;
|
| + args->files_file = fopen(optarg,
|
| + c == OPT_FILES ? "r" : "rb");
|
| + if (args->files_file == NULL)
|
| + message_fatal("%s: %s", optarg,
|
| + strerror(errno));
|
| + }
|
| +
|
| + break;
|
| +
|
| + case OPT_NO_ADJUST:
|
| + opt_auto_adjust = false;
|
| + break;
|
| +
|
| + default:
|
| + message_try_help();
|
| + tuklib_exit(E_ERROR, E_ERROR, false);
|
| + }
|
| + }
|
| +
|
| + return;
|
| +}
|
| +
|
| +
|
| +static void
|
| +parse_environment(args_info *args, char *argv0)
|
| +{
|
| + char *env = getenv("XZ_OPT");
|
| + if (env == NULL)
|
| + return;
|
| +
|
| + // We modify the string, so make a copy of it.
|
| + env = xstrdup(env);
|
| +
|
| + // Calculate the number of arguments in env. argc stats at one
|
| + // to include space for the program name.
|
| + int argc = 1;
|
| + bool prev_was_space = true;
|
| + for (size_t i = 0; env[i] != '\0'; ++i) {
|
| + // NOTE: Cast to unsigned char is needed so that correct
|
| + // value gets passed to isspace(), which expects
|
| + // unsigned char cast to int. Casting to int is done
|
| + // automatically due to integer promotion, but we need to
|
| + // force char to unsigned char manually. Otherwise 8-bit
|
| + // characters would get promoted to wrong value if
|
| + // char is signed.
|
| + if (isspace((unsigned char)env[i])) {
|
| + prev_was_space = true;
|
| + } else if (prev_was_space) {
|
| + prev_was_space = false;
|
| +
|
| + // Keep argc small enough to fit into a singed int
|
| + // and to keep it usable for memory allocation.
|
| + if (++argc == my_min(
|
| + INT_MAX, SIZE_MAX / sizeof(char *)))
|
| + message_fatal(_("The environment variable "
|
| + "XZ_OPT contains too many "
|
| + "arguments"));
|
| + }
|
| + }
|
| +
|
| + // Allocate memory to hold pointers to the arguments. Add one to get
|
| + // space for the terminating NULL (if some systems happen to need it).
|
| + char **argv = xmalloc(((size_t)(argc) + 1) * sizeof(char *));
|
| + argv[0] = argv0;
|
| + argv[argc] = NULL;
|
| +
|
| + // Go through the string again. Split the arguments using '\0'
|
| + // characters and add pointers to the resulting strings to argv.
|
| + argc = 1;
|
| + prev_was_space = true;
|
| + for (size_t i = 0; env[i] != '\0'; ++i) {
|
| + if (isspace((unsigned char)env[i])) {
|
| + prev_was_space = true;
|
| + env[i] = '\0';
|
| + } else if (prev_was_space) {
|
| + prev_was_space = false;
|
| + argv[argc++] = env + i;
|
| + }
|
| + }
|
| +
|
| + // Parse the argument list we got from the environment. All non-option
|
| + // arguments i.e. filenames are ignored.
|
| + parse_real(args, argc, argv);
|
| +
|
| + // Reset the state of the getopt_long() so that we can parse the
|
| + // command line options too. There are two incompatible ways to
|
| + // do it.
|
| +#ifdef HAVE_OPTRESET
|
| + // BSD
|
| + optind = 1;
|
| + optreset = 1;
|
| +#else
|
| + // GNU, Solaris
|
| + optind = 0;
|
| +#endif
|
| +
|
| + // We don't need the argument list from environment anymore.
|
| + free(argv);
|
| + free(env);
|
| +
|
| + return;
|
| +}
|
| +
|
| +
|
| +extern void
|
| +args_parse(args_info *args, int argc, char **argv)
|
| +{
|
| + // Initialize those parts of *args that we need later.
|
| + args->files_name = NULL;
|
| + args->files_file = NULL;
|
| + args->files_delim = '\0';
|
| +
|
| + // Check how we were called.
|
| + {
|
| + // Remove the leading path name, if any.
|
| + const char *name = strrchr(argv[0], '/');
|
| + if (name == NULL)
|
| + name = argv[0];
|
| + else
|
| + ++name;
|
| +
|
| + // NOTE: It's possible that name[0] is now '\0' if argv[0]
|
| + // is weird, but it doesn't matter here.
|
| +
|
| + // Look for full command names instead of substrings like
|
| + // "un", "cat", and "lz" to reduce possibility of false
|
| + // positives when the programs have been renamed.
|
| + if (strstr(name, "xzcat") != NULL) {
|
| + opt_mode = MODE_DECOMPRESS;
|
| + opt_stdout = true;
|
| + } else if (strstr(name, "unxz") != NULL) {
|
| + opt_mode = MODE_DECOMPRESS;
|
| + } else if (strstr(name, "lzcat") != NULL) {
|
| + opt_format = FORMAT_LZMA;
|
| + opt_mode = MODE_DECOMPRESS;
|
| + opt_stdout = true;
|
| + } else if (strstr(name, "unlzma") != NULL) {
|
| + opt_format = FORMAT_LZMA;
|
| + opt_mode = MODE_DECOMPRESS;
|
| + } else if (strstr(name, "lzma") != NULL) {
|
| + opt_format = FORMAT_LZMA;
|
| + }
|
| + }
|
| +
|
| + // First the flags from environment
|
| + parse_environment(args, argv[0]);
|
| +
|
| + // Then from the command line
|
| + parse_real(args, 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;
|
| + }
|
| +
|
| + // When compressing, if no --format flag was used, or it
|
| + // was --format=auto, we compress to the .xz format.
|
| + if (opt_mode == MODE_COMPRESS && opt_format == FORMAT_AUTO)
|
| + opt_format = FORMAT_XZ;
|
| +
|
| + // Compression settings need to be validated (options themselves and
|
| + // their memory usage) when compressing to any file format. It has to
|
| + // be done also when uncompressing raw data, since for raw decoding
|
| + // the options given on the command line are used to know what kind
|
| + // of raw data we are supposed to decode.
|
| + if (opt_mode == MODE_COMPRESS || opt_format == FORMAT_RAW)
|
| + coder_set_compression_settings();
|
| +
|
| + // If no filenames are given, use stdin.
|
| + if (argv[optind] == NULL && args->files_name == NULL) {
|
| + // We don't modify or free() the "-" constant. The caller
|
| + // modifies this so don't make the struct itself const.
|
| + static char *names_stdin[2] = { (char *)"-", NULL };
|
| + args->arg_names = names_stdin;
|
| + args->arg_count = 1;
|
| + } else {
|
| + // We got at least one filename from the command line, or
|
| + // --files or --files0 was specified.
|
| + args->arg_names = argv + optind;
|
| + args->arg_count = argc - optind;
|
| + }
|
| +
|
| + return;
|
| +}
|
|
|
| Property changes on: xz/src/xz/args.c
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|