options.c revision 292588
1///////////////////////////////////////////////////////////////////////////////
2//
3/// \file       options.c
4/// \brief      Parser for filter-specific options
5//
6//  Author:     Lasse Collin
7//
8//  This file has been put into the public domain.
9//  You can do whatever you want with this file.
10//
11///////////////////////////////////////////////////////////////////////////////
12
13#include "private.h"
14
15
16///////////////////
17// Generic stuff //
18///////////////////
19
20typedef struct {
21	const char *name;
22	uint64_t id;
23} name_id_map;
24
25
26typedef struct {
27	const char *name;
28	const name_id_map *map;
29	uint64_t min;
30	uint64_t max;
31} option_map;
32
33
34/// Parses option=value pairs that are separated with commas:
35/// opt=val,opt=val,opt=val
36///
37/// Each option is a string, that is converted to an integer using the
38/// index where the option string is in the array.
39///
40/// Value can be
41///  - a string-id map mapping a list of possible string values to integers
42///    (opts[i].map != NULL, opts[i].min and opts[i].max are ignored);
43///  - a number with minimum and maximum value limit
44///    (opts[i].map == NULL && opts[i].min != UINT64_MAX);
45///  - a string that will be parsed by the filter-specific code
46///    (opts[i].map == NULL && opts[i].min == UINT64_MAX, opts[i].max ignored)
47///
48/// When parsing both option and value succeed, a filter-specific function
49/// is called, which should update the given value to filter-specific
50/// options structure.
51///
52/// \param      str     String containing the options from the command line
53/// \param      opts    Filter-specific option map
54/// \param      set     Filter-specific function to update filter_options
55/// \param      filter_options  Pointer to filter-specific options structure
56///
57/// \return     Returns only if no errors occur.
58///
59static void
60parse_options(const char *str, const option_map *opts,
61		void (*set)(void *filter_options,
62			unsigned key, uint64_t value, const char *valuestr),
63		void *filter_options)
64{
65	if (str == NULL || str[0] == '\0')
66		return;
67
68	char *s = xstrdup(str);
69	char *name = s;
70
71	while (*name != '\0') {
72		if (*name == ',') {
73			++name;
74			continue;
75		}
76
77		char *split = strchr(name, ',');
78		if (split != NULL)
79			*split = '\0';
80
81		char *value = strchr(name, '=');
82		if (value != NULL)
83			*value++ = '\0';
84
85		if (value == NULL || value[0] == '\0')
86			message_fatal(_("%s: Options must be `name=value' "
87					"pairs separated with commas"), str);
88
89		// Look for the option name from the option map.
90		unsigned i = 0;
91		while (true) {
92			if (opts[i].name == NULL)
93				message_fatal(_("%s: Invalid option name"),
94						name);
95
96			if (strcmp(name, opts[i].name) == 0)
97				break;
98
99			++i;
100		}
101
102		// Option was found from the map. See how we should handle it.
103		if (opts[i].map != NULL) {
104			// value is a string which we should map
105			// to an integer.
106			unsigned j;
107			for (j = 0; opts[i].map[j].name != NULL; ++j) {
108				if (strcmp(opts[i].map[j].name, value) == 0)
109					break;
110			}
111
112			if (opts[i].map[j].name == NULL)
113				message_fatal(_("%s: Invalid option value"),
114						value);
115
116			set(filter_options, i, opts[i].map[j].id, value);
117
118		} else if (opts[i].min == UINT64_MAX) {
119			// value is a special string that will be
120			// parsed by set().
121			set(filter_options, i, 0, value);
122
123		} else {
124			// value is an integer.
125			const uint64_t v = str_to_uint64(name, value,
126					opts[i].min, opts[i].max);
127			set(filter_options, i, v, value);
128		}
129
130		// Check if it was the last option.
131		if (split == NULL)
132			break;
133
134		name = split + 1;
135	}
136
137	free(s);
138	return;
139}
140
141
142///////////
143// Delta //
144///////////
145
146enum {
147	OPT_DIST,
148};
149
150
151static void
152set_delta(void *options, unsigned key, uint64_t value,
153		const char *valuestr lzma_attribute((__unused__)))
154{
155	lzma_options_delta *opt = options;
156	switch (key) {
157	case OPT_DIST:
158		opt->dist = value;
159		break;
160	}
161}
162
163
164extern lzma_options_delta *
165options_delta(const char *str)
166{
167	static const option_map opts[] = {
168		{ "dist",     NULL,  LZMA_DELTA_DIST_MIN,
169		                     LZMA_DELTA_DIST_MAX },
170		{ NULL,       NULL,  0, 0 }
171	};
172
173	lzma_options_delta *options = xmalloc(sizeof(lzma_options_delta));
174	*options = (lzma_options_delta){
175		// It's hard to give a useful default for this.
176		.type = LZMA_DELTA_TYPE_BYTE,
177		.dist = LZMA_DELTA_DIST_MIN,
178	};
179
180	parse_options(str, opts, &set_delta, options);
181
182	return options;
183}
184
185
186/////////
187// BCJ //
188/////////
189
190enum {
191	OPT_START_OFFSET,
192};
193
194
195static void
196set_bcj(void *options, unsigned key, uint64_t value,
197		const char *valuestr lzma_attribute((__unused__)))
198{
199	lzma_options_bcj *opt = options;
200	switch (key) {
201	case OPT_START_OFFSET:
202		opt->start_offset = value;
203		break;
204	}
205}
206
207
208extern lzma_options_bcj *
209options_bcj(const char *str)
210{
211	static const option_map opts[] = {
212		{ "start",    NULL,  0, UINT32_MAX },
213		{ NULL,       NULL,  0, 0 }
214	};
215
216	lzma_options_bcj *options = xmalloc(sizeof(lzma_options_bcj));
217	*options = (lzma_options_bcj){
218		.start_offset = 0,
219	};
220
221	parse_options(str, opts, &set_bcj, options);
222
223	return options;
224}
225
226
227//////////
228// LZMA //
229//////////
230
231enum {
232	OPT_PRESET,
233	OPT_DICT,
234	OPT_LC,
235	OPT_LP,
236	OPT_PB,
237	OPT_MODE,
238	OPT_NICE,
239	OPT_MF,
240	OPT_DEPTH,
241};
242
243
244static void lzma_attribute((__noreturn__))
245error_lzma_preset(const char *valuestr)
246{
247	message_fatal(_("Unsupported LZMA1/LZMA2 preset: %s"), valuestr);
248}
249
250
251static void
252set_lzma(void *options, unsigned key, uint64_t value, const char *valuestr)
253{
254	lzma_options_lzma *opt = options;
255
256	switch (key) {
257	case OPT_PRESET: {
258		if (valuestr[0] < '0' || valuestr[0] > '9')
259			error_lzma_preset(valuestr);
260
261		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