diff options
Diffstat (limited to '')
-rw-r--r-- | src/xz/options.c | 358 |
1 files changed, 358 insertions, 0 deletions
diff --git a/src/xz/options.c b/src/xz/options.c new file mode 100644 index 0000000..b434b0c --- /dev/null +++ b/src/xz/options.c @@ -0,0 +1,358 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file options.c +/// \brief Parser for filter-specific options +// +// Author: Lasse Collin +// +// This file has been put into the public domain. +// You can do whatever you want with this file. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "private.h" + + +/////////////////// +// Generic stuff // +/////////////////// + +typedef struct { + const char *name; + uint64_t id; +} name_id_map; + + +typedef struct { + const char *name; + const name_id_map *map; + uint64_t min; + uint64_t max; +} option_map; + + +/// Parses option=value pairs that are separated with commas: +/// opt=val,opt=val,opt=val +/// +/// Each option is a string, that is converted to an integer using the +/// index where the option string is in the array. +/// +/// Value can be +/// - a string-id map mapping a list of possible string values to integers +/// (opts[i].map != NULL, opts[i].min and opts[i].max are ignored); +/// - a number with minimum and maximum value limit +/// (opts[i].map == NULL && opts[i].min != UINT64_MAX); +/// - a string that will be parsed by the filter-specific code +/// (opts[i].map == NULL && opts[i].min == UINT64_MAX, opts[i].max ignored) +/// +/// When parsing both option and value succeed, a filter-specific function +/// is called, which should update the given value to filter-specific +/// options structure. +/// +/// \param str String containing the options from the command line +/// \param opts Filter-specific option map +/// \param set Filter-specific function to update filter_options +/// \param filter_options Pointer to filter-specific options structure +/// +/// \return Returns only if no errors occur. +/// +static void +parse_options(const char *str, const option_map *opts, + void (*set)(void *filter_options, + unsigned key, uint64_t value, const char *valuestr), + void *filter_options) +{ + if (str == NULL || str[0] == '\0') + return; + + char *s = xstrdup(str); + char *name = s; + + while (*name != '\0') { + if (*name == ',') { + ++name; + continue; + } + + char *split = strchr(name, ','); + if (split != NULL) + *split = '\0'; + + char *value = strchr(name, '='); + if (value != NULL) + *value++ = '\0'; + + if (value == NULL || value[0] == '\0') + message_fatal(_("%s: Options must be `name=value' " + "pairs separated with commas"), str); + + // Look for the option name from the option map. + unsigned i = 0; + while (true) { + if (opts[i].name == NULL) + message_fatal(_("%s: Invalid option name"), + name); + + if (strcmp(name, opts[i].name) == 0) + break; + + ++i; + } + + // Option was found from the map. See how we should handle it. + if (opts[i].map != NULL) { + // value is a string which we should map + // to an integer. + unsigned j; + for (j = 0; opts[i].map[j].name != NULL; ++j) { + if (strcmp(opts[i].map[j].name, value) == 0) + break; + } + + if (opts[i].map[j].name == NULL) + message_fatal(_("%s: Invalid option value"), + value); + + set(filter_options, i, opts[i].map[j].id, value); + + } else if (opts[i].min == UINT64_MAX) { + // value is a special string that will be + // parsed by set(). + set(filter_options, i, 0, value); + + } else { + // value is an integer. + const uint64_t v = str_to_uint64(name, value, + opts[i].min, opts[i].max); + set(filter_options, i, v, value); + } + + // Check if it was the last option. + if (split == NULL) + break; + + name = split + 1; + } + + free(s); + return; +} + + +/////////// +// Delta // +/////////// + +enum { + OPT_DIST, +}; + + +static void +set_delta(void *options, unsigned key, uint64_t value, + const char *valuestr lzma_attribute((__unused__))) +{ + lzma_options_delta *opt = options; + switch (key) { + case OPT_DIST: + opt->dist = value; + break; + } +} + + +extern lzma_options_delta * +options_delta(const char *str) +{ + static const option_map opts[] = { + { "dist", NULL, LZMA_DELTA_DIST_MIN, + LZMA_DELTA_DIST_MAX }, + { NULL, NULL, 0, 0 } + }; + + lzma_options_delta *options = xmalloc(sizeof(lzma_options_delta)); + *options = (lzma_options_delta){ + // It's hard to give a useful default for this. + .type = LZMA_DELTA_TYPE_BYTE, + .dist = LZMA_DELTA_DIST_MIN, + }; + + parse_options(str, opts, &set_delta, options); + + return options; +} + + +///////// +// BCJ // +///////// + +enum { + OPT_START_OFFSET, +}; + + +static void +set_bcj(void *options, unsigned key, uint64_t value, + const char *valuestr lzma_attribute((__unused__))) +{ + lzma_options_bcj *opt = options; + switch (key) { + case OPT_START_OFFSET: + opt->start_offset = value; + break; + } +} + + +extern lzma_options_bcj * +options_bcj(const char *str) +{ + static const option_map opts[] = { + { "start", NULL, 0, UINT32_MAX }, + { NULL, NULL, 0, 0 } + }; + + lzma_options_bcj *options = xmalloc(sizeof(lzma_options_bcj)); + *options = (lzma_options_bcj){ + .start_offset = 0, + }; + + parse_options(str, opts, &set_bcj, options); + + return options; +} + + +////////// +// LZMA // +////////// + +enum { + OPT_PRESET, + OPT_DICT, + OPT_LC, + OPT_LP, + OPT_PB, + OPT_MODE, + OPT_NICE, + OPT_MF, + OPT_DEPTH, +}; + + +static void lzma_attribute((__noreturn__)) +error_lzma_preset(const char *valuestr) +{ + message_fatal(_("Unsupported LZMA1/LZMA2 preset: %s"), valuestr); +} + + +static void +set_lzma(void *options, unsigned key, uint64_t value, const char *valuestr) +{ + lzma_options_lzma *opt = options; + + switch (key) { + case OPT_PRESET: { + if (valuestr[0] < '0' || valuestr[0] > '9') + error_lzma_preset(valuestr); + + uint32_t preset = (uint32_t)(valuestr[0] - '0'); + + // Currently only "e" is supported as a modifier, + // so keep this simple for now. + if (valuestr[1] != '\0') { + if (valuestr[1] == 'e') + preset |= LZMA_PRESET_EXTREME; + else + error_lzma_preset(valuestr); + + if (valuestr[2] != '\0') + error_lzma_preset(valuestr); + } + + if (lzma_lzma_preset(options, preset)) + error_lzma_preset(valuestr); + + break; + } + + case OPT_DICT: + opt->dict_size = value; + break; + + case OPT_LC: + opt->lc = value; + break; + + case OPT_LP: + opt->lp = value; + break; + + case OPT_PB: + opt->pb = value; + break; + + case OPT_MODE: + opt->mode = value; + break; + + case OPT_NICE: + opt->nice_len = value; + break; + + case OPT_MF: + opt->mf = value; + break; + + case OPT_DEPTH: + opt->depth = value; + break; + } +} + + +extern lzma_options_lzma * +options_lzma(const char *str) +{ + static const name_id_map modes[] = { + { "fast", LZMA_MODE_FAST }, + { "normal", LZMA_MODE_NORMAL }, + { NULL, 0 } + }; + + static const name_id_map mfs[] = { + { "hc3", LZMA_MF_HC3 }, + { "hc4", LZMA_MF_HC4 }, + { "bt2", LZMA_MF_BT2 }, + { "bt3", LZMA_MF_BT3 }, + { "bt4", LZMA_MF_BT4 }, + { NULL, 0 } + }; + + static const option_map opts[] = { + { "preset", NULL, UINT64_MAX, 0 }, + { "dict", NULL, LZMA_DICT_SIZE_MIN, + (UINT32_C(1) << 30) + (UINT32_C(1) << 29) }, + { "lc", NULL, LZMA_LCLP_MIN, LZMA_LCLP_MAX }, + { "lp", NULL, LZMA_LCLP_MIN, LZMA_LCLP_MAX }, + { "pb", NULL, LZMA_PB_MIN, LZMA_PB_MAX }, + { "mode", modes, 0, 0 }, + { "nice", NULL, 2, 273 }, + { "mf", mfs, 0, 0 }, + { "depth", NULL, 0, UINT32_MAX }, + { NULL, NULL, 0, 0 } + }; + + lzma_options_lzma *options = xmalloc(sizeof(lzma_options_lzma)); + if (lzma_lzma_preset(options, LZMA_PRESET_DEFAULT)) + message_bug(); + + parse_options(str, opts, &set_lzma, options); + + if (options->lc + options->lp > LZMA_LCLP_MAX) + message_fatal(_("The sum of lc and lp must not exceed 4")); + + return options; +} |