aboutsummaryrefslogblamecommitdiff
path: root/src/lzma/options.c
blob: 2928aafc23eb38ff0f8bee5635e23dfaa37603ae (plain) (tree)

























































































































































































































































































































































                                                                               
///////////////////////////////////////////////////////////////////////////////
//
/// \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';

		if (value == NULL || value[0] == '\0') {
			errmsg(V_ERROR, _("%s: Options must be `name=value' "
					"pairs separated with commas"),
					str);
			my_exit(ERROR);
		}

		// 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;
				}

				if (opts[i].map[j].name == NULL) {
					errmsg(V_ERROR, _("%s: Invalid option "
							"value"), value);
					my_exit(ERROR);
				}

				set(filter_options, i, j);
			}

			found = true;
			break;
		}

		if (!found) {
			errmsg(V_ERROR, _("%s: Invalid option name"), name);
			my_exit(ERROR);
		}

		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 *
parse_options_subblock(const char *str)
{
	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 {
	OPT_DISTANCE,
};


static void
set_delta(void *options, uint32_t key, uint64_t value)
{
	lzma_options_delta *opt = options;
	switch (key) {
	case OPT_DISTANCE:
		opt->distance = value;
		break;
	}
}


extern lzma_options_delta *
parse_options_delta(const char *str)
{
	static const option_map opts[] = {
		{ "distance", NULL,  LZMA_DELTA_DISTANCE_MIN,
		                     LZMA_DELTA_DISTANCE_MAX },
		{ NULL,       NULL,  0, 0 }
	};

	lzma_options_delta *options = xmalloc(sizeof(lzma_options_subblock));
	*options = (lzma_options_delta){
		// It's hard to give a useful default for this.
		.distance = LZMA_DELTA_DISTANCE_MIN,
	};

	parse_options(str, opts, &set_delta, options);

	return options;
}


//////////
// LZMA //
//////////

enum {
	OPT_DICT,
	OPT_LC,
	OPT_LP,
	OPT_PB,
	OPT_MODE,
	OPT_FB,
	OPT_MF,
	OPT_MC
};


static void
set_lzma(void *options, uint32_t key, uint64_t value)
{
	lzma_options_lzma *opt = options;

	switch (key) {
	case OPT_DICT:
		opt->dictionary_size = value;
		break;

	case OPT_LC:
		opt->literal_context_bits = value;
		break;

	case OPT_LP:
		opt->literal_pos_bits = value;
		break;

	case OPT_PB:
		opt->pos_bits = value;
		break;

	case OPT_MODE:
		opt->mode = value;
		break;

	case OPT_FB:
		opt->fast_bytes = value;
		break;

	case OPT_MF:
		opt->match_finder = value;
		break;

	case OPT_MC:
		opt->match_finder_cycles = value;
		break;
	}
}


extern lzma_options_lzma *
parse_options_lzma(const char *str)
{
	static const name_id_map modes[] = {
		{ "fast", LZMA_MODE_FAST },
		{ "best", LZMA_MODE_BEST },
		{ 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[] = {
		{ "dict", NULL,   LZMA_DICTIONARY_SIZE_MIN,
				LZMA_DICTIONARY_SIZE_MAX },
		{ "lc",   NULL,   LZMA_LITERAL_CONTEXT_BITS_MIN,
				  LZMA_LITERAL_CONTEXT_BITS_MAX },
		{ "lp",   NULL,   LZMA_LITERAL_POS_BITS_MIN,
				  LZMA_LITERAL_POS_BITS_MAX },
		{ "pb",   NULL,   LZMA_POS_BITS_MIN, LZMA_POS_BITS_MAX },
		{ "mode", modes,  0, 0 },
		{ "fb",   NULL,   LZMA_FAST_BYTES_MIN, LZMA_FAST_BYTES_MAX },
		{ "mf",   mfs,    0, 0 },
		{ "mc",   NULL,   0, UINT32_MAX },
		{ NULL,   NULL,   0, 0 }
	};

	lzma_options_lzma *options = xmalloc(sizeof(lzma_options_lzma));
	*options = (lzma_options_lzma){
		.dictionary_size = LZMA_DICTIONARY_SIZE_DEFAULT,
		.literal_context_bits = LZMA_LITERAL_CONTEXT_BITS_DEFAULT,
		.literal_pos_bits = LZMA_LITERAL_POS_BITS_DEFAULT,
		.pos_bits = LZMA_POS_BITS_DEFAULT,
		.mode = LZMA_MODE_BEST,
		.fast_bytes = LZMA_FAST_BYTES_DEFAULT,
		.match_finder = LZMA_MF_BT4,
		.match_finder_cycles = 0,
	};

	parse_options(str, opts, &set_lzma, options);

	return options;
}