2007-12-08 23:42:33 +01:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
/// \file options.c
|
|
|
|
/// \brief Parser for filter-specific options
|
|
|
|
//
|
|
|
|
// 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"
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////
|
|
|
|
// 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 either a number with minimum and maximum value limit, or
|
|
|
|
/// a string-id map mapping a list of possible string values to integers.
|
|
|
|
///
|
|
|
|
/// 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),
|
|
|
|
void *filter_options)
|
|
|
|
{
|
|
|
|
if (str == NULL || str[0] == '\0')
|
|
|
|
return;
|
|
|
|
|
|
|
|
char *s = xstrdup(str);
|
|
|
|
char *name = s;
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
char *split = strchr(name, ',');
|
|
|
|
if (split != NULL)
|
|
|
|
*split = '\0';
|
|
|
|
|
|
|
|
char *value = strchr(name, '=');
|
|
|
|
if (value != NULL)
|
|
|
|
*value++ = '\0';
|
|
|
|
|
2008-11-19 19:46:52 +01:00
|
|
|
if (value == NULL || value[0] == '\0')
|
|
|
|
message_fatal(_("%s: Options must be `name=value' "
|
2008-09-27 18:09:21 +02:00
|
|
|
"pairs separated with commas"), str);
|
2007-12-08 23:42:33 +01:00
|
|
|
|
|
|
|
// Look for the option name from the option map.
|
|
|
|
bool found = false;
|
|
|
|
for (size_t i = 0; opts[i].name != NULL; ++i) {
|
|
|
|
if (strcmp(name, opts[i].name) != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (opts[i].map == NULL) {
|
|
|
|
// value is an integer.
|
|
|
|
const uint64_t v = str_to_uint64(name, value,
|
|
|
|
opts[i].min, opts[i].max);
|
|
|
|
set(filter_options, i, v);
|
|
|
|
} else {
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2008-11-19 19:46:52 +01:00
|
|
|
if (opts[i].map[j].name == NULL)
|
|
|
|
message_fatal(_("%s: Invalid option "
|
2007-12-08 23:42:33 +01:00
|
|
|
"value"), value);
|
|
|
|
|
2008-04-24 19:20:27 +02:00
|
|
|
set(filter_options, i, opts[i].map[j].id);
|
2007-12-08 23:42:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-11-19 19:46:52 +01:00
|
|
|
if (!found)
|
|
|
|
message_fatal(_("%s: Invalid option name"), name);
|
2007-12-08 23:42:33 +01:00
|
|
|
|
|
|
|
if (split == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
name = split + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////
|
|
|
|
// Subblock //
|
|
|
|
//////////////
|
|
|
|
|
|
|
|
enum {
|
|
|
|
OPT_SIZE,
|
|
|
|
OPT_RLE,
|
|
|
|
OPT_ALIGN,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
set_subblock(void *options, uint32_t key, uint64_t value)
|
|
|
|
{
|
|
|
|
lzma_options_subblock *opt = options;
|
|
|
|
|
|
|
|
switch (key) {
|
|
|
|
case OPT_SIZE:
|
|
|
|
opt->subblock_data_size = value;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OPT_RLE:
|
|
|
|
opt->rle = value;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OPT_ALIGN:
|
|
|
|
opt->alignment = value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
extern lzma_options_subblock *
|
2008-11-19 19:46:52 +01:00
|
|
|
options_subblock(const char *str)
|
2007-12-08 23:42:33 +01:00
|
|
|
{
|
|
|
|
static const option_map opts[] = {
|
|
|
|
{ "size", NULL, LZMA_SUBBLOCK_DATA_SIZE_MIN,
|
|
|
|
LZMA_SUBBLOCK_DATA_SIZE_MAX },
|
|
|
|
{ "rle", NULL, LZMA_SUBBLOCK_RLE_OFF,
|
|
|
|
LZMA_SUBBLOCK_RLE_MAX },
|
|
|
|
{ "align",NULL, LZMA_SUBBLOCK_ALIGNMENT_MIN,
|
|
|
|
LZMA_SUBBLOCK_ALIGNMENT_MAX },
|
|
|
|
{ NULL, NULL, 0, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
lzma_options_subblock *options
|
|
|
|
= xmalloc(sizeof(lzma_options_subblock));
|
|
|
|
*options = (lzma_options_subblock){
|
|
|
|
.allow_subfilters = false,
|
|
|
|
.alignment = LZMA_SUBBLOCK_ALIGNMENT_DEFAULT,
|
|
|
|
.subblock_data_size = LZMA_SUBBLOCK_DATA_SIZE_DEFAULT,
|
|
|
|
.rle = LZMA_SUBBLOCK_RLE_OFF,
|
|
|
|
};
|
|
|
|
|
|
|
|
parse_options(str, opts, &set_subblock, options);
|
|
|
|
|
|
|
|
return options;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////
|
|
|
|
// Delta //
|
|
|
|
///////////
|
|
|
|
|
|
|
|
enum {
|
2008-09-27 18:09:21 +02:00
|
|
|
OPT_DIST,
|
2007-12-08 23:42:33 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
set_delta(void *options, uint32_t key, uint64_t value)
|
|
|
|
{
|
|
|
|
lzma_options_delta *opt = options;
|
|
|
|
switch (key) {
|
2008-09-27 18:09:21 +02:00
|
|
|
case OPT_DIST:
|
|
|
|
opt->dist = value;
|
2007-12-08 23:42:33 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
extern lzma_options_delta *
|
2008-11-19 19:46:52 +01:00
|
|
|
options_delta(const char *str)
|
2007-12-08 23:42:33 +01:00
|
|
|
{
|
|
|
|
static const option_map opts[] = {
|
2008-09-27 18:09:21 +02:00
|
|
|
{ "dist", NULL, LZMA_DELTA_DIST_MIN,
|
|
|
|
LZMA_DELTA_DIST_MAX },
|
2007-12-08 23:42:33 +01:00
|
|
|
{ NULL, NULL, 0, 0 }
|
|
|
|
};
|
|
|
|
|
2008-11-19 19:46:52 +01:00
|
|
|
lzma_options_delta *options = xmalloc(sizeof(lzma_options_delta));
|
2007-12-08 23:42:33 +01:00
|
|
|
*options = (lzma_options_delta){
|
|
|
|
// It's hard to give a useful default for this.
|
2008-09-27 18:09:21 +02:00
|
|
|
.type = LZMA_DELTA_TYPE_BYTE,
|
|
|
|
.dist = LZMA_DELTA_DIST_MIN,
|
2007-12-08 23:42:33 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
parse_options(str, opts, &set_delta, options);
|
|
|
|
|
|
|
|
return options;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////
|
|
|
|
// LZMA //
|
|
|
|
//////////
|
|
|
|
|
|
|
|
enum {
|
2008-12-10 00:23:58 +01:00
|
|
|
OPT_PRESET,
|
2007-12-08 23:42:33 +01:00
|
|
|
OPT_DICT,
|
|
|
|
OPT_LC,
|
|
|
|
OPT_LP,
|
|
|
|
OPT_PB,
|
|
|
|
OPT_MODE,
|
2008-09-27 18:09:21 +02:00
|
|
|
OPT_NICE,
|
2007-12-08 23:42:33 +01:00
|
|
|
OPT_MF,
|
2008-09-27 18:09:21 +02:00
|
|
|
OPT_DEPTH,
|
2007-12-08 23:42:33 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
set_lzma(void *options, uint32_t key, uint64_t value)
|
|
|
|
{
|
|
|
|
lzma_options_lzma *opt = options;
|
|
|
|
|
|
|
|
switch (key) {
|
2008-12-10 00:23:58 +01:00
|
|
|
case OPT_PRESET:
|
|
|
|
if (lzma_lzma_preset(options, (uint32_t)(value)))
|
|
|
|
message_fatal("LZMA1/LZMA2 preset %u is not supported",
|
|
|
|
(unsigned int)(value));
|
|
|
|
break;
|
|
|
|
|
2007-12-08 23:42:33 +01:00
|
|
|
case OPT_DICT:
|
2008-09-27 18:09:21 +02:00
|
|
|
opt->dict_size = value;
|
2007-12-08 23:42:33 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OPT_LC:
|
2008-09-27 18:09:21 +02:00
|
|
|
opt->lc = value;
|
2007-12-08 23:42:33 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OPT_LP:
|
2008-09-27 18:09:21 +02:00
|
|
|
opt->lp = value;
|
2007-12-08 23:42:33 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OPT_PB:
|
2008-09-27 18:09:21 +02:00
|
|
|
opt->pb = value;
|
2007-12-08 23:42:33 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OPT_MODE:
|
|
|
|
opt->mode = value;
|
|
|
|
break;
|
|
|
|
|
2008-09-27 18:09:21 +02:00
|
|
|
case OPT_NICE:
|
|
|
|
opt->nice_len = value;
|
2007-12-08 23:42:33 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OPT_MF:
|
2008-09-27 18:09:21 +02:00
|
|
|
opt->mf = value;
|
2007-12-08 23:42:33 +01:00
|
|
|
break;
|
|
|
|
|
2008-09-27 18:09:21 +02:00
|
|
|
case OPT_DEPTH:
|
|
|
|
opt->depth = value;
|
2007-12-08 23:42:33 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
extern lzma_options_lzma *
|
2008-11-19 19:46:52 +01:00
|
|
|
options_lzma(const char *str)
|
2007-12-08 23:42:33 +01:00
|
|
|
{
|
|
|
|
static const name_id_map modes[] = {
|
2008-08-28 21:53:15 +02:00
|
|
|
{ "fast", LZMA_MODE_FAST },
|
|
|
|
{ "normal", LZMA_MODE_NORMAL },
|
|
|
|
{ NULL, 0 }
|
2007-12-08 23:42:33 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
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[] = {
|
2008-12-10 00:23:58 +01:00
|
|
|
{ "preset", NULL, 1, 9 },
|
2008-09-27 18:09:21 +02:00
|
|
|
{ "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 }
|
2007-12-08 23:42:33 +01:00
|
|
|
};
|
|
|
|
|
2008-09-27 18:09:21 +02:00
|
|
|
// TODO There should be a way to take some preset as the base for
|
|
|
|
// custom settings.
|
2007-12-08 23:42:33 +01:00
|
|
|
lzma_options_lzma *options = xmalloc(sizeof(lzma_options_lzma));
|
|
|
|
*options = (lzma_options_lzma){
|
2008-09-27 18:09:21 +02:00
|
|
|
.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,
|
2008-08-28 21:53:15 +02:00
|
|
|
.persistent = false,
|
|
|
|
.mode = LZMA_MODE_NORMAL,
|
2008-09-27 18:09:21 +02:00
|
|
|
.nice_len = 64,
|
|
|
|
.mf = LZMA_MF_BT4,
|
|
|
|
.depth = 0,
|
2007-12-08 23:42:33 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
parse_options(str, opts, &set_lzma, options);
|
|
|
|
|
2008-11-19 19:46:52 +01:00
|
|
|
if (options->lc + options->lp > LZMA_LCLP_MAX)
|
|
|
|
message_fatal(_("The sum of lc and lp must be at "
|
|
|
|
"maximum of 4"));
|
2008-09-27 18:09:21 +02:00
|
|
|
|
|
|
|
const uint32_t nice_len_min = options->mf & 0x0F;
|
2008-11-19 19:46:52 +01:00
|
|
|
if (options->nice_len < nice_len_min)
|
|
|
|
message_fatal(_("The selected match finder requires at "
|
|
|
|
"least nice=%" PRIu32), nice_len_min);
|
2008-09-27 18:09:21 +02:00
|
|
|
|
2007-12-08 23:42:33 +01:00
|
|
|
return options;
|
|
|
|
}
|