options.c revision 213700
143045Sdillon///////////////////////////////////////////////////////////////////////////////
243045Sdillon//
3132935Simp/// \file       options.c
443045Sdillon/// \brief      Parser for filter-specific options
543045Sdillon//
6132935Simp//  Author:     Lasse Collin
7132935Simp//
8132935Simp//  This file has been put into the public domain.
9132935Simp//  You can do whatever you want with this file.
10132935Simp//
11132935Simp///////////////////////////////////////////////////////////////////////////////
12132935Simp
13132935Simp#include "private.h"
14132935Simp
15132935Simp
16132935Simp///////////////////
17132935Simp// Generic stuff //
18132935Simp///////////////////
19132935Simp
20132935Simptypedef struct {
21132935Simp	const char *name;
22132935Simp	uint64_t id;
23132935Simp} name_id_map;
24132935Simp
2543045Sdillon
2643045Sdillontypedef struct {
2783551Sdillon	const char *name;
2883551Sdillon	const name_id_map *map;
2943045Sdillon	uint64_t min;
3043045Sdillon	uint64_t max;
3143045Sdillon} option_map;
3243045Sdillon
3343045Sdillon
3472950Srwatson/// Parses option=value pairs that are separated with colons, semicolons,
3543045Sdillon/// or commas: opt=val:opt=val;opt=val,opt=val
36166550Sjhb///
3777605Stmm/// Each option is a string, that is converted to an integer using the
3877605Stmm/// index where the option string is in the array.
3943045Sdillon///
4077605Stmm/// Value can be
4143045Sdillon///  - a string-id map mapping a list of possible string values to integers
4243045Sdillon///    (opts[i].map != NULL, opts[i].min and opts[i].max are ignored);
4343045Sdillon///  - a number with minimum and maximum value limit
4469793Sobrien///    (opts[i].map == NULL && opts[i].min != UINT64_MAX);
4543045Sdillon///  - a string that will be parsed by the filter-specific code
4643045Sdillon///    (opts[i].map == NULL && opts[i].min == UINT64_MAX, opts[i].max ignored)
4743045Sdillon///
4843045Sdillon/// When parsing both option and value succeed, a filter-specific function
4972950Srwatson/// is called, which should update the given value to filter-specific
5043045Sdillon/// options structure.
5172950Srwatson///
5272950Srwatson/// \param      str     String containing the options from the command line
53166550Sjhb/// \param      opts    Filter-specific option map
54217744Suqs/// \param      set     Filter-specific function to update filter_options
55217744Suqs/// \param      filter_options  Pointer to filter-specific options structure
56217744Suqs///
57166550Sjhb/// \return     Returns only if no errors occur.
5843045Sdillon///
59166550Sjhbstatic void
60166550Sjhbparse_options(const char *str, const option_map *opts,
61166550Sjhb		void (*set)(void *filter_options,
6243045Sdillon			uint32_t key, uint64_t value, const char *valuestr),
6372950Srwatson		void *filter_options)
6443045Sdillon{
6543045Sdillon	if (str == NULL || str[0] == '\0')
66166550Sjhb		return;
6772950Srwatson
68166550Sjhb	char *s = xstrdup(str);
69217744Suqs	char *name = s;
7043045Sdillon
71166550Sjhb	while (*name != '\0') {
72166550Sjhb		if (*name == ',') {
73166550Sjhb			++name;
74166550Sjhb			continue;
75166550Sjhb		}
76166550Sjhb
77166550Sjhb		char *split = strchr(name, ',');
78166550Sjhb		if (split != NULL)
79166550Sjhb			*split = '\0';
80166550Sjhb
8177605Stmm		char *value = strchr(name, '=');
8277605Stmm		if (value != NULL)
8377605Stmm			*value++ = '\0';
8477605Stmm
8577605Stmm		if (value == NULL || value[0] == '\0')
8677605Stmm			message_fatal(_("%s: Options must be `name=value' "
8777605Stmm					"pairs separated with commas"), str);
8877605Stmm
8977605Stmm		// Look for the option name from the option map.
9072950Srwatson		size_t i = 0;
9172950Srwatson		while (true) {
9243045Sdillon			if (opts[i].name == NULL)
93217744Suqs				message_fatal(_("%s: Invalid option name"),
94217744Suqs						name);
9543045Sdillon
9643045Sdillon			if (strcmp(name, opts[i].name) == 0)
9743045Sdillon				break;
9843045Sdillon
9943045Sdillon			++i;
10043045Sdillon		}
10143045Sdillon
10243045Sdillon		// Option was found from the map. See how we should handle it.
10343045Sdillon		if (opts[i].map != NULL) {
10472950Srwatson			// value is a string which we should map
10572950Srwatson			// to an integer.
10672950Srwatson			size_t j;
107166550Sjhb			for (j = 0; opts[i].map[j].name != NULL; ++j) {
10872950Srwatson				if (strcmp(opts[i].map[j].name, value) == 0)
10972950Srwatson					break;
11043045Sdillon			}
111166550Sjhb
112217744Suqs			if (opts[i].map[j].name == NULL)
113217744Suqs				message_fatal(_("%s: Invalid option value"),
114217744Suqs						value);
115166550Sjhb
116166550Sjhb			set(filter_options, i, opts[i].map[j].id, value);
117166550Sjhb
118166550Sjhb		} else if (opts[i].min == UINT64_MAX) {
119166550Sjhb			// value is a special string that will be
120166550Sjhb			// parsed by set().
121166550Sjhb			set(filter_options, i, 0, value);
122166550Sjhb
123166550Sjhb		} else {
124166550Sjhb			// value is an integer.
125166550Sjhb			const uint64_t v = str_to_uint64(name, value,
126166550Sjhb					opts[i].min, opts[i].max);
127166550Sjhb			set(filter_options, i, v, value);
128166550Sjhb		}
129166550Sjhb
130166550Sjhb		// Check if it was the last option.
131166550Sjhb		if (split == NULL)
132166550Sjhb			break;
133166550Sjhb
134166550Sjhb		name = split + 1;
135166550Sjhb	}
136166550Sjhb
137166550Sjhb	free(s);
138166550Sjhb	return;
139166550Sjhb}
140166550Sjhb
141166550Sjhb
142166550Sjhb///////////
143166550Sjhb// Delta //
144166550Sjhb///////////
145166550Sjhb
146166550Sjhbenum {
147166550Sjhb	OPT_DIST,
148166550Sjhb};
149166550Sjhb
15077605Stmm
15177605Stmmstatic void
15272950Srwatsonset_delta(void *options, uint32_t key, uint64_t value,
15377605Stmm		const char *valuestr lzma_attribute((unused)))
15477605Stmm{
15577605Stmm	lzma_options_delta *opt = options;
15672950Srwatson	switch (key) {
157217744Suqs	case OPT_DIST:
158217744Suqs		opt->dist = value;
159217744Suqs		break;
16077605Stmm	}
16177605Stmm}
16277605Stmm
16377605Stmm
16477605Stmmextern lzma_options_delta *
16572950Srwatsonoptions_delta(const char *str)
16677605Stmm{
16772950Srwatson	static const option_map opts[] = {
16872950Srwatson		{ "dist",     NULL,  LZMA_DELTA_DIST_MIN,
16977605Stmm		                     LZMA_DELTA_DIST_MAX },
17077605Stmm		{ NULL,       NULL,  0, 0 }
17177605Stmm	};
17277605Stmm
17372950Srwatson	lzma_options_delta *options = xmalloc(sizeof(lzma_options_delta));
17477605Stmm	*options = (lzma_options_delta){
17577605Stmm		// It's hard to give a useful default for this.
17677605Stmm		.type = LZMA_DELTA_TYPE_BYTE,
17777605Stmm		.dist = LZMA_DELTA_DIST_MIN,
17877605Stmm	};
17977605Stmm
18077605Stmm	parse_options(str, opts, &set_delta, options);
18177605Stmm
18277605Stmm	return options;
18377605Stmm}
18472950Srwatson
18572950Srwatson
18677605Stmm/////////
18777605Stmm// BCJ //
18877605Stmm/////////
18972950Srwatson
19077605Stmmenum {
19177605Stmm	OPT_START_OFFSET,
19277605Stmm};
19377605Stmm
19472950Srwatson
19577605Stmmstatic void
19672950Srwatsonset_bcj(void *options, uint32_t key, uint64_t value,
19777605Stmm		const char *valuestr lzma_attribute((unused)))
19877605Stmm{
19977605Stmm	lzma_options_bcj *opt = options;
20077605Stmm	switch (key) {
20177605Stmm	case OPT_START_OFFSET:
20277605Stmm		opt->start_offset = value;
20377605Stmm		break;
20477605Stmm	}
20572950Srwatson}
20677605Stmm
20777605Stmm
20872950Srwatsonextern lzma_options_bcj *
20972950Srwatsonoptions_bcj(const char *str)
21077605Stmm{
21177605Stmm	static const option_map opts[] = {
21277605Stmm		{ "start",    NULL,  0, UINT32_MAX },
21377605Stmm		{ NULL,       NULL,  0, 0 }
21477605Stmm	};
21577605Stmm
21672950Srwatson	lzma_options_bcj *options = xmalloc(sizeof(lzma_options_bcj));
21772950Srwatson	*options = (lzma_options_bcj){
21872950Srwatson		.start_offset = 0,
21972950Srwatson	};
220166550Sjhb
221166550Sjhb	parse_options(str, opts, &set_bcj, options);
222166550Sjhb
223166550Sjhb	return options;
224166550Sjhb}
225166550Sjhb
226166550Sjhb
227166550Sjhb//////////
228166550Sjhb// LZMA //
229166550Sjhb//////////
230166550Sjhb
231166550Sjhbenum {
232166550Sjhb	OPT_PRESET,
233166550Sjhb	OPT_DICT,
234166550Sjhb	OPT_LC,
235166550Sjhb	OPT_LP,
236166550Sjhb	OPT_PB,
237166550Sjhb	OPT_MODE,
238166550Sjhb	OPT_NICE,
239166550Sjhb	OPT_MF,
240166550Sjhb	OPT_DEPTH,
241166550Sjhb};
242166550Sjhb
243166550Sjhb
244166550Sjhbstatic void lzma_attribute((noreturn))
245166550Sjhberror_lzma_preset(const char *valuestr)
246166550Sjhb{
247166550Sjhb	message_fatal(_("Unsupported LZMA1/LZMA2 preset: %s"), valuestr);
248217744Suqs}
249217744Suqs
25077605Stmm
25172950Srwatsonstatic void
25277605Stmmset_lzma(void *options, uint32_t key, uint64_t value, const char *valuestr)
25377605Stmm{
25472950Srwatson	lzma_options_lzma *opt = options;
25572950Srwatson
25672950Srwatson	switch (key) {
25772950Srwatson	case OPT_PRESET: {
25872950Srwatson		if (valuestr[0] < '0' || valuestr[0] > '9')
25972950Srwatson			error_lzma_preset(valuestr);
26072950Srwatson
26172950Srwatson		uint32_t preset = valuestr[0] - '0';
262
263		// Currently only "e" is supported as a modifier,
264		// so keep this simple for now.
265		if (valuestr[1] != '\0') {
266			if (valuestr[1] == 'e')
267				preset |= LZMA_PRESET_EXTREME;
268			else
269				error_lzma_preset(valuestr);
270
271			if (valuestr[2] != '\0')
272				error_lzma_preset(valuestr);
273		}
274
275		if (lzma_lzma_preset(options, preset))
276			error_lzma_preset(valuestr);
277
278		break;
279	}
280
281	case OPT_DICT:
282		opt->dict_size = value;
283		break;
284
285	case OPT_LC:
286		opt->lc = value;
287		break;
288
289	case OPT_LP:
290		opt->lp = value;
291		break;
292
293	case OPT_PB:
294		opt->pb = value;
295		break;
296
297	case OPT_MODE:
298		opt->mode = value;
299		break;
300
301	case OPT_NICE:
302		opt->nice_len = value;
303		break;
304
305	case OPT_MF:
306		opt->mf = value;
307		break;
308
309	case OPT_DEPTH:
310		opt->depth = value;
311		break;
312	}
313}
314
315
316extern lzma_options_lzma *
317options_lzma(const char *str)
318{
319	static const name_id_map modes[] = {
320		{ "fast",   LZMA_MODE_FAST },
321		{ "normal", LZMA_MODE_NORMAL },
322		{ NULL,     0 }
323	};
324
325	static const name_id_map mfs[] = {
326		{ "hc3", LZMA_MF_HC3 },
327		{ "hc4", LZMA_MF_HC4 },
328		{ "bt2", LZMA_MF_BT2 },
329		{ "bt3", LZMA_MF_BT3 },
330		{ "bt4", LZMA_MF_BT4 },
331		{ NULL,  0 }
332	};
333
334	static const option_map opts[] = {
335		{ "preset", NULL,   UINT64_MAX, 0 },
336		{ "dict",   NULL,   LZMA_DICT_SIZE_MIN,
337				(UINT32_C(1) << 30) + (UINT32_C(1) << 29) },
338		{ "lc",     NULL,   LZMA_LCLP_MIN, LZMA_LCLP_MAX },
339		{ "lp",     NULL,   LZMA_LCLP_MIN, LZMA_LCLP_MAX },
340		{ "pb",     NULL,   LZMA_PB_MIN, LZMA_PB_MAX },
341		{ "mode",   modes,  0, 0 },
342		{ "nice",   NULL,   2, 273 },
343		{ "mf",     mfs,    0, 0 },
344		{ "depth",  NULL,   0, UINT32_MAX },
345		{ NULL,     NULL,   0, 0 }
346	};
347
348	lzma_options_lzma *options = xmalloc(sizeof(lzma_options_lzma));
349	if (lzma_lzma_preset(options, LZMA_PRESET_DEFAULT))
350		message_bug();
351
352	parse_options(str, opts, &set_lzma, options);
353
354	if (options->lc + options->lp > LZMA_LCLP_MAX)
355		message_fatal(_("The sum of lc and lp must not exceed 4"));
356
357	const uint32_t nice_len_min = options->mf & 0x0F;
358	if (options->nice_len < nice_len_min)
359		message_fatal(_("The selected match finder requires at "
360				"least nice=%" PRIu32), nice_len_min);
361
362	return options;
363}
364