Index: xz/src/xz/options.c |
=================================================================== |
--- xz/src/xz/options.c (revision 0) |
+++ xz/src/xz/options.c (revision 0) |
@@ -0,0 +1,374 @@ |
+/////////////////////////////////////////////////////////////////////////////// |
+// |
+/// \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 colons, semicolons, |
+/// or commas: opt=val: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, |
+ uint32_t 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. |
+ size_t 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. |
+ size_t 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, uint32_t 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, uint32_t 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, uint32_t 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 = 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)); |
+ *options = (lzma_options_lzma){ |
+ .dict_size = LZMA_DICT_SIZE_DEFAULT, |
+ .preset_dict = NULL, |
+ .preset_dict_size = 0, |
+ .lc = LZMA_LC_DEFAULT, |
+ .lp = LZMA_LP_DEFAULT, |
+ .pb = LZMA_PB_DEFAULT, |
+ .mode = LZMA_MODE_NORMAL, |
+ .nice_len = 64, |
+ .mf = LZMA_MF_BT4, |
+ .depth = 0, |
+ }; |
+ |
+ parse_options(str, opts, &set_lzma, options); |
+ |
+ if (options->lc + options->lp > LZMA_LCLP_MAX) |
+ message_fatal(_("The sum of lc and lp must be at " |
+ "maximum of 4")); |
+ |
+ const uint32_t nice_len_min = options->mf & 0x0F; |
+ if (options->nice_len < nice_len_min) |
+ message_fatal(_("The selected match finder requires at " |
+ "least nice=%" PRIu32), nice_len_min); |
+ |
+ return options; |
+} |
Property changes on: xz/src/xz/options.c |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |